Compare commits

..

105 Commits

Author SHA1 Message Date
Cyril Anisimov
25a24bf38c focusControl => FocusControl 2024-12-30 18:10:25 +01:00
Cyril Anisimov
5e492e8392 remove debug output from focusControl 2024-12-30 18:05:31 +01:00
Cyril Anisimov
b25d911328 remove debug output from ListViewFocusController 2024-12-30 18:04:05 +01:00
Cyril Anisimov
caf29fb982 remove debug output from FocusController 2024-12-30 18:03:26 +01:00
Cyril Anisimov
32ef1eae84 fix PageDevMenu 2024-12-30 00:19:11 +01:00
Cyril Anisimov
b0fdbe6c4f fix PageSettingsAppSplitTunneling 2024-12-29 22:11:35 +01:00
Cyril Anisimov
dccfcde6f6 fix PageProtocolCloakSettings 2024-12-29 22:04:00 +01:00
Cyril Anisimov
26ba520022 fix ListViewWithLabelsType 2024-12-29 22:01:41 +01:00
Cyril Anisimov
0f98fc955f remove comments and clean up 2024-12-29 21:19:56 +01:00
Cyril Anisimov
2c1f3eaf3a refactor PageSetupWizardProtocols 2024-12-29 20:26:22 +01:00
Cyril Anisimov
867ad8ee3c fix focus movement on dynamic delegates in listView 2024-12-29 00:34:20 +01:00
Cyril Anisimov
f8f4d75cb7 Merge commit 'b88ab8e432fbd2ab5002856cd83ef57181a6d7fa' into improve_navigation_cpp 2024-12-23 09:36:55 +01:00
Cyril Anisimov
13e680f6e1 fix PageSetupWizardApiServicesList 2024-12-22 16:38:07 +01:00
Cyril Anisimov
8767f487b8 fix ScrollBarType 2024-12-22 16:28:13 +01:00
Cyril Anisimov
0cdd2ff826 fix CMakeLists.txt 2024-12-20 15:35:40 +01:00
Cyril Anisimov
c973b8868b rename focusControl to qmlUtils 2024-12-20 15:30:20 +01:00
Cyril Anisimov
53aa14a2ee fix back button on SelectLanguageDrawer 2024-12-20 02:26:05 +01:00
Cyril Anisimov
b29b5962d2 I have nothing is visible only on start screen 2024-12-20 02:17:50 +01:00
Cyril Anisimov
441a2f6df6 Restore from backup is visible only on start screen 2024-12-20 02:15:48 +01:00
Cyril Anisimov
fa64c8d68d fix focus movement in ServersListView 2024-12-20 02:05:28 +01:00
Cyril Anisimov
ae31bafeb9 remove arrow from Close application item 2024-12-19 00:22:59 +01:00
Cyril Anisimov
14d67503a8 add printSectionName method to listViewController 2024-12-19 00:00:36 +01:00
Cyril Anisimov
1029142b47 refactor focusConroller according to focusControl 2024-12-18 23:59:14 +01:00
Cyril Anisimov
7670d9dace refactor listViewFocusController acoording to focusControl 2024-12-18 23:57:08 +01:00
Cyril Anisimov
2266702a0b add focusControl with utility functions for
focus control
2024-12-18 23:47:52 +01:00
Cyril Anisimov
11d20974f0 format FocusController 2024-12-16 22:12:15 +01:00
Cyril Anisimov
a57e94e4f2 format ListViewFocusController 2024-12-16 22:12:00 +01:00
Cyril Anisimov
9620a24d6a remove manual focus control on PageShare 2024-12-15 23:23:15 +01:00
Cyril Anisimov
62be292833 Fix list view focus moving on PageShare 2024-12-15 23:14:52 +01:00
Cyril Anisimov
10e91b06f4 PageShare readonly properties 2024-12-15 23:12:59 +01:00
Cyril Anisimov
3a62f1f20b fix hide/unhide password 2024-12-14 23:44:15 +01:00
Cyril Anisimov
3021066180 refactor SwitcherType 2024-12-14 19:34:24 +01:00
Cyril Anisimov
378db1a9e5 make unchangable properties readonly 2024-12-14 19:33:54 +01:00
Cyril Anisimov
906cf7b939 fix split tunneling settings 2024-12-14 19:32:46 +01:00
Cyril Anisimov
8bc31b60a1 fix checked item in lists 2024-12-14 19:29:35 +01:00
Cyril Anisimov
149c9d12d3 Merge commit '367789bda28f33a11a72d61b222453c5b502299e' into improve_navigation_cpp 2024-12-14 18:38:41 +01:00
Cyril Anisimov
b33b97802e update logging settings page 2024-12-14 18:01:34 +01:00
Cyril Anisimov
d18f50a3e0 Update select language list 2024-12-13 22:27:21 +01:00
Cyril Anisimov
ef2ffce47a Fix selection in language change list 2024-12-13 22:14:27 +01:00
Cyril Anisimov
53f41408b8 Scrollbar is on if the content is larger than a screen 2024-12-13 22:09:37 +01:00
albexk
5b9ba8c027 fix: get rid of the assign function call 2024-12-09 20:56:58 +03:00
Cyril Anisimov
882c288d92 make minor fixes and clean up 2024-12-09 01:53:48 +01:00
Cyril Anisimov
ea39447708 fix focus behavior on new page/popup 2024-12-09 01:35:10 +01:00
Cyril Anisimov
3cc70790bd add setDelegateIndex method to listViewFocusController 2024-12-09 01:32:11 +01:00
Cyril Anisimov
f331c11f51 refactor credentials setup page to handle the focus navigation 2024-12-09 01:30:11 +01:00
Cyril Anisimov
39cbe6de28 update initial config page 2024-12-09 01:28:57 +01:00
Cyril Anisimov
87d00116e1 add ScrollBarType 2024-12-06 22:59:12 +01:00
Cyril Anisimov
f992252c6e Merge commit into improve_navigation_cpp 2024-12-02 11:10:30 +01:00
albexk
c9205afbd4 chore: bump version code 2024-11-25 14:08:25 +03:00
albexk
09b1f322ba fix: hide UI elements that use file saving 2024-11-25 13:56:14 +03:00
albexk
4d2d7e4071 fix: add the touch emulation method for Android TV 2024-11-25 12:33:34 +03:00
albexk
c72d76aec7 Merge branch 'improve_navigation_cpp' into feature/android-tv 2024-11-25 12:20:31 +03:00
albexk
50bd364f29 Merge branch 'dev' into feature/android-tv
# Conflicts:
#	CMakeLists.txt
2024-11-25 12:19:52 +03:00
albexk
3eeab2f2e7 fix: null uri processing 2024-11-25 12:17:22 +03:00
albexk
2987e03a86 fix: add the touch emulation method for Android TV 2024-11-25 12:17:22 +03:00
albexk
392cacaabc refactor: make TvFilePicker activity more sustainable 2024-11-25 12:17:22 +03:00
albexk
51092e91cf fix: change the banner format for TV 2024-11-25 12:17:22 +03:00
albexk
d02a6dfb22 fix: add a workaround to open files on Android TV due to lack of SAF 2024-11-25 12:17:22 +03:00
Nethius
4e1862a802 chore: minor fixes (#1235) 2024-11-25 12:15:36 +03:00
albexk
53e766f6d3 Fix connection check for AWG/WG 2024-11-25 12:15:36 +03:00
albexk
341d6f569b fix(android): add CHANGE_NETWORK_STATE permission for all Android versions 2024-11-25 12:15:36 +03:00
albexk
a5abab8caf fix: add separate method for reading files to fix file reading on Android TV 2024-11-25 12:15:36 +03:00
albexk
7df050371e Fix importing files on TVs 2024-11-25 12:15:36 +03:00
albexk
65870ad5c9 Add mandatory requirement for android.software.leanback. 2024-11-25 12:15:36 +03:00
albexk
940806a6fa Bump version 2024-11-25 12:15:36 +03:00
Cyril Anisimov
0620b4536a update popup 2024-11-21 19:11:57 +01:00
Cyril Anisimov
d956be901d refactor page logging settings for focus navigation 2024-11-21 19:11:57 +01:00
Cyril Anisimov
942805cca2 fix focus move on qr code on share page 2024-11-21 19:11:57 +01:00
Cyril Anisimov
ed6fc27e52 fix focus navigation in dynamic delegate of list view 2024-11-21 19:11:57 +01:00
Cyril Anisimov
416421cba7 fix crash on return with esc key 2024-11-21 19:11:57 +01:00
Cyril Anisimov
45b8235a81 update config page for scrolling with tab 2024-11-21 19:11:57 +01:00
Cyril Anisimov
a92f706524 fix focus on notification 2024-11-21 19:11:57 +01:00
Cyril Anisimov
5e9202f6ac refactor focusController to make the logic more
straightforward
2024-11-21 19:11:57 +01:00
Cyril Anisimov
dc6c1cdf49 refactor listViewFocusController 2024-11-21 19:11:57 +01:00
Cyril Anisimov
f020bdb6e8 fix drawer open close usage 2024-11-21 19:11:57 +01:00
Cyril Anisimov
88958c042f fix share details drawer 2024-11-21 19:11:57 +01:00
Cyril Anisimov
e4d21dc4d7 fix spawn signals on switch 2024-11-21 19:11:57 +01:00
Cyril Anisimov
2e896ed34f fix server list back button handler 2024-11-21 19:11:57 +01:00
Cyril Anisimov
163399816f minor fixes 2024-11-21 19:11:57 +01:00
Cyril Anisimov
3d7209ee7a fix focus move on list views with header and-or footer 2024-11-21 19:11:57 +01:00
Cyril Anisimov
42645a98f8 refactor about page for correct focus move 2024-11-21 19:11:57 +01:00
Cyril Anisimov
7c3d08d80e fix page share default server selection 2024-11-21 19:11:57 +01:00
Cyril Anisimov
c77e01fb37 fix server rename 2024-11-21 19:11:57 +01:00
Cyril Anisimov
b6c59b08a1 clean up page share 2024-11-21 19:11:56 +01:00
Cyril Anisimov
766e1c92fc fix focus on users on page share 2024-11-21 19:11:56 +01:00
Cyril Anisimov
21755cbd54 fix protocol settings focus move 2024-11-21 19:11:56 +01:00
Cyril Anisimov
dac45a9f7f fix crashes after ListView navigation 2024-11-21 19:11:56 +01:00
Cyril Anisimov
db5d289edc fix back navigation on default focus item 2024-11-21 19:11:56 +01:00
Cyril Anisimov
c962211068 fix PageSetupWizardProtocolSettings focus move 2024-11-21 19:11:56 +01:00
Cyril Anisimov
9cfa4c1389 fix drawer layout and focus move 2024-11-21 19:11:56 +01:00
Cyril Anisimov
2c9fa10b8b fix drawer radio buttons selection 2024-11-21 19:11:56 +01:00
Cyril Anisimov
626b9e1e76 fix reverse focus move on listView 2024-11-21 19:11:56 +01:00
Cyril Anisimov
ada3f9a7fa remove useless slots, logs, Drawer open and close 2024-11-21 19:11:56 +01:00
Cyril Anisimov
063851445a remove useless key navigation 2024-11-21 19:11:56 +01:00
Cyril Anisimov
852e90e317 update CardType for using with focus navigation 2024-11-21 19:11:56 +01:00
Cyril Anisimov
89ac585e07 fix ListView navigation 2024-11-21 19:11:56 +01:00
Cyril Anisimov
f3df9eb5f5 add ListViewFocusController 2024-11-21 19:11:56 +01:00
Cyril Anisimov
75f189e256 update pages 2024-11-21 19:11:56 +01:00
Cyril Anisimov
3c655d0051 update transitions 2024-11-21 19:11:56 +01:00
Cyril Anisimov
b4f4ec4ac9 add default focus item 2024-11-21 19:11:56 +01:00
Cyril Anisimov
f189f7b6af add reverse focus change to FocusController 2024-11-21 19:11:56 +01:00
Cyril Anisimov
78a4caa47e fixed language selector 2024-11-21 19:11:56 +01:00
Cyril Anisimov
01e31b4b4d add focus navigation to qml 2024-11-21 19:11:56 +01:00
Cyril Anisimov
cecee3769e add more key handlers 2024-11-21 19:11:55 +01:00
Cyril Anisimov
02bbcd3a31 add focusController class 2024-11-21 19:11:55 +01:00
381 changed files with 15255 additions and 31160 deletions

View File

@@ -1,69 +0,0 @@
# 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

View File

@@ -10,7 +10,7 @@ env:
jobs:
Build-Linux-Ubuntu:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
env:
QT_VERSION: 6.6.2
@@ -20,8 +20,6 @@ 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'
@@ -92,8 +90,6 @@ 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'
@@ -160,8 +156,6 @@ 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'
@@ -196,7 +190,7 @@ jobs:
- name: 'Install go'
uses: actions/setup-go@v5
with:
go-version: '1.24'
go-version: '1.22.1'
cache: false
- name: 'Setup gomobile'
@@ -249,33 +243,18 @@ jobs:
# ------------------------------------------------------
Build-MacOS-old:
Build-MacOS:
runs-on: macos-latest
env:
# Keep compat with MacOS 10.15 aka Catalina by Qt 6.4
QT_VERSION: 6.4.3
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 }}
QIF_VERSION: 4.6
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'
@@ -296,6 +275,11 @@ 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
@@ -309,90 +293,14 @@ jobs:
- 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_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
export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin"
bash deploy/build_macos.sh
- name: 'Upload installer artifact'
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN_MacOS_installer
path: deploy/build/pkg/AmneziaVPN.pkg
path: AmneziaVPN.dmg
retention-days: 7
- name: 'Upload unpacked artifact'
@@ -416,8 +324,6 @@ 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'

View File

@@ -20,8 +20,6 @@ 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'

View File

@@ -1,41 +1,64 @@
name: 'Upload a new version'
on:
workflow_dispatch:
inputs:
RELEASE_VERSION:
description: 'Release version (e.g. 1.2.3.4)'
required: true
type: string
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+.[0-9]+'
jobs:
Upload-S3:
upload:
runs-on: ubuntu-latest
name: upload
steps:
- name: Checkout
- name: Checkout CMakeLists.txt
uses: actions/checkout@v4
with:
ref: ${{ inputs.RELEASE_VERSION }}
ref: ${{ github.ref_name }}
sparse-checkout: |
CMakeLists.txt
deploy/deploy_s3.sh
sparse-checkout-cone-mode: false
- name: Verify git tag
run: |
TAG_NAME=${{ inputs.RELEASE_VERSION }}
GIT_TAG=${{ github.ref_name }}
CMAKE_TAG=$(grep 'project.*VERSION' CMakeLists.txt | sed -E 's/.* ([0-9]+.[0-9]+.[0-9]+.[0-9]+)$/\1/')
if [[ "$TAG_NAME" == "$CMAKE_TAG" ]]; then
echo "Git tag ($TAG_NAME) matches CMakeLists.txt version ($CMAKE_TAG)."
if [[ "$GIT_TAG" == "$CMAKE_TAG" ]]; then
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are the same. Continuing..."
else
echo "::error::Mismatch: Git tag ($TAG_NAME) != CMakeLists.txt version ($CMAKE_TAG). Exiting with error..."
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are not the same! Cancelling..."
exit 1
fi
- name: Setup Rclone
uses: AnimMouse/setup-rclone@v1
- name: Download artifacts from the "${{ github.ref_name }}" tag
uses: robinraju/release-downloader@v1.8
with:
rclone_config: ${{ secrets.RCLONE_CONFIG }}
tag: ${{ github.ref_name }}
fileName: "AmneziaVPN_(Linux_|)${{ github.ref_name }}*"
out-file-path: ${{ github.ref_name }}
- name: Send dist to S3
run: bash deploy/deploy_s3.sh ${{ inputs.RELEASE_VERSION }}
- 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 }}

7
.gitignore vendored
View File

@@ -133,9 +133,4 @@ client/3rd/ShadowSocks/ss_ios.xcconfig
out/
# CMake files
CMakeFiles/
ios-ne-build.sh
macos-ne-build.sh
macos-signed-build.sh
macos-with-sign-build.sh
CMakeFiles/

4
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[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
@@ -7,7 +10,6 @@
[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

View File

@@ -1,9 +1,8 @@
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
set(AMNEZIAVPN_VERSION 4.8.9.1)
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
project(${PROJECT} VERSION 4.8.3.0
DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/"
)
@@ -12,7 +11,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 2091)
set(APP_ANDROID_VERSION_CODE 2072)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")
@@ -29,7 +28,7 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
endif()
set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(APPLE AND NOT IOS)

View File

@@ -13,13 +13,13 @@
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org)
### [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)
### [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)
> [!TIP]
> 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 ).
> 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).
<a href="https://amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
<a href="https://storage.googleapis.com/amnezia/q9p19109"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a>
<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>
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
@@ -185,7 +185,7 @@ GPL v3.0
Patreon: [https://www.patreon.com/amneziavpn](https://www.patreon.com/amneziavpn)
Bitcoin: bc1qmhtgcf9637rl3kqyy22r2a8wa8laka4t9rx2mf <br>
Bitcoin: bc1q26eevjcg9j0wuyywd2e3uc9cs2w58lpkpjxq6p <br>
USDT BEP20: 0x6abD576765a826f87D1D95183438f9408C901bE4 <br>
USDT TRC20: TELAitazF1MZGmiNjTcnxDjEiH5oe7LC9d <br>
XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3 <br>

View File

@@ -6,16 +6,16 @@
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](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 на вашем сервере.
[AmneziaVPN](https://amnezia.org) — это open sourse VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org)
### [Сайт](https://amnezia.org) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
### [Сайт](https://amnezia.org) | [Зеркало на сайт](https://storage.googleapis.com/kldscp/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
> [!TIP]
> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org).
> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/kldscp/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://storage.googleapis.com/kldscp/amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website-ru.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
[Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases)
@@ -30,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).
## Ссылки
@@ -38,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\)
## Технологии
@@ -80,8 +80,8 @@ git submodule update --init --recursive
Проверьте папку deploy для скриптов сборки.
### Как собрать iOS-приложение из исходного кода на MacOS
1. Убедитесь, что у вас установлен Xcode версии 14 или выше.
2. Для генерации проекта Xcode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули:
1. Убедитесь, что у вас установлен XCode версии 14 или выше.
2. Для генерации проекта XCode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули:
- MacOS
- iOS
- Модуль совместимости с Qt 5
@@ -117,7 +117,7 @@ $QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR
export PATH=$(PATH):/path/to/GOPATH/bin
```
6. Откройте проект в Xcode. Теперь вы можете тестировать, архивировать или публиковать приложение.
6. Откройте проект в XCode. Теперь вы можете тестировать, архивировать или публиковать приложение.
Если сборка завершится с ошибкой:
```
@@ -169,7 +169,7 @@ GPL v3.0
Patreon: [https://www.patreon.com/amneziavpn](https://www.patreon.com/amneziavpn)
Bitcoin: bc1qmhtgcf9637rl3kqyy22r2a8wa8laka4t9rx2mf <br>
Bitcoin: bc1q26eevjcg9j0wuyywd2e3uc9cs2w58lpkpjxq6p <br>
USDT BEP20: 0x6abD576765a826f87D1D95183438f9408C901bE4 <br>
USDT TRC20: TELAitazF1MZGmiNjTcnxDjEiH5oe7LC9d <br>
XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3 <br>

1
client/3rd/OpenVPNAdapter vendored Submodule

View File

@@ -31,8 +31,9 @@ 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}")
add_definitions(-DFREE_V2_ENDPOINT="$ENV{FREE_V2_ENDPOINT}")
add_definitions(-DPREM_V1_ENDPOINT="$ENV{PREM_V1_ENDPOINT}")
if(IOS)
set(PACKAGES ${PACKAGES} Multimedia)
endif()
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
set(PACKAGES ${PACKAGES} Widgets)
@@ -47,6 +48,10 @@ 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()
@@ -91,6 +96,11 @@ 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")
@@ -100,8 +110,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
@@ -110,22 +120,167 @@ 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
${CMAKE_CURRENT_LIST_DIR}/utils/qmlUtils.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)
configure_file(${CMAKE_CURRENT_LIST_DIR}/../version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
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
${CMAKE_CURRENT_LIST_DIR}/utils/qmlUtils.cpp
)
# Mozilla sources
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/mozilla/models/server.cpp
${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/ipaddress.cpp
${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/leakdetector.cpp
${CMAKE_CURRENT_LIST_DIR}/mozilla/localsocketcontroller.cpp
)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
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
@@ -169,6 +324,30 @@ 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)

View File

@@ -2,8 +2,6 @@
#include <QClipboard>
#include <QFontDatabase>
#include <QLocalServer>
#include <QLocalSocket>
#include <QMimeData>
#include <QQuickItem>
#include <QQuickStyle>
@@ -12,16 +10,26 @@
#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);
@@ -76,12 +84,79 @@ void AmneziaApplication::init()
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
m_vpnConnectionThread.start();
m_coreController.reset(new CoreController(m_vpnConnection, m_settings, m_engine));
initModels();
loadTranslator();
initControllers();
#ifdef Q_OS_ANDROID
if (!AndroidController::initLogging()) {
qFatal("Android logging initialization failed");
}
AndroidController::instance()->setSaveLogs(m_settings->isSaveLogs());
connect(m_settings.get(), &Settings::saveLogsChanged, AndroidController::instance(), &AndroidController::setSaveLogs);
AndroidController::instance()->setScreenshotsEnabled(m_settings->isScreenshotsEnabled());
connect(m_settings.get(), &Settings::screenshotsEnabledChanged, AndroidController::instance(), &AndroidController::setScreenshotsEnabled);
connect(m_settings.get(), &Settings::serverRemoved, AndroidController::instance(), &AndroidController::resetLastServer);
connect(m_settings.get(), &Settings::settingsCleared, []() { AndroidController::instance()->resetLastServer(-1); });
connect(AndroidController::instance(), &AndroidController::initConnectionState, this, [this](Vpn::ConnectionState state) {
m_connectionController->onConnectionStateChanged(state);
if (m_vpnConnection)
m_vpnConnection->restoreConnection();
});
if (!AndroidController::instance()->initialize()) {
qFatal("Android controller initialization failed");
}
connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, this, [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_engine->addImportPath("qrc:/ui/qml/Modules/");
m_engine->load(url);
m_coreController->setQmlRoot();
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
bool enabled = m_settings->isSaveLogs();
#ifndef Q_OS_ANDROID
@@ -93,13 +168,13 @@ void AmneziaApplication::init()
#endif
Logger::setServiceLogsEnabled(enabled);
#ifdef Q_OS_WIN //TODO
#ifdef Q_OS_WIN
if (m_parser.isSet("a"))
m_coreController->pageController()->showOnStartup();
m_pageController->showOnStartup();
else
emit m_coreController->pageController()->raiseMainWindow();
emit m_pageController->raiseMainWindow();
#else
m_coreController->pageController()->showOnStartup();
m_pageController->showOnStartup();
#endif
// Android TextArea clipboard workaround
@@ -156,6 +231,33 @@ 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);
@@ -180,20 +282,19 @@ 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_coreController->pageController()->raiseMainWindow(); //TODO
emit m_pageController->raiseMainWindow();
});
}
#endif
@@ -203,12 +304,163 @@ QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
return m_engine;
}
QNetworkAccessManager *AmneziaApplication::networkManager()
void AmneziaApplication::initModels()
{
return m_nam;
m_containersModel.reset(new ContainersModel(this));
m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
m_defaultServerContainersModel.reset(new ContainersModel(this));
m_engine->rootContext()->setContextProperty("DefaultServerContainersModel", m_defaultServerContainersModel.get());
m_serversModel.reset(new ServersModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(), &ContainersModel::updateModel);
connect(m_serversModel.get(), &ServersModel::defaultServerContainersUpdated, m_defaultServerContainersModel.get(),
&ContainersModel::updateModel);
m_serversModel->resetModel();
m_languageModel.reset(new LanguageModel(m_settings, this));
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &AmneziaApplication::updateTranslator);
connect(this, &AmneziaApplication::translationsUpdated, m_languageModel.get(), &LanguageModel::translationsUpdated);
m_sitesModel.reset(new SitesModel(m_settings, this));
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
m_openVpnConfigModel.reset(new OpenVpnConfigModel(this));
m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get());
m_shadowSocksConfigModel.reset(new ShadowSocksConfigModel(this));
m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get());
m_cloakConfigModel.reset(new CloakConfigModel(this));
m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get());
m_wireGuardConfigModel.reset(new WireGuardConfigModel(this));
m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get());
m_awgConfigModel.reset(new AwgConfigModel(this));
m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get());
m_xrayConfigModel.reset(new XrayConfigModel(this));
m_engine->rootContext()->setContextProperty("XrayConfigModel", m_xrayConfigModel.get());
#ifdef Q_OS_WINDOWS
m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this));
m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get());
#endif
m_sftpConfigModel.reset(new SftpConfigModel(this));
m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
m_socks5ConfigModel.reset(new Socks5ProxyConfigModel(this));
m_engine->rootContext()->setContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel.get());
m_clientManagementModel.reset(new ClientManagementModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get());
connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked, m_serversModel.get(),
&ServersModel::clearCachedProfile);
m_apiServicesModel.reset(new ApiServicesModel(this));
m_engine->rootContext()->setContextProperty("ApiServicesModel", m_apiServicesModel.get());
m_apiCountryModel.reset(new ApiCountryModel(this));
m_engine->rootContext()->setContextProperty("ApiCountryModel", m_apiCountryModel.get());
connect(m_serversModel.get(), &ServersModel::updateApiLanguageModel, this, [this]() {
m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(),
m_serversModel->getProcessedServerData("apiServerCountryCode").toString());
});
connect(m_serversModel.get(), &ServersModel::updateApiServicesModel, this,
[this]() { m_apiServicesModel->updateModel(m_serversModel->getProcessedServerData("apiConfig").toJsonObject()); });
}
QClipboard *AmneziaApplication::getClipboard()
void AmneziaApplication::initControllers()
{
return this->clipboard();
m_connectionController.reset(
new ConnectionController(m_serversModel, m_containersModel, m_clientManagementModel, m_vpnConnection, m_settings));
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
connect(m_connectionController.get(), qOverload<const QString &>(&ConnectionController::connectionErrorOccurred), this,
[this](const QString &errorMessage) {
emit m_pageController->showErrorMessage(errorMessage);
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
});
connect(m_connectionController.get(), qOverload<ErrorCode>(&ConnectionController::connectionErrorOccurred), this,
[this](ErrorCode errorCode) {
emit m_pageController->showErrorMessage(errorCode);
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
});
connect(m_connectionController.get(), &ConnectionController::connectButtonClicked, m_connectionController.get(),
&ConnectionController::toggleConnection, Qt::QueuedConnection);
m_pageController.reset(new PageController(m_serversModel, m_settings));
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
m_focusController.reset(new FocusController(m_engine, this));
m_engine->rootContext()->setContextProperty("FocusController", m_focusController.get());
m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel,
m_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());
}

View File

@@ -11,12 +11,44 @@
#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/focusController.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)
@@ -35,6 +67,8 @@ 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)
@@ -42,24 +76,68 @@ public:
#endif
QQmlApplicationEngine *qmlEngine() const;
QNetworkAccessManager *networkManager();
QClipboard *getClipboard();
QNetworkAccessManager *manager() { return m_nam; }
signals:
void translationsUpdated();
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<FocusController> m_focusController;
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

View File

@@ -11,7 +11,7 @@
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<!-- for TV -->
<uses-feature android:name="android.software.leanback" android:required="false" />
<uses-feature android:name="android.software.leanback" android:required="true" />
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<!-- The following comment will be replaced upon deployment with default features based on the dependencies

View File

@@ -120,21 +120,10 @@ 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) {

View File

@@ -20,21 +20,10 @@ 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?,
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?
val h4: Long?
) : ProtocolConfig(protocolConfigBuilder) {
protected constructor(builder: Builder) : this(
@@ -50,21 +39,10 @@ open class WireguardConfig protected constructor(
builder.jmax,
builder.s1,
builder.s2,
builder.s3,
builder.s4,
builder.h1,
builder.h2,
builder.h3,
builder.h4,
builder.i1,
builder.i2,
builder.i3,
builder.i4,
builder.i5,
builder.j1,
builder.j2,
builder.j3,
builder.itime
builder.h4
)
fun toWgUserspaceString(): String = with(StringBuilder()) {
@@ -83,21 +61,10 @@ 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") }
}
}
@@ -150,21 +117,10 @@ 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 }
@@ -183,21 +139,10 @@ 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) }
}

View File

@@ -76,22 +76,12 @@ 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"
@@ -136,9 +126,9 @@ add_subdirectory(ios/networkextension)
add_dependencies(${PROJECT} networkextension)
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
"${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework"
"${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework"
)
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/)
target_link_libraries("networkextension" PRIVATE "${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")

View File

@@ -18,11 +18,7 @@ set(LIBS ${LIBS}
${FW_NETWORK_EXTENSION}
)
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_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)

View File

@@ -1,235 +0,0 @@
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/migrations.h
${CLIENT_ROOT_DIR}/../ipc/ipc.h
${CLIENT_ROOT_DIR}/amnezia_application.h
${CLIENT_ROOT_DIR}/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()

View File

@@ -1,29 +1,23 @@
#include "awg_configurator.h"
#include "protocols/protocols_defs.h"
#include <QJsonDocument>
#include <QJsonObject>
AwgConfigurator::AwgConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
: WireguardConfigurator(settings, serverController, true, parent)
{
}
QSharedPointer<ProtocolConfig> AwgConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
QString AwgConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode)
{
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;
QString config = WireguardConfigurator::createConfig(credentials, container, containerConfig, errorCode);
QJsonObject jsonConfig = QJsonDocument::fromJson(config.toUtf8()).object();
QString awgConfig = jsonConfig.value(config_key::config).toString();
QMap<QString, QString> configMap;
auto configLines = config.split("\n");
auto configLines = awgConfig.split("\n");
for (auto &line : configLines) {
auto trimmedLine = line.trimmed();
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
@@ -36,17 +30,17 @@ QSharedPointer<ProtocolConfig> AwgConfigurator::createConfig(const ServerCredent
}
}
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);
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.wireGuardData.mtu = awgConfig->serverProtocolConfig.mtu;
return awgConfig;
return QJsonDocument(jsonConfig).toJson();
}

View File

@@ -11,8 +11,8 @@ class AwgConfigurator : public WireguardConfigurator
public:
AwgConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
};
#endif // AWGCONFIGURATOR_H

View File

@@ -4,47 +4,23 @@
#include <QJsonDocument>
#include <QJsonObject>
#include "core/models/containers/containers_defs.h"
#include "core/controllers/selfhosted/serverController.h"
#include "core/models/protocols/cloakProtocolConfig.h"
#include "protocols/protocols_defs.h"
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
CloakConfigurator::CloakConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
: ConfiguratorBase(settings, serverController, parent)
{
}
ConfiguratorBase::Vars CloakConfigurator::generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig) const
QString CloakConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode)
{
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 nullptr;
return "";
}
QString cloakBypassUid =
@@ -52,38 +28,24 @@ QSharedPointer<ProtocolConfig> CloakConfigurator::createConfig(const ServerCrede
cloakBypassUid.replace("\n", "");
if (errorCode != ErrorCode::NoError) {
return nullptr;
return "";
}
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", 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);
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");
QString textCfg = QJsonDocument(config).toJson();
QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(),
m_serverController->genVarsForScript(credentials, container, containerConfig));
cloakConfig->clientProtocolConfig.isEmpty = false;
cloakConfig->clientProtocolConfig.nativeConfig = textCfg;
return cloakConfig;
return textCfg;
}

View File

@@ -4,7 +4,6 @@
#include <QObject>
#include "configurator_base.h"
#include "core/models/protocols/cloakProtocolConfig.h"
using namespace amnezia;
@@ -14,11 +13,8 @@ class CloakConfigurator : public ConfiguratorBase
public:
CloakConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
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;
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
};
#endif // CLOAK_CONFIGURATOR_H

View File

@@ -1,58 +1,26 @@
#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)
{
}
void ConfiguratorBase::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QSharedPointer<ProtocolConfig> &protocolConfig)
QString ConfiguratorBase::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString)
{
processConfigWithDnsSettings(dns, protocolConfig);
processConfigWithDnsSettings(dns, protocolConfigString);
return protocolConfigString;
}
void ConfiguratorBase::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QSharedPointer<ProtocolConfig> &protocolConfig)
QString ConfiguratorBase::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString)
{
processConfigWithDnsSettings(dns, protocolConfig);
processConfigWithDnsSettings(dns, protocolConfigString);
return protocolConfigString;
}
void ConfiguratorBase::processConfigWithDnsSettings(const QPair<QString, QString> &dns, QSharedPointer<ProtocolConfig> &protocolConfig)
void ConfiguratorBase::processConfigWithDnsSettings(const QPair<QString, QString> &dns, QString &protocolConfigString)
{
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;
protocolConfigString.replace("$PRIMARY_DNS", dns.first);
protocolConfigString.replace("$SECONDARY_DNS", dns.second);
}

View File

@@ -2,39 +2,28 @@
#define CONFIGURATORBASE_H
#include <QObject>
#include <QList>
#include <QPair>
#include <QSharedPointer>
#include "core/models/containers/containers_defs.h"
#include "containers/containers_defs.h"
#include "core/defs.h"
#include "core/controllers/selfhosted/serverController.h"
#include "core/models/protocols/protocolConfig.h"
#include "core/controllers/serverController.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 QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) = 0;
virtual QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode) = 0;
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;
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);
protected:
void processConfigWithDnsSettings(const QPair<QString, QString> &dns, QSharedPointer<ProtocolConfig> &protocolConfig);
Vars generateCommonVars(const ServerCredentials &credentials, DockerContainer container) const;
void processConfigWithDnsSettings(const QPair<QString, QString> &dns, QString &protocolConfigString);
std::shared_ptr<Settings> m_settings;
QSharedPointer<ServerController> m_serverController;

View File

@@ -8,11 +8,10 @@
#include <QTemporaryFile>
#include <QUuid>
#include "core/models/containers/containers_defs.h"
#include "core/controllers/selfhosted/serverController.h"
#include "containers/containers_defs.h"
#include "core/controllers/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)
@@ -20,28 +19,6 @@ 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)
{
@@ -77,58 +54,21 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
return connData;
}
QSharedPointer<ProtocolConfig> Ikev2Configurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
QString Ikev2Configurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode)
{
Q_UNUSED(protocolConfig)
// IKEv2 uses a generic ProtocolConfig - no specific subclass needed
if (!protocolConfig) {
errorCode = ErrorCode::InternalError;
return nullptr;
}
Q_UNUSED(containerConfig)
ConnectionData connData = prepareIkev2Config(credentials, container, errorCode);
if (errorCode != ErrorCode::NoError) {
return nullptr;
return "";
}
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;
return genIkev2Config(connData);
}
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;

View File

@@ -5,8 +5,6 @@
#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
@@ -23,16 +21,13 @@ public:
QString host; // host ip
};
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
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);
};

View File

@@ -1,7 +1,8 @@
#include "openvpn_configurator.h"
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QProcess>
#include <QString>
#include <QTemporaryDir>
@@ -12,14 +13,10 @@
#include <QApplication>
#endif
#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 "containers/containers_defs.h"
#include "core/controllers/serverController.h"
#include "core/scripts_registry.h"
#include "core/models/protocols/openvpnProtocolConfig.h"
#include "protocols/protocols_defs.h"
#include "core/server_defs.h"
#include "settings.h"
#include "utilities.h"
@@ -27,46 +24,12 @@
#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)
{
@@ -108,21 +71,15 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
return connData;
}
QSharedPointer<ProtocolConfig> OpenVpnConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
QString OpenVpnConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, 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),
generateProtocolVars(credentials, container, protocolConfig));
m_serverController->genVarsForScript(credentials, container, containerConfig));
ConnectionData connData = prepareOpenVpnConfig(credentials, container, errorCode);
if (errorCode != ErrorCode::NoError) {
return nullptr;
return "";
}
config.replace("$OPENVPN_CA_CERT", connData.caCert);
@@ -140,100 +97,86 @@ QSharedPointer<ProtocolConfig> OpenVpnConfigurator::createConfig(const ServerCre
config.replace("block-outside-dns", "");
#endif
openVpnConfig->clientProtocolConfig.isEmpty = false;
openVpnConfig->clientProtocolConfig.clientId = connData.clientId;
openVpnConfig->clientProtocolConfig.nativeConfig = config;
QJsonObject jConfig;
jConfig[config_key::config] = config;
return openVpnConfig;
jConfig[config_key::clientId] = connData.clientId;
return QJsonDocument(jConfig).toJson();
}
void OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QSharedPointer<ProtocolConfig> &protocolConfig)
QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString)
{
processConfigWithDnsSettings(dns, protocolConfig);
processConfigWithDnsSettings(dns, protocolConfigString);
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;
QJsonObject json = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
QString config = json[config_key::config].toString();
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 (!isApiConfig) {
QRegularExpression regex("redirect-gateway.*");
config.replace(regex, "");
if (!m_settings->isSitesSplitTunnelingEnabled()) {
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
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)
nativeConfig.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
// Prevent ipv6 leak
// Prevent ipv6 leak
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
#endif
nativeConfig.append("block-ipv6\n");
}
config.append("block-ipv6\n");
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
QStringList routeList = m_settings->vpnRoutes();
if (!routeList.isEmpty()) {
for (auto route : routeList) {
nativeConfig.append("\nroute " + route + " 255.255.255.255 vpn_gateway\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");
}
}
#ifndef MZ_WINDOWS
nativeConfig.replace("block-outside-dns", "");
config.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());
nativeConfig.append(dnsConf);
config.append(dnsConf);
#endif
}
}, variant);
json[config_key::config] = config;
return QJsonDocument(json).toJson();
}
void OpenVpnConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QSharedPointer<ProtocolConfig> &protocolConfig)
QString OpenVpnConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString)
{
processConfigWithDnsSettings(dns, protocolConfig);
processConfigWithDnsSettings(dns, protocolConfigString);
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;
QJsonObject json = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
QString config = json[config_key::config].toString();
QRegularExpression regex("redirect-gateway.*");
nativeConfig.replace(regex, "");
QRegularExpression regex("redirect-gateway.*");
config.replace(regex, "");
// We don't use secondary DNS if primary DNS is AmneziaDNS
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
nativeConfig.replace(dnsRegex, "");
}
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
nativeConfig.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
// Prevent ipv6 leak
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
config.append("block-ipv6\n");
// Prevent ipv6 leak
nativeConfig.append("block-ipv6\n");
// remove block-outside-dns for all exported configs
config.replace("block-outside-dns", "");
// remove block-outside-dns for all exported configs
nativeConfig.replace("block-outside-dns", "");
}
}, variant);
json[config_key::config] = config;
return QJsonDocument(json).toJson();
}
ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId)
@@ -250,7 +193,7 @@ ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, const ServerC
.arg(clientId);
QStringList scriptList { script_import, script_sign };
QString script = m_serverController->replaceVars(scriptList.join("\n"), generateProtocolVars(credentials, container));
QString script = m_serverController->replaceVars(scriptList.join("\n"), m_serverController->genVarsForScript(credentials, container));
return m_serverController->runScript(credentials, script);
}

View File

@@ -6,7 +6,6 @@
#include "configurator_base.h"
#include "core/defs.h"
#include "core/models/protocols/openvpnProtocolConfig.h"
class OpenVpnConfigurator : public ConfiguratorBase
{
@@ -25,16 +24,13 @@ public:
QString host; // host ip
};
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
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;
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
static ConnectionData createCertRequest();

View File

@@ -4,10 +4,8 @@
#include <QJsonDocument>
#include <QJsonObject>
#include "core/models/containers/containers_defs.h"
#include "core/controllers/selfhosted/serverController.h"
#include "core/models/protocols/shadowsocksProtocolConfig.h"
#include "protocols/protocols_defs.h"
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
QObject *parent)
@@ -15,59 +13,28 @@ ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> setti
{
}
ConfiguratorBase::Vars ShadowSocksConfigurator::generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig) const
QString ShadowSocksConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode)
{
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 nullptr;
return "";
}
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", 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);
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");
QString textCfg = QJsonDocument(config).toJson();
QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(),
m_serverController->genVarsForScript(credentials, container, containerConfig));
shadowsocksConfig->clientProtocolConfig.isEmpty = false;
shadowsocksConfig->clientProtocolConfig.nativeConfig = textCfg;
return shadowsocksConfig;
// qDebug().noquote() << textCfg;
return textCfg;
}

View File

@@ -4,7 +4,6 @@
#include <QObject>
#include "configurator_base.h"
#include "core/models/protocols/shadowsocksProtocolConfig.h"
#include "core/defs.h"
class ShadowSocksConfigurator : public ConfiguratorBase
@@ -13,11 +12,8 @@ class ShadowSocksConfigurator : public ConfiguratorBase
public:
ShadowSocksConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
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;
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
};
#endif // SHADOWSOCKS_CONFIGURATOR_H

View File

@@ -3,86 +3,35 @@
#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 "core/models/containers/containers_defs.h"
#include "core/controllers/selfhosted/serverController.h"
#include "containers/containers_defs.h"
#include "core/controllers/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
@@ -114,94 +63,78 @@ 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 QSharedPointer<ProtocolConfig> &protocolConfig,
ErrorCode &errorCode)
const QJsonObject &containerConfig, ErrorCode &errorCode)
{
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
connData.host = credentials.hostName;
// 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;
}
connData.port = containerConfig.value(m_protocolName).toObject().value(config_key::port).toString(m_defaultPort);
if (connData.clientPrivKey.isEmpty() || connData.clientPubKey.isEmpty()) {
errorCode = ErrorCode::InternalError;
return connData;
}
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;
};
// 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;
};
errorCode = m_serverController->runContainerScript(credentials, container, getIpsScript, cbReadStdOut);
if (errorCode != ErrorCode::NoError) {
return connData;
}
auto ips = getIpsFromConf(stdOut);
errorCode = m_serverController->runContainerScript(credentials, container, script, cbReadStdOut);
if (errorCode != ErrorCode::NoError) {
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;
}
lastIp.setAddress(subnetAddress);
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 {
lastIp = ips.last();
int next = ips.last().split(".").last().toInt() + 1;
if (next > 254) {
errorCode = ErrorCode::AddressPoolError;
return connData;
}
nextIpNumber = QString::number(next);
}
quint8 lastOctet = static_cast<quint8>(lastIp.toIPv4Address());
switch (lastOctet) {
case 254: result.setAddress(lastIp.toIPv4Address() + 3); break;
case 255: result.setAddress(lastIp.toIPv4Address() + 2); break;
default: result.setAddress(lastIp.toIPv4Address() + 1); break;
}
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;
}
l.removeLast();
l.append(nextIpNumber);
return result;
}();
connData.clientIP = nextIp.toString();
connData.clientIP = l.join(".");
}
// 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;
@@ -228,44 +161,24 @@ 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, generateProtocolVars(credentials, container)));
credentials, m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
return connData;
}
QSharedPointer<ProtocolConfig> WireguardConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
QString WireguardConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode)
{
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;
}
QString scriptData = amnezia::scriptData(m_configTemplate, container);
QString config = m_serverController->replaceVars(
scriptData, generateProtocolVars(credentials, container, protocolConfig));
QString config =
m_serverController->replaceVars(scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
ConnectionData connData = prepareWireguardConfig(credentials, container, protocolConfig, errorCode);
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
if (errorCode != ErrorCode::NoError) {
return nullptr;
return "";
}
config.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", connData.clientPrivKey);
@@ -273,37 +186,40 @@ QSharedPointer<ProtocolConfig> WireguardConfigurator::createConfig(const ServerC
config.replace("$WIREGUARD_SERVER_PUBLIC_KEY", connData.serverPubKey);
config.replace("$WIREGUARD_PSK", connData.pskKey);
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);
const QJsonObject &wireguarConfig = containerConfig.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject();
QJsonObject jConfig;
jConfig[config_key::config] = config;
return result;
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();
}
void WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns,
const bool isApiConfig, QSharedPointer<ProtocolConfig> &protocolConfig)
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString)
{
processConfigWithDnsSettings(dns, protocolConfig);
processConfigWithDnsSettings(dns, protocolConfigString);
return protocolConfigString;
}
void WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns,
const bool isApiConfig, QSharedPointer<ProtocolConfig> &protocolConfig)
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString)
{
processConfigWithDnsSettings(dns, protocolConfig);
processConfigWithDnsSettings(dns, protocolConfigString);
return protocolConfigString;
}

View File

@@ -1,13 +1,10 @@
#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"
@@ -15,8 +12,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
{
@@ -29,23 +26,17 @@ public:
QString port;
};
QSharedPointer<ProtocolConfig> createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode) override;
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode);
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;
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
static ConnectionData genClientKeys();
private:
QList<QHostAddress> getIpsFromConf(const QString &input);
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode);
const QJsonObject &containerConfig, ErrorCode &errorCode);
bool m_isAwg;
QString m_serverConfigPath;

View File

@@ -6,11 +6,9 @@
#include <QUuid>
#include "logger.h"
#include "core/models/containers/containers_defs.h"
#include "core/controllers/selfhosted/serverController.h"
#include "containers/containers_defs.h"
#include "core/controllers/serverController.h"
#include "core/scripts_registry.h"
#include "core/models/protocols/xrayProtocolConfig.h"
#include "protocols/protocols_defs.h"
namespace {
Logger logger("XrayConfigurator");
@@ -21,24 +19,8 @@ XrayConfigurator::XrayConfigurator(std::shared_ptr<Settings> settings, const QSh
{
}
ConfiguratorBase::Vars XrayConfigurator::generateProtocolVars(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig) const
{
Vars vars = generateCommonVars(credentials, container);
auto xrayConfig = qSharedPointerCast<XrayProtocolConfig>(protocolConfig);
if (!xrayConfig) {
return vars;
}
vars.append({{"$XRAY_SITE_NAME", xrayConfig->serverProtocolConfig.site}});
vars.append({{"$XRAY_SERVER_PORT", xrayConfig->serverProtocolConfig.port}});
return vars;
}
QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
const QJsonObject &containerConfig, ErrorCode &errorCode)
{
// Generate new UUID for client
QString clientId = QUuid::createUuid().toString(QUuid::WithoutBraces);
@@ -62,6 +44,7 @@ QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentia
QJsonObject serverConfig = doc.object();
// Validate server config structure
if (!serverConfig.contains("inbounds")) {
logger.error() << "Server config missing 'inbounds' field";
errorCode = ErrorCode::InternalError;
@@ -123,7 +106,7 @@ QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentia
QString restartScript = QString("sudo docker restart $CONTAINER_NAME");
errorCode = m_serverController->runScript(
credentials,
m_serverController->replaceVars(restartScript, generateProtocolVars(credentials, container))
m_serverController->replaceVars(restartScript, m_serverController->genVarsForScript(credentials, container))
);
if (errorCode != ErrorCode::NoError) {
@@ -134,30 +117,24 @@ QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentia
return clientId;
}
QSharedPointer<ProtocolConfig> XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode)
QString XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, 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);
QString xrayClientId = prepareServerConfig(credentials, container, containerConfig, errorCode);
if (errorCode != ErrorCode::NoError || xrayClientId.isEmpty()) {
logger.error() << "Failed to prepare server config";
errorCode = ErrorCode::InternalError;
return nullptr;
return "";
}
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container),
generateProtocolVars(credentials, container, protocolConfig));
m_serverController->genVarsForScript(credentials, container, containerConfig));
if (config.isEmpty()) {
logger.error() << "Failed to get config template";
errorCode = ErrorCode::InternalError;
return nullptr;
return "";
}
QString xrayPublicKey =
@@ -165,7 +142,7 @@ QSharedPointer<ProtocolConfig> XrayConfigurator::createConfig(const ServerCreden
if (errorCode != ErrorCode::NoError || xrayPublicKey.isEmpty()) {
logger.error() << "Failed to get public key";
errorCode = ErrorCode::InternalError;
return nullptr;
return "";
}
xrayPublicKey.replace("\n", "");
@@ -174,7 +151,7 @@ QSharedPointer<ProtocolConfig> XrayConfigurator::createConfig(const ServerCreden
if (errorCode != ErrorCode::NoError || xrayShortId.isEmpty()) {
logger.error() << "Failed to get short ID";
errorCode = ErrorCode::InternalError;
return nullptr;
return "";
}
xrayShortId.replace("\n", "");
@@ -185,16 +162,12 @@ QSharedPointer<ProtocolConfig> XrayConfigurator::createConfig(const ServerCreden
<< "XRAY_PUBLIC_KEY:" << !config.contains("$XRAY_PUBLIC_KEY")
<< "XRAY_SHORT_ID:" << !config.contains("$XRAY_SHORT_ID");
errorCode = ErrorCode::InternalError;
return nullptr;
return "";
}
config.replace("$XRAY_CLIENT_ID", xrayClientId);
config.replace("$XRAY_PUBLIC_KEY", xrayPublicKey);
config.replace("$XRAY_SHORT_ID", xrayShortId);
xrayConfig->clientProtocolConfig.isEmpty = false;
xrayConfig->clientProtocolConfig.clientId = xrayClientId;
xrayConfig->clientProtocolConfig.nativeConfig = config;
return xrayConfig;
return config;
}

View File

@@ -4,7 +4,6 @@
#include <QObject>
#include "configurator_base.h"
#include "core/models/protocols/xrayProtocolConfig.h"
#include "core/defs.h"
class XrayConfigurator : public ConfiguratorBase
@@ -13,15 +12,12 @@ class XrayConfigurator : public ConfiguratorBase
public:
XrayConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
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;
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode);
private:
QString prepareServerConfig(const ServerCredentials &credentials, DockerContainer container,
const QSharedPointer<ProtocolConfig> &protocolConfig, ErrorCode &errorCode);
QString prepareServerConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode);
};
#endif // XRAY_CONFIGURATOR_H

View File

@@ -110,19 +110,22 @@ QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
QObject::tr("OpenVPN is the most popular VPN protocol, with flexible configuration options. It uses its "
"own security protocol with SSL/TLS for key exchange.") },
{ DockerContainer::ShadowSocks,
QObject::tr("Shadowsocks masks VPN traffic, making it resemble normal web traffic, but it may still be detected by certain analysis systems.") },
QObject::tr("Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it "
"may be recognized by analysis systems in some highly censored regions.") },
{ DockerContainer::Cloak,
QObject::tr("OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against "
"active-probing detection. It is very resistant to detection, but offers low speed.") },
"active-probing detection. Ideal for bypassing blocking in regions with the highest levels "
"of censorship.") },
{ DockerContainer::WireGuard,
QObject::tr("WireGuard - popular VPN protocol with high performance, high speed and low power "
"consumption.") },
QObject::tr("WireGuard - New popular VPN protocol with high performance, high speed and low power "
"consumption. Recommended for regions with low levels of censorship.") },
{ DockerContainer::Awg,
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.") },
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.") },
{ DockerContainer::Xray,
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.") },
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.") },
{ 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.") },
@@ -140,83 +143,100 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
{
return {
{ DockerContainer::OpenVpn,
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") },
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.") },
{ DockerContainer::ShadowSocks,
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"
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"
"* Detectable by some DPI systems\n"
"* Operates over TCP protocol\n") },
"* Works over TCP network protocol.") },
{ DockerContainer::Cloak,
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"
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"
"* High power consumption on mobile devices\n"
"* Flexible configuration options\n"
"* Undetectable by DPI systems\n"
"* Operates over TCP protocol on port 443") },
"* Flexible settings\n"
"* Not recognised by DPI analysis systems\n"
"* Works over TCP network protocol, 443 port.\n") },
{ DockerContainer::WireGuard,
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") },
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.") },
{ DockerContainer::Awg,
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") },
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.") },
{ DockerContainer::Xray,
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") },
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.")
},
{ DockerContainer::Ipsec,
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)") },
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.") },
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
{ DockerContainer::Dns, QObject::tr("DNS Service") },
@@ -312,7 +332,9 @@ 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;
}
}
@@ -320,7 +342,9 @@ bool ContainerProps::isEasySetupContainer(DockerContainer container)
QString ContainerProps::easySetupHeader(DockerContainer container)
{
switch (container) {
case DockerContainer::Awg: return tr("Automatic");
case DockerContainer::WireGuard: return tr("Low");
case DockerContainer::Awg: return tr("High");
// case DockerContainer::Cloak: return tr("Extreme");
default: return "";
}
}
@@ -328,8 +352,10 @@ QString ContainerProps::easySetupHeader(DockerContainer container)
QString ContainerProps::easySetupDescription(DockerContainer container)
{
switch (container) {
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.");
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.");
default: return "";
}
}
@@ -337,7 +363,9 @@ QString ContainerProps::easySetupDescription(DockerContainer container)
int ContainerProps::easySetupOrder(DockerContainer container)
{
switch (container) {
case DockerContainer::Awg: return 1;
case DockerContainer::WireGuard: return 3;
case DockerContainer::Awg: return 2;
// case DockerContainer::Cloak: return 1;
default: return 0;
}
}
@@ -356,9 +384,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();
}

View File

@@ -1,67 +0,0 @@
#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

View File

@@ -1,164 +0,0 @@
#include "apiUtils.h"
#include <QDateTime>
#include <QJsonObject>
namespace
{
const QByteArray AMNEZIA_CONFIG_SIGNATURE = QByteArray::fromHex("000000ff");
QString escapeUnicode(const QString &input)
{
QString output;
for (QChar c : input) {
if (c.unicode() < 0x20 || c.unicode() > 0x7E) {
output += QString("\\u%1").arg(QString::number(c.unicode(), 16).rightJustified(4, '0'));
} else {
output += c;
}
}
return output;
}
}
bool apiUtils::isSubscriptionExpired(const QString &subscriptionEndDate)
{
QDateTime now = QDateTime::currentDateTime();
QDateTime endDate = QDateTime::fromString(subscriptionEndDate, Qt::ISODateWithMs);
return endDate < now;
}
bool apiUtils::isServerFromApi(const QJsonObject &serverConfigObject)
{
auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt();
switch (configVersion) {
case 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)));
}

View File

@@ -1,26 +0,0 @@
#ifndef APIUTILS_H
#define APIUTILS_H
#include <QNetworkReply>
#include <QObject>
#include "apiDefs.h"
#include "core/defs.h"
namespace apiUtils
{
bool isServerFromApi(const QJsonObject &serverConfigObject);
bool isSubscriptionExpired(const QString &subscriptionEndDate);
bool isPremiumServer(const QJsonObject &serverConfigObject);
apiDefs::ConfigType getConfigType(const QJsonObject &serverConfigObject);
amnezia::ServerConfigType getConfigSource(const QJsonObject &serverConfigObject);
amnezia::ErrorCode checkNetworkReplyErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply);
QString getPremiumV1VpnKey(const QJsonObject &serverConfigObject);
}
#endif // APIUTILS_H

View File

@@ -1,684 +0,0 @@
#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);
}

View File

@@ -1,57 +0,0 @@
#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

View File

@@ -1,134 +0,0 @@
#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();
}

View File

@@ -1,50 +0,0 @@
#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

View File

@@ -1,94 +0,0 @@
#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());
}

View File

@@ -1,34 +0,0 @@
#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

View File

@@ -1,364 +0,0 @@
#include "gatewayController.h"
#include <algorithm>
#include <random>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkReply>
#include <QUrl>
#include "QBlockCipher.h"
#include "QRsa.h"
#include "amnezia_application.h"
#include "core/api/apiUtils.h"
#include "core/networkUtilities.h"
#include "utilities.h"
#ifdef AMNEZIA_DESKTOP
#include "core/ipcclient.h"
#endif
namespace
{
namespace configKey
{
constexpr char aesKey[] = "aes_key";
constexpr char aesIv[] = "aes_iv";
constexpr char aesSalt[] = "aes_salt";
constexpr char apiPayload[] = "api_payload";
constexpr char keyPayload[] = "key_payload";
}
constexpr QLatin1String errorResponsePattern1("No active configuration found for");
constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for");
constexpr QLatin1String errorResponsePattern3("Account not found.");
constexpr QLatin1String updateRequestResponsePattern("client version update is required");
}
GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
const bool isStrictKillSwitchEnabled, QObject *parent)
: QObject(parent),
m_gatewayEndpoint(gatewayEndpoint),
m_isDevEnvironment(isDevEnvironment),
m_requestTimeoutMsecs(requestTimeoutMsecs),
m_isStrictKillSwitchEnabled(isStrictKillSwitchEnabled)
{
}
ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBody)
{
#ifdef Q_OS_IOS
IosController::Instance()->requestInetAccess();
QThread::msleep(10);
#endif
QNetworkRequest request;
request.setTransferTimeout(m_requestTimeoutMsecs);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setUrl(QString(endpoint).arg(m_gatewayEndpoint));
// bypass killSwitch exceptions for API-gateway
#ifdef AMNEZIA_DESKTOP
if (m_isStrictKillSwitchEnabled) {
QString host = QUrl(request.url()).host();
QString ip = NetworkUtilities::getIPAddress(host);
if (!ip.isEmpty()) {
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip });
}
}
#endif
QNetworkReply *reply;
reply = amnApp->networkManager()->get(request);
QEventLoop wait;
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
QList<QSslError> sslErrors;
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec();
responseBody = reply->readAll();
if (sslErrors.isEmpty() && shouldBypassProxy(reply, responseBody, false)) {
auto requestFunction = [&request, &responseBody](const QString &url) {
request.setUrl(url);
return amnApp->networkManager()->get(request);
};
auto replyProcessingFunction = [&responseBody, &reply, &sslErrors, this](QNetworkReply *nestedReply,
const QList<QSslError> &nestedSslErrors) {
responseBody = nestedReply->readAll();
if (!sslErrors.isEmpty() || !shouldBypassProxy(nestedReply, responseBody, false)) {
sslErrors = nestedSslErrors;
reply = nestedReply;
return true;
}
return false;
};
bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
}
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
reply->deleteLater();
return errorCode;
}
ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody)
{
#ifdef Q_OS_IOS
IosController::Instance()->requestInetAccess();
QThread::msleep(10);
#endif
QNetworkRequest request;
request.setTransferTimeout(m_requestTimeoutMsecs);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setUrl(endpoint.arg(m_gatewayEndpoint));
// bypass killSwitch exceptions for API-gateway
#ifdef AMNEZIA_DESKTOP
if (m_isStrictKillSwitchEnabled) {
QString host = QUrl(request.url()).host();
QString ip = NetworkUtilities::getIPAddress(host);
if (!ip.isEmpty()) {
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip });
}
}
#endif
QSimpleCrypto::QBlockCipher blockCipher;
QByteArray key = blockCipher.generatePrivateSalt(32);
QByteArray iv = blockCipher.generatePrivateSalt(32);
QByteArray salt = blockCipher.generatePrivateSalt(8);
QJsonObject keyPayload;
keyPayload[configKey::aesKey] = QString(key.toBase64());
keyPayload[configKey::aesIv] = QString(iv.toBase64());
keyPayload[configKey::aesSalt] = QString(salt.toBase64());
QByteArray encryptedKeyPayload;
QByteArray encryptedApiPayload;
try {
QSimpleCrypto::QRsa rsa;
EVP_PKEY *publicKey = nullptr;
try {
QByteArray rsaKey = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
QSimpleCrypto::QRsa rsa;
publicKey = rsa.getPublicKeyFromByteArray(rsaKey);
} catch (...) {
Utils::logException();
qCritical() << "error loading public key from environment variables";
return ErrorCode::ApiMissingAgwPublicKey;
}
encryptedKeyPayload = rsa.encrypt(QJsonDocument(keyPayload).toJson(), publicKey, RSA_PKCS1_PADDING);
EVP_PKEY_free(publicKey);
encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), key, iv, "", salt);
} catch (...) { // todo change error handling in QSimpleCrypto?
Utils::logException();
qCritical() << "error when encrypting the request body";
return ErrorCode::ApiConfigDecryptionError;
}
QJsonObject requestBody;
requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
QNetworkReply *reply = amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
QEventLoop wait;
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
QList<QSslError> sslErrors;
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec();
QByteArray encryptedResponseBody = reply->readAll();
if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
auto requestFunction = [&request, &encryptedResponseBody, &requestBody](const QString &url) {
request.setUrl(url);
return amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
};
auto replyProcessingFunction = [&encryptedResponseBody, &reply, &sslErrors, &key, &iv, &salt,
this](QNetworkReply *nestedReply, const QList<QSslError> &nestedSslErrors) {
encryptedResponseBody = nestedReply->readAll();
reply = nestedReply;
if (!sslErrors.isEmpty() || shouldBypassProxy(nestedReply, encryptedResponseBody, true, key, iv, salt)) {
sslErrors = nestedSslErrors;
return false;
}
return true;
};
bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
}
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
reply->deleteLater();
if (errorCode) {
return errorCode;
}
try {
responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
return ErrorCode::NoError;
} catch (...) { // todo change error handling in QSimpleCrypto?
Utils::logException();
qCritical() << "error when decrypting the request body";
return ErrorCode::ApiConfigDecryptionError;
}
}
QStringList GatewayController::getProxyUrls()
{
QNetworkRequest request;
request.setTransferTimeout(m_requestTimeoutMsecs);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QEventLoop wait;
QList<QSslError> sslErrors;
QNetworkReply *reply;
QStringList proxyStorageUrls;
if (m_isDevEnvironment) {
proxyStorageUrls = QString(DEV_S3_ENDPOINT).split(", ");
} else {
proxyStorageUrls = QString(PROD_S3_ENDPOINT).split(", ");
}
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
for (const auto &proxyStorageUrl : proxyStorageUrls) {
request.setUrl(proxyStorageUrl);
reply = amnApp->networkManager()->get(request);
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec();
if (reply->error() == QNetworkReply::NetworkError::NoError) {
auto encryptedResponseBody = reply->readAll();
reply->deleteLater();
EVP_PKEY *privateKey = nullptr;
QByteArray responseBody;
try {
if (!m_isDevEnvironment) {
QCryptographicHash hash(QCryptographicHash::Sha512);
hash.addData(key);
QByteArray hashResult = hash.result().toHex();
QByteArray key = QByteArray::fromHex(hashResult.left(64));
QByteArray iv = QByteArray::fromHex(hashResult.mid(64, 32));
QByteArray ba = QByteArray::fromBase64(encryptedResponseBody);
QSimpleCrypto::QBlockCipher blockCipher;
responseBody = blockCipher.decryptAesBlockCipher(ba, key, iv);
} else {
responseBody = encryptedResponseBody;
}
} catch (...) {
Utils::logException();
qCritical() << "error loading private key from environment variables or decrypting payload" << encryptedResponseBody;
continue;
}
auto endpointsArray = QJsonDocument::fromJson(responseBody).array();
QStringList endpoints;
for (const auto &endpoint : endpointsArray) {
endpoints.push_back(endpoint.toString());
}
return endpoints;
} else {
apiUtils::checkNetworkReplyErrors(sslErrors, reply);
qDebug() << "go to the next storage endpoint";
reply->deleteLater();
}
}
return {};
}
bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key,
const QByteArray &iv, const QByteArray &salt)
{
if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError || reply->error() == QNetworkReply::NetworkError::TimeoutError) {
qDebug() << "timeout occurred";
qDebug() << reply->error();
return true;
} else if (responseBody.contains("html")) {
qDebug() << "the response contains an html tag";
return true;
} else if (reply->error() == QNetworkReply::NetworkError::ContentNotFoundError) {
if (responseBody.contains(errorResponsePattern1) || responseBody.contains(errorResponsePattern2)
|| responseBody.contains(errorResponsePattern3)) {
return false;
} else {
qDebug() << reply->error();
return true;
}
} else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) {
if (responseBody.contains(updateRequestResponsePattern)) {
return false;
} else {
qDebug() << reply->error();
return true;
}
} else if (reply->error() != QNetworkReply::NetworkError::NoError) {
qDebug() << reply->error();
return true;
} else if (checkEncryption) {
try {
QSimpleCrypto::QBlockCipher blockCipher;
static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
} catch (...) {
qDebug() << "failed to decrypt the data";
return true;
}
}
return false;
}
void GatewayController::bypassProxy(const QString &endpoint, QNetworkReply *reply,
std::function<QNetworkReply *(const QString &url)> requestFunction,
std::function<bool(QNetworkReply *reply, const QList<QSslError> &sslErrors)> replyProcessingFunction)
{
QStringList proxyUrls = getProxyUrls();
std::random_device randomDevice;
std::mt19937 generator(randomDevice());
std::shuffle(proxyUrls.begin(), proxyUrls.end(), generator);
QEventLoop wait;
QList<QSslError> sslErrors;
QByteArray responseBody;
for (const QString &proxyUrl : proxyUrls) {
qDebug() << "go to the next proxy endpoint";
reply->deleteLater(); // delete the previous reply
reply = requestFunction(endpoint.arg(proxyUrl));
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec();
if (replyProcessingFunction(reply, sslErrors)) {
break;
}
}
}

View File

@@ -1,37 +0,0 @@
#ifndef GATEWAYCONTROLLER_H
#define GATEWAYCONTROLLER_H
#include <QNetworkReply>
#include <QObject>
#include "core/defs.h"
#ifdef Q_OS_IOS
#include "platforms/ios/ios_controller.h"
#endif
class GatewayController : public QObject
{
Q_OBJECT
public:
explicit GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
const bool isStrictKillSwitchEnabled, QObject *parent = nullptr);
amnezia::ErrorCode get(const QString &endpoint, QByteArray &responseBody);
amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody);
private:
QStringList getProxyUrls();
bool shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key = "",
const QByteArray &iv = "", const QByteArray &salt = "");
void bypassProxy(const QString &endpoint, QNetworkReply *reply, std::function<QNetworkReply *(const QString &url)> requestFunction,
std::function<bool(QNetworkReply *reply, const QList<QSslError> &sslErrors)> replyProcessingFunction);
int m_requestTimeoutMsecs;
QString m_gatewayEndpoint;
bool m_isDevEnvironment = false;
bool m_isStrictKillSwitchEnabled = false;
};
#endif // GATEWAYCONTROLLER_H

View File

@@ -0,0 +1,509 @@
#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();
if (errorCode == ErrorCode::NoError) {
if (!responseBody.contains("services")) {
return ErrorCode::ApiServicesMissingError;
}
}
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;
}

View File

@@ -0,0 +1,50 @@
#ifndef APICONTROLLER_H
#define APICONTROLLER_H
#include <QObject>
#include "configurators/openvpn_configurator.h"
#ifdef Q_OS_IOS
#include "platforms/ios/ios_controller.h"
#endif
class ApiController : public QObject
{
Q_OBJECT
public:
explicit ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent = nullptr);
public slots:
void updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig);
ErrorCode getServicesList(QByteArray &responseBody);
ErrorCode getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType,
const QString &protocol, const QString &serverCountryCode, 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

View File

@@ -1,172 +0,0 @@
#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;
}

View File

@@ -1,59 +0,0 @@
#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

View File

@@ -1,93 +0,0 @@
#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;
}

View File

@@ -1,57 +0,0 @@
#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

View File

@@ -1,464 +0,0 @@
#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;
}

View File

@@ -1,170 +0,0 @@
#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

View File

@@ -1,66 +0,0 @@
#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();
}

View File

@@ -1,32 +0,0 @@
#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

View File

@@ -1,825 +0,0 @@
#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;
}

View File

@@ -1,133 +0,0 @@
#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

View File

@@ -1,353 +0,0 @@
#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)));
}

View File

@@ -1,117 +0,0 @@
#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

View File

@@ -1,397 +0,0 @@
#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;
}

View File

@@ -1,111 +0,0 @@
#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

View File

@@ -1,140 +0,0 @@
#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;
}
}

View File

@@ -1,45 +0,0 @@
#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

View File

@@ -8,15 +8,6 @@
#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>
@@ -31,22 +22,14 @@
#include <chrono>
#include <thread>
#include "core/models/containers/containers_defs.h"
#include "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 "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"
#include "vpnConfigurationController.h"
namespace
{
@@ -123,10 +106,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, generateVarsForContainer(credentials, container)), cbReadStdOut, cbReadStdErr);
e = runScript(credentials, replaceVars(runner, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
QString remover = QString("sudo docker exec -i $CONTAINER_NAME rm %1 ").arg(fileName);
runScript(credentials, replaceVars(remover, generateVarsForContainer(credentials, container)), cbReadStdOut, cbReadStdErr);
runScript(credentials, replaceVars(remover, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
return e;
}
@@ -149,30 +132,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, generateVarsForContainer(credentials, container)));
e = runScript(credentials, replaceVars(mkdir, genVarsForScript(credentials, container)));
if (e)
return e;
if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) {
e = runScript(credentials,
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, path),
generateVarsForContainer(credentials, container)),
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path),
genVarsForScript(credentials, container)),
cbReadStd, cbReadStd);
if (e)
return e;
} else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) {
e = runScript(credentials,
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, tmpFileName),
generateVarsForContainer(credentials, container)),
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName),
genVarsForScript(credentials, container)),
cbReadStd, cbReadStd);
if (e)
return e;
e = runScript(credentials,
replaceVars(QStringLiteral("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName, path),
generateVarsForContainer(credentials, container)),
replaceVars(QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path),
genVarsForScript(credentials, container)),
cbReadStd, cbReadStd);
if (e)
@@ -184,7 +167,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
return ErrorCode::ServerContainerMissingError;
}
runScript(credentials, replaceVars(QString("sudo shred -u %1").arg(tmpFileName), generateVarsForContainer(credentials, container)));
runScript(credentials, replaceVars(QString("sudo shred -u %1").arg(tmpFileName), genVarsForScript(credentials, container)));
return e;
}
@@ -194,7 +177,7 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
errorCode = ErrorCode::NoError;
QString script = QStringLiteral("sudo docker exec -i %1 sh -c \"xxd -p '%2'\"").arg(ContainerProps::containerToString(container), path);
QString script = QString("sudo docker exec -i %1 sh -c \"xxd -p \'%2\'\"").arg(ContainerProps::containerToString(container)).arg(path);
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
@@ -253,10 +236,10 @@ ErrorCode ServerController::removeAllContainers(const ServerCredentials &credent
ErrorCode ServerController::removeContainer(const ServerCredentials &credentials, DockerContainer container)
{
return runScript(credentials,
replaceVars(amnezia::scriptData(SharedScriptType::remove_container), generateVarsForContainer(credentials, container)));
replaceVars(amnezia::scriptData(SharedScriptType::remove_container), genVarsForScript(credentials, container)));
}
ErrorCode ServerController::setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, bool isUpdate)
ErrorCode ServerController::setupContainer(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config, bool isUpdate)
{
qDebug().noquote() << "ServerController::setupContainer" << ContainerProps::containerToString(container);
ErrorCode e = ErrorCode::NoError;
@@ -316,8 +299,8 @@ ErrorCode ServerController::setupContainer(const ServerCredentials &credentials,
return startupContainerWorker(credentials, container, config);
}
ErrorCode ServerController::updateContainer(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &oldConfig,
ContainerConfig &newConfig)
ErrorCode ServerController::updateContainer(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &oldConfig,
QJsonObject &newConfig)
{
bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig);
qDebug() << "ServerController::updateContainer for container" << container << "reinstall required is" << reinstallRequired;
@@ -333,20 +316,70 @@ ErrorCode ServerController::updateContainer(const ServerCredentials &credentials
}
}
bool ServerController::isReinstallContainerRequired(DockerContainer container, const ContainerConfig &oldConfig, const ContainerConfig &newConfig)
bool ServerController::isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig)
{
const auto &mainProto = ContainerProps::defaultProtocol(container);
const QString protocolName = ProtocolProps::protoToString(mainProto);
Proto mainProto = ContainerProps::defaultProtocol(container);
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
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;
}
// Use the existing isServerSettingsEqual method from ProtocolConfig
return !oldProtocolConfig->isServerSettingsEqual(newProtocolConfig);
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;
}
ErrorCode ServerController::installDockerWorker(const ServerCredentials &credentials, DockerContainer container)
@@ -366,7 +399,7 @@ ErrorCode ServerController::installDockerWorker(const ServerCredentials &credent
};
ErrorCode error =
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::install_docker), generateVarsForContainer(credentials, DockerContainer::None)),
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::install_docker), genVarsForScript(credentials)),
cbReadStdOut, cbReadStdErr);
qDebug().noquote() << "ServerController::installDockerWorker" << stdOut;
@@ -378,17 +411,17 @@ ErrorCode ServerController::installDockerWorker(const ServerCredentials &credent
return error;
}
ErrorCode ServerController::prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config)
ErrorCode ServerController::prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
{
// create folder on host
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::prepare_host), generateVarsForContainer(credentials, container)));
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::prepare_host), genVarsForScript(credentials, container)));
}
ErrorCode ServerController::buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config)
ErrorCode ServerController::buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
{
QString dockerFilePath = amnezia::server::getDockerfileFolder(container) + "/Dockerfile";
QString scriptString = QString("sudo rm %1").arg(dockerFilePath);
ErrorCode errorCode = runScript(credentials, replaceVars(scriptString, generateVarsForContainer(credentials, container)));
ErrorCode errorCode = runScript(credentials, replaceVars(scriptString, genVarsForScript(credentials, container)));
if (errorCode)
return errorCode;
@@ -402,27 +435,18 @@ 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 error =
errorCode =
runScript(credentials,
replaceVars(amnezia::scriptData(SharedScriptType::build_container), generateVarsForContainer(credentials, container, config)),
cbReadStdOut, cbReadStdErr);
replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)),
cbReadStdOut);
if (errorCode)
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;
return errorCode;
}
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config)
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
{
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
@@ -432,7 +456,7 @@ ErrorCode ServerController::runContainerWorker(const ServerCredentials &credenti
ErrorCode e = runScript(credentials,
replaceVars(amnezia::scriptData(ProtocolScriptType::run_container, container),
generateVarsForContainer(credentials, container, config)),
genVarsForScript(credentials, container, config)),
cbReadStdOut);
if (stdOut.contains("address already in use"))
@@ -445,7 +469,7 @@ ErrorCode ServerController::runContainerWorker(const ServerCredentials &credenti
return e;
}
ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config)
ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
{
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
@@ -459,16 +483,15 @@ ErrorCode ServerController::configureContainerWorker(const ServerCredentials &cr
ErrorCode e = runContainerScript(credentials, container,
replaceVars(amnezia::scriptData(ProtocolScriptType::configure_container, container),
generateVarsForContainer(credentials, container, config)),
genVarsForScript(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 ContainerConfig &config)
ErrorCode ServerController::startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
{
QString script = amnezia::scriptData(ProtocolScriptType::container_startup, container);
@@ -476,7 +499,7 @@ ErrorCode ServerController::startupContainerWorker(const ServerCredentials &cred
return ErrorCode::NoError;
}
ErrorCode e = uploadTextFileToContainer(container, credentials, replaceVars(script, generateVarsForContainer(credentials, container, config)),
ErrorCode e = uploadTextFileToContainer(container, credentials, replaceVars(script, genVarsForScript(credentials, container, config)),
"/opt/amnezia/start.sh");
if (e)
return e;
@@ -484,105 +507,133 @@ 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\"",
generateVarsForContainer(credentials, container, config)));
genVarsForScript(credentials, container, config)));
}
ServerController::Vars ServerController::generateVarsForContainer(const ServerCredentials &credentials, DockerContainer container,
const ContainerConfig &config)
ServerController::Vars ServerController::genVarsForScript(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &config)
{
// 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);
}
}
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", "" } });
}
// 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()}});
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" } });
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}});
}
// 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"}});
}
vars.append({ { "$SERVER_IP_ADDRESS", serverIp } });
} else {
qWarning() << "ServerController::genVarsForScript unable to resolve address for credentials.hostName";
}
return vars;
@@ -612,7 +663,7 @@ void ServerController::cancelInstallation()
ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials)
{
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::setup_host_firewall), generateVarsForContainer(credentials, DockerContainer::None)));
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::setup_host_firewall), genVarsForScript(credentials)));
}
QString ServerController::replaceVars(const QString &script, const Vars &vars)
@@ -624,7 +675,7 @@ QString ServerController::replaceVars(const QString &script, const Vars &vars)
return s;
}
ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config)
ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
{
if (container == DockerContainer::Dns) {
return ErrorCode::NoError;
@@ -641,40 +692,18 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
};
const Proto protocol = ContainerProps::defaultProtocol(container);
const QString protocolName = ProtocolProps::protoToString(protocol);
auto protocolConfig = config.protocolConfigs.value(protocolName);
const QString containerString = ProtocolProps::protoToString(protocol);
const QJsonObject containerConfig = config.value(containerString).toObject();
QStringList fixedPorts = ContainerProps::fixedPortsForContainer(container);
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;
}
}
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);
// TODO reimplement with netstat
QString script = QString("which lsof > /dev/null 2>&1 || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
QString script = QString("which lsof &>/dev/null || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
for (auto &port : fixedPorts) {
script = script.append("|:%1").arg(port);
}
@@ -687,12 +716,12 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
tcpProtoScript.append(" | grep LISTEN");
ErrorCode errorCode =
runScript(credentials, replaceVars(tcpProtoScript, generateVarsForContainer(credentials, container)), cbReadStdOut, cbReadStdErr);
runScript(credentials, replaceVars(tcpProtoScript, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
errorCode = runScript(credentials, replaceVars(udpProtoScript, generateVarsForContainer(credentials, container)), cbReadStdOut, cbReadStdErr);
errorCode = runScript(credentials, replaceVars(udpProtoScript, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
@@ -709,7 +738,7 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
script = script.append(" | grep LISTEN");
}
ErrorCode errorCode = runScript(credentials, replaceVars(script, generateVarsForContainer(credentials, container)), cbReadStdOut, cbReadStdErr);
ErrorCode errorCode = runScript(credentials, replaceVars(script, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
@@ -722,6 +751,10 @@ 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";
@@ -733,18 +766,10 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
};
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo);
ErrorCode error = runScript(credentials, replaceVars(scriptData, generateVarsForContainer(credentials, DockerContainer::None)), cbReadStdOut, cbReadStdErr);
ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
if (credentials.userName != "root" && stdOut.contains("sudo:") && !stdOut.contains("uname:") && stdOut.contains("not found"))
return ErrorCode::ServerSudoPackageIsNotPreinstalled;
if (credentials.userName != "root" && !stdOut.contains("sudo") && !stdOut.contains("wheel"))
if (!stdOut.contains("sudo"))
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;
}
@@ -771,12 +796,12 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential
return ErrorCode::ServerCancelInstallation;
}
stdOut.clear();
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::check_server_is_busy), generateVarsForContainer(credentials, DockerContainer::None)),
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::check_server_is_busy), genVarsForScript(credentials)),
cbReadStdOut, cbReadStdErr);
if (stdOut.contains("Packet manager not found"))
return ErrorCode::ServerPacketManagerError;
if (stdOut.contains("fuser not installed") || stdOut.contains("cat not installed"))
if (stdOut.contains("fuser not installed"))
return ErrorCode::NoError;
if (stdOut.isEmpty()) {

View File

@@ -4,8 +4,7 @@
#include <QJsonObject>
#include <QObject>
#include "core/models/containers/containers_defs.h"
#include "core/models/containers/containerConfig.h"
#include "containers/containers_defs.h"
#include "core/defs.h"
#include "core/sshclient.h"
@@ -26,12 +25,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, ContainerConfig &config, bool isUpdate = false);
ErrorCode updateContainer(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &oldConfig,
ContainerConfig &newConfig);
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 startupContainerWorker(const ServerCredentials &credentials, DockerContainer container,
const ContainerConfig &config = ContainerConfig());
const QJsonObject &config = QJsonObject());
ErrorCode uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials, const QString &file,
const QString &path,
@@ -40,8 +39,8 @@ public:
ErrorCode &errorCode);
QString replaceVars(const QString &script, const Vars &vars);
Vars generateVarsForContainer(const ServerCredentials &credentials, DockerContainer container,
const ContainerConfig &config = ContainerConfig());
Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None,
const QJsonObject &config = QJsonObject());
ErrorCode runScript(const ServerCredentials &credentials, QString script,
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
@@ -60,14 +59,14 @@ public:
private:
ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container);
ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config = ContainerConfig());
ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject());
ErrorCode buildContainerWorker(const ServerCredentials &credentials, DockerContainer container,
const ContainerConfig &config = ContainerConfig());
ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config);
ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config);
const QJsonObject &config = QJsonObject());
ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config);
ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config);
ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config);
bool isReinstallContainerRequired(DockerContainer container, const ContainerConfig &oldConfig, const ContainerConfig &newConfig);
ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config);
bool isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig);
ErrorCode isUserInSudo(const ServerCredentials &credentials, DockerContainer container);
ErrorCode isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container);

View File

@@ -1,238 +0,0 @@
#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);
}

View File

@@ -1,88 +0,0 @@
#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

View File

@@ -1,228 +0,0 @@
#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();
}

View File

@@ -1,71 +0,0 @@
#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

View File

@@ -7,18 +7,6 @@
#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)
@@ -41,33 +29,8 @@ 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, ContainerConfig &containerConfig)
const DockerContainer container, QJsonObject &containerConfig)
{
ErrorCode errorCode = ErrorCode::NoError;
@@ -76,34 +39,25 @@ ErrorCode VpnConfigurationsController::createProtocolConfigForContainer(const Se
}
for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
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);
}
QJsonObject protocolConfig = containerConfig.value(ProtocolProps::protoToString(protocol)).toObject();
auto configurator = createConfigurator(protocol);
auto result = configurator->createConfig(credentials, container, protocolConfig, errorCode);
if (errorCode != ErrorCode::NoError || !result) {
QString protocolConfigString = configurator->createConfig(credentials, container, containerConfig, errorCode);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
containerConfig.protocolConfigs.insert(protocolName, result);
protocolConfig.insert(config_key::last_config, protocolConfigString);
containerConfig.insert(ProtocolProps::protoToString(protocol), protocolConfig);
}
return errorCode;
}
ErrorCode VpnConfigurationsController::createProtocolConfigString(const bool isApiConfig, const QPair<QString, QString> &dns,
const ServerCredentials &credentials, const DockerContainer container,
const ContainerConfig &containerConfig, const Proto protocol,
QString &protocolConfigString)
const ServerCredentials &credentials, const DockerContainer container,
const QJsonObject &containerConfig, const Proto protocol,
QString &protocolConfigString)
{
ErrorCode errorCode = ErrorCode::NoError;
@@ -111,30 +65,20 @@ ErrorCode VpnConfigurationsController::createProtocolConfigString(const bool isA
return errorCode;
}
QString protocolName = ProtocolProps::protoToString(protocol);
auto protocolConfig = containerConfig.protocolConfigs.value(protocolName);
if (!protocolConfig) {
errorCode = ErrorCode::InternalError;
return errorCode;
}
auto configurator = createConfigurator(protocol);
auto result = configurator->createConfig(credentials, container, protocolConfig, errorCode);
if (errorCode != ErrorCode::NoError || !result) {
protocolConfigString = configurator->createConfig(credentials, container, containerConfig, errorCode);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
configurator->processConfigWithExportSettings(dns, isApiConfig, result);
ProtocolConfigVariant variant = ProtocolConfig::getProtocolConfigVariant(result);
std::visit([&protocolConfigString](const auto &config) -> void {
protocolConfigString = config->clientProtocolConfig.nativeConfig;
}, variant);
protocolConfigString = configurator->processConfigWithExportSettings(dns, isApiConfig, protocolConfigString);
return errorCode;
}
QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QString, QString> &dns, const QSharedPointer<ServerConfig> &serverConfig,
const ContainerConfig &containerConfig, const DockerContainer container)
QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig,
const QJsonObject &containerConfig, const DockerContainer container,
ErrorCode &errorCode)
{
QJsonObject vpnConfiguration {};
@@ -142,47 +86,24 @@ QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QStr
return vpnConfiguration;
}
bool isApiConfig = static_cast<int>(serverConfig->type);
bool isApiConfig = serverConfig.value(config_key::configVersion).toInt();
for (ProtocolEnumNS::Proto proto : ContainerProps::protocolsForContainer(container)) {
if (isApiConfig && container == DockerContainer::Cloak && proto == ProtocolEnumNS::Proto::ShadowSocks) {
continue;
}
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 = "";
}
QString protocolConfigString =
containerConfig.value(ProtocolProps::protoToString(proto)).toObject().value(config_key::last_config).toString();
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();
}
}
}
}
auto configurator = createConfigurator(proto);
protocolConfigString = configurator->processConfigWithLocalSettings(dns, isApiConfig, protocolConfigString);
QJsonObject vpnConfigData = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
if (container == DockerContainer::Awg || container == DockerContainer::WireGuard) {
// add mtu for old configs
if (vpnConfigData[config_key::mtu].toString().isEmpty()) {
vpnConfigData[config_key::mtu] =
container == DockerContainer::Awg ? protocols::awg::defaultMtu : protocols::wireguard::defaultMtu;
vpnConfigData[config_key::mtu] = container == DockerContainer::Awg ? protocols::awg::defaultMtu : protocols::wireguard::defaultMtu;
}
}
@@ -195,32 +116,30 @@ QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QStr
vpnConfiguration[config_key::dns1] = dns.first;
vpnConfiguration[config_key::dns2] = dns.second;
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::hostName] = serverConfig.value(config_key::hostName).toString();
vpnConfiguration[config_key::description] = serverConfig.value(config_key::description).toString();
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, ContainerConfig &containerConfig,
const QString &stdOut)
void VpnConfigurationsController::updateContainerConfigAfterInstallation(const DockerContainer container, QJsonObject &containerConfig,
const QString &stdOut)
{
Proto mainProto = ContainerProps::defaultProtocol(container);
if (container == DockerContainer::TorWebSite) {
QString protocolName = ProtocolProps::protoToString(mainProto);
auto protocolConfig = containerConfig.protocolConfigs.value(protocolName);
QJsonObject protocol = containerConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
qDebug() << "amnezia-tor onions" << stdOut;
QString onion = stdOut;
onion.replace("\n", "");
if (auto torConfig = qSharedPointerCast<TorWebsiteProtocolConfig>(protocolConfig)) {
torConfig->serverProtocolConfig.site = onion;
}
protocol.insert(config_key::site, onion);
containerConfig.insert(ProtocolProps::protoToString(mainProto), protocol);
}
}

View File

@@ -4,35 +4,28 @@
#include <QObject>
#include "configurators/configurator_base.h"
#include "core/models/containers/containers_defs.h"
#include "core/models/containers/containerConfig.h"
#include "containers/containers_defs.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,
ContainerConfig &containerConfig);
QJsonObject &containerConfig);
ErrorCode createProtocolConfigString(const bool isApiConfig, const QPair<QString, QString> &dns, const ServerCredentials &credentials,
const DockerContainer container, const ContainerConfig &containerConfig, const Proto protocol,
const DockerContainer container, const QJsonObject &containerConfig, const Proto protocol,
QString &protocolConfigString);
QJsonObject createVpnConfiguration(const QPair<QString, QString> &dns, const QSharedPointer<ServerConfig> &serverConfig,
const ContainerConfig &containerConfig, const DockerContainer container);
QJsonObject createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig,
const QJsonObject &containerConfig, const DockerContainer container, ErrorCode &errorCode);
static void updateContainerConfigAfterInstallation(const DockerContainer container, ContainerConfig &containerConfig, const QString &stdOut);
static void updateContainerConfigAfterInstallation(const DockerContainer container, QJsonObject &containerConfig, const QString &stdOut);
signals:
public:
QSharedPointer<ProtocolConfig> createProtocolConfig(const Proto protocol);
private:
QScopedPointer<ConfiguratorBase> createConfigurator(const Proto protocol);

View File

@@ -6,6 +6,9 @@
namespace amnezia
{
constexpr const qint16 qrMagicCode = 1984;
struct ServerCredentials
{
QString hostName;
@@ -19,13 +22,6 @@ namespace amnezia
}
};
enum ServerConfigType
{
SelfHosted,
ApiV1,
ApiV2
};
struct InstalledAppInfo {
QString appName;
QString packageName;
@@ -51,7 +47,6 @@ namespace amnezia
InternalError = 101,
NotImplementedError = 102,
AmneziaServiceNotRunning = 103,
NotSupportedOnThisPlatform = 104,
// Server errors
ServerCheckFailed = 200,
@@ -61,13 +56,6 @@ 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,
@@ -109,7 +97,6 @@ namespace amnezia
// import and install errors
ImportInvalidConfigError = 900,
ImportOpenConfigError = 901,
NoInstalledContainersError = 902,
// Android errors
AndroidError = 1000,
@@ -123,10 +110,6 @@ namespace amnezia
ApiMissingAgwPublicKey = 1105,
ApiConfigDecryptionError = 1106,
ApiServicesMissingError = 1107,
ApiConfigLimitError = 1108,
ApiNotFoundError = 1109,
ApiMigrationError = 1110,
ApiUpdateRequestError = 1111,
// QFile errors
OpenError = 1200,

View File

@@ -0,0 +1,9 @@
#ifndef APIENUMS_H
#define APIENUMS_H
enum ApiConfigSources {
Telegram = 1,
AmneziaGateway
};
#endif // APIENUMS_H

View File

@@ -12,7 +12,6 @@ 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;
@@ -20,15 +19,8 @@ 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 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;
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;
// Libssh errors
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
@@ -59,7 +51,6 @@ 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;
@@ -73,10 +64,6 @@ QString errorString(ErrorCode code) {
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;

View File

@@ -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,15 +25,13 @@ 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;
}
@@ -44,28 +42,15 @@ 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!";
}
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.reset(Instance()->m_ClientNode.acquire<IpcProcessTun2SocksReplica>());
Instance()->m_Tun2SocksClient->waitForSource(1000);
if (!Instance()->m_Tun2SocksClient->isReplicaValid()) {
@@ -73,8 +58,9 @@ 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();
@@ -91,7 +77,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;
}
@@ -114,15 +100,18 @@ 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();
@@ -130,3 +119,5 @@ QSharedPointer<PrivilegedProcess> IpcClient::CreatePrivilegedProcess()
auto processReplica = QSharedPointer<PrivilegedProcess>(pd->ipcProcess);
return processReplica;
}

View File

@@ -1,76 +0,0 @@
#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);
}

View File

@@ -1,34 +0,0 @@
#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

View File

@@ -1,5 +0,0 @@
#include "containerConfig.h"
ContainerConfig::ContainerConfig()
{
}

View File

@@ -1,21 +0,0 @@
#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

View File

@@ -1,256 +0,0 @@
#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();
}

View File

@@ -1,76 +0,0 @@
#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

View File

@@ -1,58 +0,0 @@
#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();
}

View File

@@ -1,52 +0,0 @@
#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

View File

@@ -1,32 +0,0 @@
#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();
}

View File

@@ -1,45 +0,0 @@
#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

View File

@@ -1,99 +0,0 @@
#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();
}

View File

@@ -1,49 +0,0 @@
#ifndef OPENVPNPROTOCOLCONFIG_H
#define OPENVPNPROTOCOLCONFIG_H
#include <QJsonObject>
#include <QString>
#include "protocolConfig.h"
namespace openvpn
{
struct ServerProtocolConfig
{
QString subnetAddress;
QString transportProto;
QString port;
bool ncpDisable;
QString hash;
QString cipher;
bool tlsAuth;
bool blockOutsideDns;
QString additionalClientConfig;
QString additionalServerConfig;
};
struct ClientProtocolConfig
{
bool isEmpty = true;
QString clientId;
QString nativeConfig;
};
}
class OpenVpnProtocolConfig : public ProtocolConfig
{
public:
OpenVpnProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName);
QJsonObject toJson() const override;
bool hasEqualServerSettings(const OpenVpnProtocolConfig &other) const;
void clearClientSettings();
openvpn::ServerProtocolConfig serverProtocolConfig;
openvpn::ClientProtocolConfig clientProtocolConfig;
};
#endif // OPENVPNPROTOCOLCONFIG_H

View File

@@ -1,76 +0,0 @@
#include "protocolConfig.h"
#include "core/models/protocols/awgProtocolConfig.h"
#include "core/models/protocols/cloakProtocolConfig.h"
#include "core/models/protocols/openvpnProtocolConfig.h"
#include "core/models/protocols/protocolConfig.h"
#include "core/models/protocols/shadowsocksProtocolConfig.h"
#include "core/models/protocols/wireguardProtocolConfig.h"
#include "core/models/protocols/xrayProtocolConfig.h"
#include "protocols/protocols_defs.h"
using namespace amnezia;
ProtocolConfig::ProtocolConfig(const QString &protocolName) : protocolName(protocolName)
{
}
ProtocolConfigVariant ProtocolConfig::getProtocolConfigVariant(const QSharedPointer<ProtocolConfig> &protocolConfig)
{
ProtocolConfigVariant variant;
auto proto = ProtocolProps::protoFromString(protocolConfig->protocolName);
switch (proto) {
case Proto::OpenVpn: variant = qSharedPointerCast<OpenVpnProtocolConfig>(protocolConfig); break;
case Proto::WireGuard: variant = qSharedPointerCast<WireGuardProtocolConfig>(protocolConfig); break;
case Proto::ShadowSocks: variant = qSharedPointerCast<ShadowsocksProtocolConfig>(protocolConfig); break;
case Proto::Cloak: variant = qSharedPointerCast<CloakProtocolConfig>(protocolConfig); break;
case Proto::Xray: variant = qSharedPointerCast<XrayProtocolConfig>(protocolConfig); break;
case Proto::Awg: variant = qSharedPointerCast<AwgProtocolConfig>(protocolConfig); break;
default: break;
}
return variant;
}
bool ProtocolConfig::isServerSettingsEqual(const QSharedPointer<ProtocolConfig> &other)
{
auto proto = ProtocolProps::protoFromString(protocolName);
switch (proto) {
case Proto::OpenVpn: {
auto thisConfig = qSharedPointerCast<OpenVpnProtocolConfig>(QSharedPointer<ProtocolConfig>(this));
auto otherConfig = qSharedPointerCast<OpenVpnProtocolConfig>(other);
return thisConfig->hasEqualServerSettings(*otherConfig.data());
}
case Proto::WireGuard: {
auto thisConfig = qSharedPointerCast<WireGuardProtocolConfig>(QSharedPointer<ProtocolConfig>(this));
auto otherConfig = qSharedPointerCast<WireGuardProtocolConfig>(other);
return thisConfig->hasEqualServerSettings(*otherConfig.data());
}
case Proto::ShadowSocks: {
auto thisConfig = qSharedPointerCast<ShadowsocksProtocolConfig>(QSharedPointer<ProtocolConfig>(this));
auto otherConfig = qSharedPointerCast<ShadowsocksProtocolConfig>(other);
return thisConfig->hasEqualServerSettings(*otherConfig.data());
}
case Proto::Cloak: {
auto thisConfig = qSharedPointerCast<CloakProtocolConfig>(QSharedPointer<ProtocolConfig>(this));
auto otherConfig = qSharedPointerCast<CloakProtocolConfig>(other);
return thisConfig->hasEqualServerSettings(*otherConfig.data());
}
case Proto::Xray: {
auto thisConfig = qSharedPointerCast<XrayProtocolConfig>(QSharedPointer<ProtocolConfig>(this));
auto otherConfig = qSharedPointerCast<XrayProtocolConfig>(other);
return thisConfig->hasEqualServerSettings(*otherConfig.data());
}
case Proto::Awg: {
auto thisConfig = qSharedPointerCast<AwgProtocolConfig>(QSharedPointer<ProtocolConfig>(this));
auto otherConfig = qSharedPointerCast<AwgProtocolConfig>(other);
return thisConfig->hasEqualServerSettings(*otherConfig.data());
}
default: return false;
}
}
QJsonObject ProtocolConfig::toJson() const
{
return QJsonObject();
}

View File

@@ -1,37 +0,0 @@
#ifndef PROTOCOLCONFIG_H
#define PROTOCOLCONFIG_H
#include <QJsonObject>
#include <QSharedPointer>
#include <variant>
class OpenVpnProtocolConfig;
class WireGuardProtocolConfig;
class ShadowsocksProtocolConfig;
class CloakProtocolConfig;
class XrayProtocolConfig;
class AwgProtocolConfig;
class SftpProtocolConfig;
class Socks5ProtocolConfig;
class Ikev2ProtocolConfig;
class TorWebsiteProtocolConfig;
using ProtocolConfigVariant =
std::variant<QSharedPointer<OpenVpnProtocolConfig>, QSharedPointer<WireGuardProtocolConfig>, QSharedPointer<ShadowsocksProtocolConfig>,
QSharedPointer<CloakProtocolConfig>, QSharedPointer<XrayProtocolConfig>, QSharedPointer<AwgProtocolConfig>,
QSharedPointer<SftpProtocolConfig>, QSharedPointer<Socks5ProtocolConfig>, QSharedPointer<Ikev2ProtocolConfig>,
QSharedPointer<TorWebsiteProtocolConfig> >;
class ProtocolConfig
{
public:
ProtocolConfig(const QString &protocolName);
virtual QJsonObject toJson() const;
static ProtocolConfigVariant getProtocolConfigVariant(const QSharedPointer<ProtocolConfig> &protocolConfig);
bool isServerSettingsEqual(const QSharedPointer<ProtocolConfig> &protocolConfig);
QString protocolName;
};
#endif // PROTOCOLCONFIG_H

View File

@@ -1,48 +0,0 @@
#include "sftpProtocolConfig.h"
#include <QJsonArray>
#include <QJsonDocument>
#include "protocols/protocols_defs.h"
using namespace amnezia;
SftpProtocolConfig::SftpProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName) : ProtocolConfig(protocolName)
{
serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(QString::number(ProtocolProps::defaultPort(Proto::Sftp)));
serverProtocolConfig.userName = protocolConfigObject.value(config_key::userName).toString(protocols::sftp::defaultUserName);
serverProtocolConfig.password = protocolConfigObject.value(config_key::password).toString();
auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString();
if (!clientProtocolString.isEmpty()) {
clientProtocolConfig.isEmpty = false;
}
}
QJsonObject SftpProtocolConfig::toJson() const
{
QJsonObject json;
if (!serverProtocolConfig.port.isEmpty()) {
json[config_key::port] = serverProtocolConfig.port;
}
if (!serverProtocolConfig.userName.isEmpty()) {
json[config_key::userName] = serverProtocolConfig.userName;
}
if (!serverProtocolConfig.password.isEmpty()) {
json[config_key::password] = serverProtocolConfig.password;
}
return json;
}
bool SftpProtocolConfig::hasEqualServerSettings(const SftpProtocolConfig &other) const
{
return serverProtocolConfig.port == other.serverProtocolConfig.port &&
serverProtocolConfig.userName == other.serverProtocolConfig.userName &&
serverProtocolConfig.password == other.serverProtocolConfig.password;
}
void SftpProtocolConfig::clearClientSettings()
{
clientProtocolConfig.isEmpty = true;
}

View File

@@ -1,38 +0,0 @@
#ifndef SFTPPROTOCOLCONFIG_H
#define SFTPPROTOCOLCONFIG_H
#include <QJsonObject>
#include <QString>
#include "protocolConfig.h"
namespace sftp
{
struct ServerProtocolConfig
{
QString port;
QString userName;
QString password;
};
struct ClientProtocolConfig
{
bool isEmpty = true;
};
}
class SftpProtocolConfig : public ProtocolConfig
{
public:
SftpProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName);
QJsonObject toJson() const override;
bool hasEqualServerSettings(const SftpProtocolConfig &other) const;
void clearClientSettings();
sftp::ServerProtocolConfig serverProtocolConfig;
sftp::ClientProtocolConfig clientProtocolConfig;
};
#endif // SFTPPROTOCOLCONFIG_H

View File

@@ -1,53 +0,0 @@
#include "shadowsocksProtocolConfig.h"
#include <QJsonArray>
#include <QJsonDocument>
#include "protocols/protocols_defs.h"
using namespace amnezia;
ShadowsocksProtocolConfig::ShadowsocksProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName) : ProtocolConfig(protocolName)
{
serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(protocols::shadowsocks::defaultPort);
serverProtocolConfig.cipher = protocolConfigObject.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher);
auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString();
if (!clientProtocolString.isEmpty()) {
clientProtocolConfig.isEmpty = false;
QJsonObject clientProtocolConfigObject = QJsonDocument::fromJson(clientProtocolString.toUtf8()).object();
}
}
QJsonObject ShadowsocksProtocolConfig::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 (!clientProtocolConfig.isEmpty) {
QJsonObject clientConfigJson;
json[config_key::last_config] = QString(QJsonDocument(clientConfigJson).toJson());
}
return json;
}
bool ShadowsocksProtocolConfig::hasEqualServerSettings(const ShadowsocksProtocolConfig &other) const
{
if (serverProtocolConfig.port != other.serverProtocolConfig.port ||
serverProtocolConfig.cipher != other.serverProtocolConfig.cipher) {
return false;
}
return true;
}
void ShadowsocksProtocolConfig::clearClientSettings()
{
clientProtocolConfig = shadowsocks::ClientProtocolConfig();
}

View File

@@ -1,46 +0,0 @@
#ifndef SHADOWSOCKSPROTOCOLCONFIG_H
#define SHADOWSOCKSPROTOCOLCONFIG_H
#include <QJsonObject>
#include <QString>
#include "protocolConfig.h"
namespace shadowsocks
{
struct ServerProtocolConfig
{
QString port;
QString cipher;
};
struct ClientProtocolConfig
{
bool isEmpty = true;
QString server;
QString serverPort;
QString localPort;
QString password;
int timeout = 60;
QString method;
QString nativeConfig;
};
}
class ShadowsocksProtocolConfig : public ProtocolConfig
{
public:
ShadowsocksProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName);
QJsonObject toJson() const override;
bool hasEqualServerSettings(const ShadowsocksProtocolConfig &other) const;
void clearClientSettings();
shadowsocks::ServerProtocolConfig serverProtocolConfig;
shadowsocks::ClientProtocolConfig clientProtocolConfig;
};
#endif // SHADOWSOCKSPROTOCOLCONFIG_H

View File

@@ -1,48 +0,0 @@
#include "socks5ProtocolConfig.h"
#include <QJsonArray>
#include <QJsonDocument>
#include "protocols/protocols_defs.h"
using namespace amnezia;
Socks5ProtocolConfig::Socks5ProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName) : ProtocolConfig(protocolName)
{
serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(protocols::socks5Proxy::defaultPort);
serverProtocolConfig.userName = protocolConfigObject.value(config_key::userName).toString(protocols::socks5Proxy::defaultUserName);
serverProtocolConfig.password = protocolConfigObject.value(config_key::password).toString();
auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString();
if (!clientProtocolString.isEmpty()) {
clientProtocolConfig.isEmpty = false;
}
}
QJsonObject Socks5ProtocolConfig::toJson() const
{
QJsonObject json;
if (!serverProtocolConfig.port.isEmpty()) {
json[config_key::port] = serverProtocolConfig.port;
}
if (!serverProtocolConfig.userName.isEmpty()) {
json[config_key::userName] = serverProtocolConfig.userName;
}
if (!serverProtocolConfig.password.isEmpty()) {
json[config_key::password] = serverProtocolConfig.password;
}
return json;
}
bool Socks5ProtocolConfig::hasEqualServerSettings(const Socks5ProtocolConfig &other) const
{
return serverProtocolConfig.port == other.serverProtocolConfig.port &&
serverProtocolConfig.userName == other.serverProtocolConfig.userName &&
serverProtocolConfig.password == other.serverProtocolConfig.password;
}
void Socks5ProtocolConfig::clearClientSettings()
{
clientProtocolConfig.isEmpty = true;
}

View File

@@ -1,38 +0,0 @@
#ifndef SOCKS5PROTOCOLCONFIG_H
#define SOCKS5PROTOCOLCONFIG_H
#include <QJsonObject>
#include <QString>
#include "protocolConfig.h"
namespace socks5
{
struct ServerProtocolConfig
{
QString port;
QString userName;
QString password;
};
struct ClientProtocolConfig
{
bool isEmpty = true;
};
}
class Socks5ProtocolConfig : public ProtocolConfig
{
public:
Socks5ProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName);
QJsonObject toJson() const override;
bool hasEqualServerSettings(const Socks5ProtocolConfig &other) const;
void clearClientSettings();
socks5::ServerProtocolConfig serverProtocolConfig;
socks5::ClientProtocolConfig clientProtocolConfig;
};
#endif // SOCKS5PROTOCOLCONFIG_H

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