mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-23 02:00:20 +07:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a53eb556b7 | |||
| fd915a4325 | |||
| 970fc26113 | |||
| c7b7ecd1d6 | |||
| 0edffc5f88 | |||
| dcb097b0b2 | |||
| a78f42a199 |
@@ -1,38 +0,0 @@
|
|||||||
# .github/actions/apple-install-cert/action.yml
|
|
||||||
|
|
||||||
name: Setup apple keychain
|
|
||||||
description: Creates and configures a temporary build keychain
|
|
||||||
|
|
||||||
inputs:
|
|
||||||
keychain-path:
|
|
||||||
description: Path to the keychain
|
|
||||||
required: true
|
|
||||||
keychain-password:
|
|
||||||
description: Password to the keychain
|
|
||||||
required: true
|
|
||||||
cert-base64:
|
|
||||||
description: Base64-encoded certificate
|
|
||||||
required: true
|
|
||||||
cert-password:
|
|
||||||
description: Certificate password
|
|
||||||
required: true
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: composite
|
|
||||||
steps:
|
|
||||||
- name: Create keychain
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
KEYCHAIN_PATH: ${{ inputs.keychain-path }}
|
|
||||||
KEYCHAIN_PASSWORD: ${{ inputs.keychain-password }}
|
|
||||||
CERT_BASE64: ${{ inputs.cert-base64 }}
|
|
||||||
CERT_PASSWORD: ${{ inputs.cert-password }}
|
|
||||||
run: |
|
|
||||||
CERT_PATH=$(mktemp /tmp/cert_XXXXXX.p12)
|
|
||||||
trap "rm -f '$CERT_PATH'" EXIT
|
|
||||||
|
|
||||||
echo -n "$CERT_BASE64" | base64 --decode -o "$CERT_PATH"
|
|
||||||
|
|
||||||
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
|
||||||
security import "$CERT_PATH" -k "$KEYCHAIN_PATH" -P "$CERT_PASSWORD" -A -t cert -f pkcs12
|
|
||||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
|
||||||
Binary file not shown.
@@ -1,57 +0,0 @@
|
|||||||
# .github/actions/apple-setup-keychain/action.yml
|
|
||||||
|
|
||||||
name: Setup apple keychain
|
|
||||||
description: Creates and configures a temporary build keychain
|
|
||||||
|
|
||||||
inputs:
|
|
||||||
keychain-name:
|
|
||||||
description: Name of the keychain
|
|
||||||
required: false
|
|
||||||
default: "ci-amnezia"
|
|
||||||
keychain-password:
|
|
||||||
description: The keychain password
|
|
||||||
required: true
|
|
||||||
lock-timeout:
|
|
||||||
description: A timeout after exceeding which the keychain would be locked
|
|
||||||
required: false
|
|
||||||
default: "0"
|
|
||||||
|
|
||||||
outputs:
|
|
||||||
keychain-path:
|
|
||||||
description: "Full path to the keychain created"
|
|
||||||
value: ${{ steps.setup.outputs.keychain-path }}
|
|
||||||
keychain-name:
|
|
||||||
description: "Actual name of the keychain created"
|
|
||||||
value: ${{ steps.setup.outputs.keychain-name }}
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: composite
|
|
||||||
steps:
|
|
||||||
- name: Setup keychain
|
|
||||||
id: setup
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
KEYCHAIN_NAME: ${{ inputs.keychain-name }}
|
|
||||||
KEYCHAIN_PASSWORD: ${{ inputs.keychain-password }}
|
|
||||||
LOCK_TIMEOUT: ${{ inputs.lock-timeout }}
|
|
||||||
run: |
|
|
||||||
KEYCHAIN_PATH="$HOME/Library/Keychains/$KEYCHAIN_NAME.keychain-db"
|
|
||||||
|
|
||||||
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
|
||||||
|
|
||||||
if [[ "$LOCK_TIMEOUT" == "0" ]]; then
|
|
||||||
security set-keychain-settings "$KEYCHAIN_PATH"
|
|
||||||
else
|
|
||||||
security set-keychain-settings -u -t "$LOCK_TIMEOUT" "$KEYCHAIN_PATH"
|
|
||||||
fi
|
|
||||||
|
|
||||||
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
|
||||||
|
|
||||||
security import "${{ github.action_path }}/DeveloperIDG2CA.cer" -k "$KEYCHAIN_PATH" -A
|
|
||||||
security import "${{ github.action_path }}/AppleWWDRCAG3.cer" -k "$KEYCHAIN_PATH" -A
|
|
||||||
|
|
||||||
security list-keychains -d user -s "$KEYCHAIN_PATH"
|
|
||||||
security default-keychain -s "$KEYCHAIN_PATH"
|
|
||||||
|
|
||||||
echo "keychain-name=$KEYCHAIN_NAME" >> $GITHUB_OUTPUT
|
|
||||||
echo "keychain-path=$KEYCHAIN_PATH" >> $GITHUB_OUTPUT
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# .github/actions/apple-setup-provisioning-profile/action.yml
|
|
||||||
|
|
||||||
name: Setup provisioning profiles
|
|
||||||
description: Decodes and installs provisioning profiles
|
|
||||||
|
|
||||||
inputs:
|
|
||||||
provisioning_profile_base64:
|
|
||||||
description: Base64-encoded provisioning profile
|
|
||||||
required: true
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: composite
|
|
||||||
steps:
|
|
||||||
- name: Setup provisioning profile
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
PROFILES_DIR="$HOME/Library/MobileDevice/Provisioning Profiles"
|
|
||||||
TEMP_FILE=$(mktemp)
|
|
||||||
|
|
||||||
echo "${{ inputs.provisioning_profile_base64 }}" | base64 --decode > "$TEMP_FILE"
|
|
||||||
|
|
||||||
PROFILE_UUID=$(grep UUID -A1 -a "$TEMP_FILE" | grep -io "[-A-F0-9]\{36\}")
|
|
||||||
if [[ -z "$PROFILE_UUID" ]]; then
|
|
||||||
echo "Failed to extract UUID from provisioning profile"
|
|
||||||
rm -f "$TEMP_FILE"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p "$PROFILES_DIR"
|
|
||||||
mv "$TEMP_FILE" "$PROFILES_DIR/$PROFILE_UUID.mobileprovision"
|
|
||||||
echo "Installed profile: $PROFILE_UUID"
|
|
||||||
+293
-470
@@ -9,61 +9,9 @@ env:
|
|||||||
QT_MIRROR: https://mirrors.ocf.berkeley.edu/qt/ # https://download.qt.io/static/mirrorlist/
|
QT_MIRROR: https://mirrors.ocf.berkeley.edu/qt/ # https://download.qt.io/static/mirrorlist/
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Detect-Changes:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
recipes_changed: ${{ steps.filter.outputs.recipes }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: dorny/paths-filter@v3
|
|
||||||
id: filter
|
|
||||||
with:
|
|
||||||
filters: |
|
|
||||||
recipes:
|
|
||||||
- 'recipes/**'
|
|
||||||
- 'conanfile.py'
|
|
||||||
|
|
||||||
Bake-Prebuilts-Linux:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: Detect-Changes
|
|
||||||
if: needs.Detect-Changes.outputs.recipes_changed == 'true'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: 'true'
|
|
||||||
fetch-depth: 10
|
|
||||||
|
|
||||||
- uses: actions/setup-python@v6
|
|
||||||
with:
|
|
||||||
python-version: 3.14
|
|
||||||
|
|
||||||
- name: 'Install conan'
|
|
||||||
run: pip install "conan==2.28.0"
|
|
||||||
|
|
||||||
- name: 'Build dependencies'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
for build_type in Release Debug; do
|
|
||||||
cmake -S . -B build -DPREBUILTS_ONLY=1 "-DCMAKE_BUILD_TYPE=$build_type"
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: 'Authorize in remote'
|
|
||||||
if: github.ref == 'refs/heads/dev'
|
|
||||||
run: conan remote login amnezia "${{ secrets.CONAN_USER }}" -p "${{ secrets.CONAN_PASSWORD }}"
|
|
||||||
|
|
||||||
- name: 'Upload baked prebuilts'
|
|
||||||
if: github.ref == 'refs/heads/dev'
|
|
||||||
run: conan upload -r amnezia "*" -c
|
|
||||||
|
|
||||||
# ------------------------------------------------------
|
|
||||||
|
|
||||||
Build-Linux-Ubuntu:
|
Build-Linux-Ubuntu:
|
||||||
runs-on: android-runner
|
runs-on: android-runner
|
||||||
|
|
||||||
needs: Bake-Prebuilts-Linux
|
|
||||||
if: ${{ always() }}
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
QT_VERSION: 6.10.1
|
QT_VERSION: 6.10.1
|
||||||
QIF_VERSION: 4.7
|
QIF_VERSION: 4.7
|
||||||
@@ -93,34 +41,44 @@ jobs:
|
|||||||
py7zrversion: '==0.22.*'
|
py7zrversion: '==0.22.*'
|
||||||
extra: '--base ${{ env.QT_MIRROR }}'
|
extra: '--base ${{ env.QT_MIRROR }}'
|
||||||
|
|
||||||
- name: 'Setup python'
|
|
||||||
uses: actions/setup-python@v6
|
|
||||||
with:
|
|
||||||
python-version: 3.14
|
|
||||||
|
|
||||||
- name: 'Install conan'
|
|
||||||
run: pip install "conan==2.28.0"
|
|
||||||
|
|
||||||
- name: 'Install system packages'
|
|
||||||
run: sudo apt-get install libxkbcommon-x11-0 libsecret-1-dev
|
|
||||||
|
|
||||||
- name: 'Get sources'
|
- name: 'Get sources'
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
fetch-depth: 10
|
fetch-depth: 10
|
||||||
|
|
||||||
|
- name: 'Get version from CMakeLists.txt'
|
||||||
|
id: get_version
|
||||||
|
run: |
|
||||||
|
VERSION=$(grep 'set(AMNEZIAVPN_VERSION' CMakeLists.txt | sed -E 's/.*AMNEZIAVPN_VERSION ([0-9]+.[0-9]+.[0-9]+.[0-9]+)\)/\1/')
|
||||||
|
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||||
|
echo "Version: $VERSION"
|
||||||
|
|
||||||
|
# - name: 'Setup ccache'
|
||||||
|
# uses: hendrikmuhs/ccache-action@v1.2
|
||||||
|
|
||||||
- name: 'Build project'
|
- name: 'Build project'
|
||||||
shell: bash
|
run: |
|
||||||
env:
|
sudo apt-get install libxkbcommon-x11-0 libsecret-1-dev
|
||||||
QT_INSTALL_DIR: ${{ runner.temp }}
|
export QT_BIN_DIR=${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64/bin
|
||||||
run: deploy/build.sh --generator Ninja --installer all
|
export QIF_BIN_DIR=${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin
|
||||||
|
bash deploy/build_linux.sh
|
||||||
|
|
||||||
|
- name: 'Pack installer'
|
||||||
|
run: cd deploy && tar -cf AmneziaVPN_Linux_Installer.tar AmneziaVPN_Linux_Installer.bin && zip AmneziaVPN_${VERSION}_linux_x64.tar.zip AmneziaVPN_Linux_Installer.tar
|
||||||
|
|
||||||
- name: 'Upload installer artifact'
|
- name: 'Upload installer artifact'
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: deploy/build/AmneziaVPN_*_linux_x64.run
|
name: AmneziaVPN_${{ env.VERSION }}_linux_x64.tar.zip
|
||||||
archive: false
|
path: deploy/AmneziaVPN_${{ env.VERSION }}_linux_x64.tar.zip
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
- name: 'Upload unpacked artifact'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: AmneziaVPN_Linux_unpacked
|
||||||
|
path: deploy/AppDir
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
- name: 'Upload translations artifact'
|
- name: 'Upload translations artifact'
|
||||||
@@ -130,50 +88,15 @@ jobs:
|
|||||||
path: client/translations
|
path: client/translations
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
# ------------------------------------------------------
|
|
||||||
|
|
||||||
Bake-Prebuilts-Windows:
|
|
||||||
runs-on: windows-latest
|
|
||||||
needs: Detect-Changes
|
|
||||||
if: needs.Detect-Changes.outputs.recipes_changed == 'true'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: 'true'
|
|
||||||
fetch-depth: 10
|
|
||||||
|
|
||||||
- uses: actions/setup-python@v6
|
|
||||||
with:
|
|
||||||
python-version: 3.14
|
|
||||||
|
|
||||||
- uses: ilammy/msvc-dev-cmd@v1
|
|
||||||
|
|
||||||
- name: 'Install conan'
|
|
||||||
run: pip install "conan==2.28.0"
|
|
||||||
|
|
||||||
- name: 'Build dependencies'
|
|
||||||
run: cmake -S . -B build -G "Visual Studio 17 2022" -DPREBUILTS_ONLY=1
|
|
||||||
|
|
||||||
- name: 'Authorize in remote'
|
|
||||||
if: github.ref == 'refs/heads/dev'
|
|
||||||
run: conan remote login amnezia "${{ secrets.CONAN_USER }}" -p "${{ secrets.CONAN_PASSWORD }}"
|
|
||||||
|
|
||||||
- name: 'Upload baked prebuilts'
|
|
||||||
if: github.ref == 'refs/heads/dev'
|
|
||||||
run: conan upload -r amnezia "*" -c
|
|
||||||
|
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
Build-Windows:
|
Build-Windows:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
|
||||||
needs: Bake-Prebuilts-Windows
|
|
||||||
if: ${{ always() }}
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
QT_VERSION: 6.10.1
|
QT_VERSION: 6.10.1
|
||||||
QIF_VERSION: 4.7
|
QIF_VERSION: 4.7
|
||||||
|
BUILD_ARCH: 64
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||||
FALLBACK_S3_ENDPOINT: ${{ secrets.FALLBACK_S3_ENDPOINT }}
|
FALLBACK_S3_ENDPOINT: ${{ secrets.FALLBACK_S3_ENDPOINT }}
|
||||||
@@ -190,6 +113,17 @@ jobs:
|
|||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
fetch-depth: 10
|
fetch-depth: 10
|
||||||
|
|
||||||
|
- name: 'Get version from CMakeLists.txt'
|
||||||
|
id: get_version
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
VERSION=$(grep 'set(AMNEZIAVPN_VERSION' CMakeLists.txt | sed -E 's/.*AMNEZIAVPN_VERSION ([0-9]+.[0-9]+.[0-9]+.[0-9]+)\)/\1/')
|
||||||
|
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||||
|
echo "Version: $VERSION"
|
||||||
|
|
||||||
|
# - name: 'Setup ccache'
|
||||||
|
# uses: hendrikmuhs/ccache-action@v1.2
|
||||||
|
|
||||||
- name: 'Install Qt'
|
- name: 'Install Qt'
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v3
|
||||||
with:
|
with:
|
||||||
@@ -226,91 +160,50 @@ jobs:
|
|||||||
$wixBinDir = Join-Path $env:USERPROFILE ".dotnet\tools"
|
$wixBinDir = Join-Path $env:USERPROFILE ".dotnet\tools"
|
||||||
echo "WIX_BIN_DIR=$wixBinDir" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
echo "WIX_BIN_DIR=$wixBinDir" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||||
|
|
||||||
- name: 'Setup python'
|
|
||||||
uses: actions/setup-python@v6
|
|
||||||
with:
|
|
||||||
python-version: 3.14
|
|
||||||
|
|
||||||
- name: 'Install conan'
|
|
||||||
run: pip install "conan==2.28.0"
|
|
||||||
|
|
||||||
- name: 'Build project'
|
- name: 'Build project'
|
||||||
shell: cmd
|
shell: cmd
|
||||||
env:
|
|
||||||
QT_INSTALL_DIR: ${{ runner.temp }}
|
|
||||||
run: |
|
run: |
|
||||||
set WIX_ROOT_PATH="${{ env.USERPROFILE }}\.dotnet\tools"
|
set BUILD_ARCH=${{ env.BUILD_ARCH }}
|
||||||
deploy\build.bat --installer all
|
set QT_BIN_DIR="${{ runner.temp }}\\Qt\\${{ env.QT_VERSION }}\\msvc2022_64\\bin"
|
||||||
|
set QIF_BIN_DIR="${{ runner.temp }}\\Qt\\Tools\\QtInstallerFramework\\${{ env.QIF_VERSION }}\\bin"
|
||||||
|
set WIX_BIN_DIR=%USERPROFILE%\.dotnet\tools
|
||||||
|
call deploy\\build_windows.bat
|
||||||
|
|
||||||
- name: 'Upload WIX installer artifact'
|
- name: 'Rename Windows installer'
|
||||||
uses: actions/upload-artifact@v7
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
copy AmneziaVPN_x${{ env.BUILD_ARCH }}.exe AmneziaVPN_%VERSION%_x64.exe
|
||||||
|
|
||||||
|
- name: 'Upload installer artifact'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: deploy/build/AmneziaVPN_*_windows_x64.msi
|
name: AmneziaVPN_${{ env.VERSION }}_x64.exe
|
||||||
archive: false
|
path: AmneziaVPN_${{ env.VERSION }}_x64.exe
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
- name: 'Upload IFW installer artifact'
|
- name: 'Upload MSI installer artifact'
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: deploy/build/AmneziaVPN_*_windows_x64.exe
|
name: AmneziaVPN_Windows_MSI_installer
|
||||||
archive: false
|
path: AmneziaVPN_x${{ env.BUILD_ARCH }}.msi
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
# ------------------------------------------------------
|
- name: 'Upload unpacked artifact'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
Bake-Prebuilts-iOS:
|
|
||||||
needs: Detect-Changes
|
|
||||||
if: needs.Detect-Changes.outputs.recipes_changed == 'true'
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
xcode-version: [26.0, 26.4]
|
|
||||||
include:
|
|
||||||
- xcode-version: 26.4
|
|
||||||
os: macos-26
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os || 'macos-latest' }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
name: AmneziaVPN_Windows_unpacked
|
||||||
fetch-depth: 10
|
path: deploy\\build_${{ env.BUILD_ARCH }}\\client\\Release
|
||||||
|
retention-days: 7
|
||||||
- uses: actions/setup-python@v6
|
|
||||||
with:
|
|
||||||
python-version: 3.14
|
|
||||||
|
|
||||||
- uses: maxim-lobanov/setup-xcode@v1
|
|
||||||
with:
|
|
||||||
xcode-version: ${{ matrix.xcode-version }}
|
|
||||||
|
|
||||||
- name: 'Install conan'
|
|
||||||
run: pip install "conan==2.28.0"
|
|
||||||
|
|
||||||
- name: 'Build dependencies'
|
|
||||||
run: cmake -S . -B build -G Xcode -DPREBUILTS_ONLY=1 -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphoneos
|
|
||||||
|
|
||||||
- name: 'Authorize in remote'
|
|
||||||
if: github.ref == 'refs/heads/dev'
|
|
||||||
run: conan remote login amnezia "${{ secrets.CONAN_USER }}" -p "${{ secrets.CONAN_PASSWORD }}"
|
|
||||||
|
|
||||||
- name: 'Upload baked prebuilts'
|
|
||||||
if: github.ref == 'refs/heads/dev'
|
|
||||||
run: conan upload -r amnezia "*" -c
|
|
||||||
|
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
Build-iOS:
|
Build-iOS:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
|
||||||
needs: Bake-Prebuilts-iOS
|
|
||||||
if: ${{ always() }}
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
QT_VERSION: 6.10.1
|
QT_VERSION: 6.10.1
|
||||||
KEYCHAIN_PASSWORD: ""
|
CC: cc
|
||||||
|
CXX: c++
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||||
FALLBACK_S3_ENDPOINT: ${{ secrets.FALLBACK_S3_ENDPOINT }}
|
FALLBACK_S3_ENDPOINT: ${{ secrets.FALLBACK_S3_ENDPOINT }}
|
||||||
@@ -321,39 +214,10 @@ jobs:
|
|||||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Get sources'
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: 'true'
|
|
||||||
fetch-depth: 10
|
|
||||||
|
|
||||||
- uses: ./.github/actions/apple-setup-keychain
|
|
||||||
id: setup-keychain
|
|
||||||
with:
|
|
||||||
keychain-password: ${{ env.KEYCHAIN_PASSWORD }}
|
|
||||||
|
|
||||||
- name: 'Install signing certificate'
|
|
||||||
uses: ./.github/actions/apple-install-cert
|
|
||||||
with:
|
|
||||||
keychain-path: ${{ steps.setup-keychain.outputs.keychain-path }}
|
|
||||||
keychain-password: ${{ env.KEYCHAIN_PASSWORD }}
|
|
||||||
cert-base64: ${{ secrets.IOS_SIGNING_CERT_BASE64 }}
|
|
||||||
cert-password: ${{ secrets.IOS_SIGNING_CERT_PASSWORD }}
|
|
||||||
|
|
||||||
- name: 'Install app provisioning profile'
|
|
||||||
uses: ./.github/actions/apple-setup-provisioning-profile
|
|
||||||
with:
|
|
||||||
provisioning_profile_base64: ${{ secrets.IOS_APP_PROVISIONING_PROFILE }}
|
|
||||||
|
|
||||||
- name: 'Install NE provisioning profile'
|
|
||||||
uses: ./.github/actions/apple-setup-provisioning-profile
|
|
||||||
with:
|
|
||||||
provisioning_profile_base64: ${{ secrets.IOS_NE_PROVISIONING_PROFILE }}
|
|
||||||
|
|
||||||
- name: 'Setup xcode'
|
- name: 'Setup xcode'
|
||||||
uses: maxim-lobanov/setup-xcode@v1
|
uses: maxim-lobanov/setup-xcode@v1
|
||||||
with:
|
with:
|
||||||
xcode-version: '26.0'
|
xcode-version: '26.1'
|
||||||
|
|
||||||
- name: 'Install desktop Qt'
|
- name: 'Install desktop Qt'
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v3
|
||||||
@@ -379,81 +243,81 @@ jobs:
|
|||||||
set-env: 'true'
|
set-env: 'true'
|
||||||
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
|
|
||||||
- name: 'Setup python'
|
- name: 'Install go'
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
python-version: 3.14
|
go-version: '1.24'
|
||||||
|
cache: false
|
||||||
|
|
||||||
- name: 'Install deps'
|
- name: 'Setup gomobile'
|
||||||
run: pip install "conan==2.28.0" jsonschema jinja2
|
|
||||||
|
|
||||||
- name: 'Build project'
|
|
||||||
env:
|
|
||||||
QT_INSTALL_DIR: ${{ runner.temp }}
|
|
||||||
APPSTORE_CONNECT_KEY_ID: ${{ secrets.APPSTORE_CONNECT_KEY_ID }}
|
|
||||||
APPSTORE_CONNECT_ISSUER_ID: ${{ secrets.APPSTORE_CONNECT_ISSUER_ID }}
|
|
||||||
APPSTORE_CONNECT_PRIVATE_KEY: ${{ secrets.APPSTORE_CONNECT_PRIVATE_KEY }}
|
|
||||||
run: |
|
run: |
|
||||||
sh deploy/build.sh -t ios | \
|
export PATH=$PATH:~/go/bin
|
||||||
sed -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/d' | \
|
go install golang.org/x/mobile/cmd/gomobile@latest
|
||||||
sed -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/d' | \
|
gomobile init
|
||||||
sed -e '/-DPROD_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DPROD_AGW_PUBLIC_KEY/d' | \
|
|
||||||
sed -e '/-DDEV_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DDEV_AGW_PUBLIC_KEY/d'
|
|
||||||
|
|
||||||
# ------------------------------------------------------
|
- name: 'Get sources'
|
||||||
|
uses: actions/checkout@v4
|
||||||
Bake-Prebuilts-MacOS:
|
|
||||||
needs: Detect-Changes
|
|
||||||
if: needs.Detect-Changes.outputs.recipes_changed == 'true'
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
xcode-version: [16.2, 16.4, 26.4]
|
|
||||||
include:
|
|
||||||
- xcode-version: 26.4
|
|
||||||
os: macos-26
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os || 'macos-latest' }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
fetch-depth: 10
|
fetch-depth: 10
|
||||||
|
|
||||||
- uses: actions/setup-python@v6
|
# - name: 'Setup ccache'
|
||||||
with:
|
# uses: hendrikmuhs/ccache-action@v1.2
|
||||||
python-version: 3.14
|
|
||||||
|
|
||||||
- uses: maxim-lobanov/setup-xcode@v1
|
- name: 'Install dependencies'
|
||||||
with:
|
run: pip install jsonschema jinja2
|
||||||
xcode-version: ${{ matrix.xcode-version }}
|
|
||||||
|
|
||||||
- name: 'Install conan'
|
- name: 'Build project'
|
||||||
run: pip install "conan==2.28.0"
|
run: |
|
||||||
|
git submodule update --init --recursive
|
||||||
|
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/ios/bin"
|
||||||
|
export QT_MACOS_ROOT_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos"
|
||||||
|
export PATH=$PATH:~/go/bin
|
||||||
|
sh deploy/build_ios.sh | \
|
||||||
|
sed -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/d' | \
|
||||||
|
sed -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/d' | \
|
||||||
|
sed -e '/-DPROD_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DPROD_AGW_PUBLIC_KEY/d' | \
|
||||||
|
sed -e '/-DDEV_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DDEV_AGW_PUBLIC_KEY/d'
|
||||||
|
env:
|
||||||
|
IOS_TRUST_CERT_BASE64: ${{ secrets.IOS_TRUST_CERT_BASE64 }}
|
||||||
|
IOS_SIGNING_CERT_BASE64: ${{ secrets.IOS_SIGNING_CERT_BASE64 }}
|
||||||
|
IOS_SIGNING_CERT_PASSWORD: ${{ secrets.IOS_SIGNING_CERT_PASSWORD }}
|
||||||
|
APPSTORE_CONNECT_KEY_ID: ${{ secrets.APPSTORE_CONNECT_KEY_ID }}
|
||||||
|
APPSTORE_CONNECT_ISSUER_ID: ${{ secrets.APPSTORE_CONNECT_ISSUER_ID }}
|
||||||
|
APPSTORE_CONNECT_PRIVATE_KEY: ${{ secrets.APPSTORE_CONNECT_PRIVATE_KEY }}
|
||||||
|
IOS_APP_PROVISIONING_PROFILE: ${{ secrets.IOS_APP_PROVISIONING_PROFILE }}
|
||||||
|
IOS_NE_PROVISIONING_PROFILE: ${{ secrets.IOS_NE_PROVISIONING_PROFILE }}
|
||||||
|
|
||||||
- name: 'Build dependencies'
|
# - name: 'Upload appstore .ipa and dSYMs to artifacts'
|
||||||
run: cmake -S . -B build -G Xcode -DPREBUILTS_ONLY=1
|
# uses: actions/upload-artifact@v4
|
||||||
|
# with:
|
||||||
- name: 'Authorize in remote'
|
# name: app-store ipa & dsyms
|
||||||
if: github.ref == 'refs/heads/dev'
|
# path: |
|
||||||
run: conan remote login amnezia "${{ secrets.CONAN_USER }}" -p "${{ secrets.CONAN_PASSWORD }}"
|
# ${{ github.workspace }}/AmneziaVPN-iOS.ipa
|
||||||
|
# ${{ github.workspace }}/*.app.dSYM.zip
|
||||||
- name: 'Upload baked prebuilts'
|
# retention-days: 7
|
||||||
if: github.ref == 'refs/heads/dev'
|
|
||||||
run: conan upload -r amnezia "*" -c
|
|
||||||
|
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
Build-MacOS:
|
Build-MacOS-old:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
|
||||||
needs: Bake-Prebuilts-MacOS
|
|
||||||
if: ${{ always() }}
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
QT_VERSION: 6.10.1
|
# Keep compat with MacOS 10.15 aka Catalina by Qt 6.4
|
||||||
KEYCHAIN_PASSWORD: ""
|
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 }}
|
||||||
|
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||||
@@ -465,31 +329,84 @@ jobs:
|
|||||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
steps:
|
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'
|
- name: 'Get sources'
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
fetch-depth: 10
|
fetch-depth: 10
|
||||||
|
|
||||||
- uses: ./.github/actions/apple-setup-keychain
|
# - name: 'Setup ccache'
|
||||||
id: setup-keychain
|
# uses: hendrikmuhs/ccache-action@v1.2
|
||||||
with:
|
|
||||||
keychain-password: ${{ env.KEYCHAIN_PASSWORD }}
|
|
||||||
|
|
||||||
- uses: ./.github/actions/apple-install-cert
|
- name: 'Build project'
|
||||||
with:
|
run: |
|
||||||
keychain-path: ${{ steps.setup-keychain.outputs.keychain-path }}
|
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
||||||
keychain-password: ${{ env.KEYCHAIN_PASSWORD }}
|
bash deploy/build_macos.sh -n
|
||||||
cert-base64: ${{ secrets.MAC_APP_CERT_CERT }}
|
|
||||||
cert-password: ${{ secrets.MAC_APP_CERT_PW }}
|
|
||||||
|
|
||||||
- uses: ./.github/actions/apple-install-cert
|
- name: 'Upload installer artifact'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
keychain-path: ${{ steps.setup-keychain.outputs.keychain-path }}
|
name: AmneziaVPN_MacOS_old_installer
|
||||||
keychain-password: ${{ env.KEYCHAIN_PASSWORD }}
|
path: deploy/build/pkg/AmneziaVPN.pkg
|
||||||
cert-base64: ${{ secrets.MAC_INSTALLER_SIGNER_CERT }}
|
retention-days: 7
|
||||||
cert-password: ${{ secrets.MAC_INSTALL_CERT_PW }}
|
|
||||||
|
|
||||||
|
- 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.10.1
|
||||||
|
|
||||||
|
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 }}
|
||||||
|
FALLBACK_S3_ENDPOINT: ${{ secrets.FALLBACK_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'
|
- name: 'Setup xcode'
|
||||||
uses: maxim-lobanov/setup-xcode@v1
|
uses: maxim-lobanov/setup-xcode@v1
|
||||||
with:
|
with:
|
||||||
@@ -510,79 +427,50 @@ jobs:
|
|||||||
py7zrversion: '==0.22.*'
|
py7zrversion: '==0.22.*'
|
||||||
extra: '--base ${{ env.QT_MIRROR }}'
|
extra: '--base ${{ env.QT_MIRROR }}'
|
||||||
|
|
||||||
- name: 'Setup python'
|
- name: 'Get sources'
|
||||||
uses: actions/setup-python@v6
|
uses: actions/checkout@v4
|
||||||
with:
|
|
||||||
python-version: 3.14
|
|
||||||
|
|
||||||
- name: 'Install conan'
|
|
||||||
run: pip install "conan==2.28.0"
|
|
||||||
|
|
||||||
- name: 'Build project'
|
|
||||||
env:
|
|
||||||
QT_INSTALL_DIR: ${{ runner.temp }}
|
|
||||||
CODESIGN_SIGNATURE: ${{ secrets.MAC_SIGNER_ID }}
|
|
||||||
CODESIGN_INSTALLER_SIGNATURE: ${{ secrets.MAC_INSTALLER_SIGNER_ID }}
|
|
||||||
NOTARYTOOL_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
|
|
||||||
NOTARYTOOL_EMAIL: ${{ secrets.APPLE_DEV_EMAIL }}
|
|
||||||
NOTARYTOOL_PASSWORD: ${{ secrets.APPLE_DEV_PASSWORD }}
|
|
||||||
shell: bash
|
|
||||||
run: deploy/build.sh --generator Ninja --installer all
|
|
||||||
|
|
||||||
- name: 'Upload installer artifact'
|
|
||||||
uses: actions/upload-artifact@v7
|
|
||||||
with:
|
|
||||||
path: deploy/build/AmneziaVPN_*_macos_x64.pkg
|
|
||||||
archive: false
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
# ------------------------------------------------------
|
|
||||||
|
|
||||||
Bake-Prebuilts-MacOS-NE:
|
|
||||||
runs-on: macos-latest
|
|
||||||
needs: Detect-Changes
|
|
||||||
if: needs.Detect-Changes.outputs.recipes_changed == 'true'
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
xcode-version: [16.2, 16.4]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
fetch-depth: 10
|
fetch-depth: 10
|
||||||
|
|
||||||
- uses: actions/setup-python@v6
|
- name: 'Get version from CMakeLists.txt'
|
||||||
|
id: get_version
|
||||||
|
run: |
|
||||||
|
VERSION=$(grep 'set(AMNEZIAVPN_VERSION' CMakeLists.txt | sed -E 's/.*AMNEZIAVPN_VERSION ([0-9]+.[0-9]+.[0-9]+.[0-9]+)\)/\1/')
|
||||||
|
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||||
|
echo "Version: $VERSION"
|
||||||
|
|
||||||
|
# - name: 'Setup ccache'
|
||||||
|
# uses: hendrikmuhs/ccache-action@v1.2
|
||||||
|
|
||||||
|
- name: 'Build project'
|
||||||
|
run: |
|
||||||
|
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
||||||
|
bash deploy/build_macos.sh -n
|
||||||
|
|
||||||
|
- name: 'Pack macOS installer'
|
||||||
|
run: |
|
||||||
|
cd deploy/build/pkg
|
||||||
|
zip -r ../../AmneziaVPN_${VERSION}_macos.zip AmneziaVPN.pkg
|
||||||
|
cd ../../..
|
||||||
|
|
||||||
|
- name: 'Upload installer artifact'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3.14
|
name: AmneziaVPN_${{ env.VERSION }}_macos.zip
|
||||||
|
path: deploy/AmneziaVPN_${{ env.VERSION }}_macos.zip
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
- uses: maxim-lobanov/setup-xcode@v1
|
- name: 'Upload unpacked artifact'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
xcode-version: ${{ matrix.xcode-version }}
|
name: AmneziaVPN_MacOS_unpacked
|
||||||
|
path: deploy/build/client/AmneziaVPN.app
|
||||||
- name: 'Install conan'
|
retention-days: 7
|
||||||
run: pip install "conan==2.28.0"
|
|
||||||
|
|
||||||
- name: 'Build dependencies'
|
|
||||||
run: cmake -S . -B build -G Xcode -DPREBUILTS_ONLY=1 -DMACOS_NE=TRUE
|
|
||||||
|
|
||||||
- name: 'Authorize in remote'
|
|
||||||
if: github.ref == 'refs/heads/dev'
|
|
||||||
run: conan remote login amnezia "${{ secrets.CONAN_USER }}" -p "${{ secrets.CONAN_PASSWORD }}"
|
|
||||||
|
|
||||||
- name: 'Upload baked prebuilts'
|
|
||||||
if: github.ref == 'refs/heads/dev'
|
|
||||||
run: conan upload -r amnezia "*" -c
|
|
||||||
|
|
||||||
# ------------------------------------------------------
|
|
||||||
|
|
||||||
Build-MacOS-NE:
|
Build-MacOS-NE:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
|
||||||
needs: Bake-Prebuilts-MacOS-NE
|
|
||||||
if: ${{ always() }}
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
QT_VERSION: 6.10.1
|
QT_VERSION: 6.10.1
|
||||||
|
|
||||||
@@ -602,14 +490,6 @@ jobs:
|
|||||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: ./.github/actions/apple-setup-provisioning-profile
|
|
||||||
with:
|
|
||||||
provisioning_profile_base64: ${{ secrets.MAC_APP_PROVISIONING_PROFILE }}
|
|
||||||
|
|
||||||
- uses: ./.github/actions/apple-setup-provisioning-profile
|
|
||||||
with:
|
|
||||||
provisioning_profile_base64: ${{ secrets.MAC_NE_PROVISIONING_PROFILE }}
|
|
||||||
|
|
||||||
- name: 'Setup xcode'
|
- name: 'Setup xcode'
|
||||||
uses: maxim-lobanov/setup-xcode@v1
|
uses: maxim-lobanov/setup-xcode@v1
|
||||||
with:
|
with:
|
||||||
@@ -645,13 +525,8 @@ jobs:
|
|||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
fetch-depth: 10
|
fetch-depth: 10
|
||||||
|
|
||||||
- name: 'Setup python'
|
# - name: 'Setup ccache'
|
||||||
uses: actions/setup-python@v6
|
# uses: hendrikmuhs/ccache-action@v1.2
|
||||||
with:
|
|
||||||
python-version: 3.14
|
|
||||||
|
|
||||||
- name: 'Install conan'
|
|
||||||
run: pip install "conan==2.28.0"
|
|
||||||
|
|
||||||
- name: 'Build project'
|
- name: 'Build project'
|
||||||
run: |
|
run: |
|
||||||
@@ -665,72 +540,14 @@ jobs:
|
|||||||
path: deploy/build/client/AmneziaVPN.app
|
path: deploy/build/client/AmneziaVPN.app
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
# ------------------------------------------------------
|
|
||||||
|
|
||||||
Bake-Prebuilts-Android:
|
|
||||||
runs-on: android-runner
|
|
||||||
needs: Detect-Changes
|
|
||||||
if: needs.Detect-Changes.outputs.recipes_changed == 'true'
|
|
||||||
|
|
||||||
env:
|
|
||||||
ANDROID_PLATFORM: android-28
|
|
||||||
NDK_VERSION: 27.0.11718014
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: 'true'
|
|
||||||
fetch-depth: 10
|
|
||||||
|
|
||||||
- uses: actions/setup-python@v6
|
|
||||||
with:
|
|
||||||
python-version: 3.14
|
|
||||||
|
|
||||||
- name: 'Install conan'
|
|
||||||
run: pip install "conan==2.28.0"
|
|
||||||
|
|
||||||
- name: 'Setup Android SDK'
|
|
||||||
uses: android-actions/setup-android@v4
|
|
||||||
with:
|
|
||||||
packages: "platforms;${{ env.ANDROID_PLATFORM }} ndk;${{ env.NDK_VERSION }}"
|
|
||||||
|
|
||||||
- name: 'Build dependencies'
|
|
||||||
run: |
|
|
||||||
CMAKE_ANDROID_NDK="$ANDROID_HOME/ndk/$NDK_VERSION"
|
|
||||||
args=(
|
|
||||||
-G Ninja
|
|
||||||
-DPREBUILTS_ONLY=1
|
|
||||||
-DCMAKE_SYSTEM_NAME=Android
|
|
||||||
"-DANDROID_PLATFORM=$ANDROID_PLATFORM"
|
|
||||||
"-DCMAKE_ANDROID_NDK=$CMAKE_ANDROID_NDK"
|
|
||||||
)
|
|
||||||
|
|
||||||
for abi in arm64-v8a armeabi-v7a x86 x86_64; do
|
|
||||||
for build_type in Release Debug; do
|
|
||||||
cmake -S . -B build_${abi//-/_} "${args[@]}" "-DCMAKE_ANDROID_ARCH_ABI=$abi" "-DCMAKE_BUILD_TYPE=$build_type"
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: 'Authorize in remote'
|
|
||||||
if: github.ref == 'refs/heads/dev'
|
|
||||||
run: conan remote login amnezia "${{ secrets.CONAN_USER }}" -p "${{ secrets.CONAN_PASSWORD }}"
|
|
||||||
|
|
||||||
- name: 'Upload baked prebuilts'
|
|
||||||
if: github.ref == 'refs/heads/dev'
|
|
||||||
run: conan upload -r amnezia "*" -c
|
|
||||||
|
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
Build-Android:
|
Build-Android:
|
||||||
runs-on: android-runner
|
runs-on: android-runner
|
||||||
|
|
||||||
needs: Bake-Prebuilts-Android
|
|
||||||
if: ${{ always() }}
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
ANDROID_PLATFORM: android-28
|
ANDROID_BUILD_PLATFORM: android-36
|
||||||
NDK_VERSION: 27.0.11718014
|
QT_VERSION: 6.10.1
|
||||||
QT_VERSION: 6.10.3
|
|
||||||
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||||
@@ -742,11 +559,6 @@ jobs:
|
|||||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Get sources'
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: 'true'
|
|
||||||
|
|
||||||
- name: 'Install desktop Qt'
|
- name: 'Install desktop Qt'
|
||||||
uses: jurplel/install-qt-action@v4
|
uses: jurplel/install-qt-action@v4
|
||||||
with:
|
with:
|
||||||
@@ -807,24 +619,38 @@ jobs:
|
|||||||
py7zrversion: '==0.22.*'
|
py7zrversion: '==0.22.*'
|
||||||
extra: '--base ${{ env.QT_MIRROR }}'
|
extra: '--base ${{ env.QT_MIRROR }}'
|
||||||
|
|
||||||
|
- name: 'Grant execute permission for qt-cmake'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
chmod +x ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/android_x86_64/bin/qt-cmake
|
||||||
|
|
||||||
|
- name: 'Get sources'
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: 'true'
|
||||||
|
|
||||||
|
- name: 'Get version from CMakeLists.txt'
|
||||||
|
id: get_version
|
||||||
|
run: |
|
||||||
|
VERSION=$(grep 'set(AMNEZIAVPN_VERSION' CMakeLists.txt | sed -E 's/.*AMNEZIAVPN_VERSION ([0-9]+.[0-9]+.[0-9]+.[0-9]+)\)/\1/')
|
||||||
|
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||||
|
echo "Version: $VERSION"
|
||||||
|
|
||||||
|
# - name: 'Setup ccache'
|
||||||
|
# uses: hendrikmuhs/ccache-action@v1.2
|
||||||
|
|
||||||
- name: 'Setup Java'
|
- name: 'Setup Java'
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
|
# cache: 'gradle'
|
||||||
|
|
||||||
- name: 'Setup Android SDK'
|
- name: 'Setup Android NDK'
|
||||||
uses: android-actions/setup-android@v4
|
id: setup-ndk
|
||||||
|
uses: nttld/setup-ndk@v1
|
||||||
with:
|
with:
|
||||||
packages: "platforms;${{ env.ANDROID_PLATFORM }} ndk;${{ env.NDK_VERSION }}"
|
ndk-version: 'r26b'
|
||||||
|
|
||||||
- name: 'Setup python'
|
|
||||||
uses: actions/setup-python@v6
|
|
||||||
with:
|
|
||||||
python-version: 3.14
|
|
||||||
|
|
||||||
- name: 'Install conan'
|
|
||||||
run: pip install "conan==2.28.0"
|
|
||||||
|
|
||||||
- name: 'Decode keystore secret to file'
|
- name: 'Decode keystore secret to file'
|
||||||
env:
|
env:
|
||||||
@@ -835,64 +661,61 @@ jobs:
|
|||||||
|
|
||||||
- name: 'Build project'
|
- name: 'Build project'
|
||||||
env:
|
env:
|
||||||
QT_INSTALL_DIR: ${{ runner.temp }}
|
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||||
QT_ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android.keystore
|
QT_HOST_PATH: ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64
|
||||||
QT_ANDROID_KEYSTORE_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }}
|
ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android.keystore
|
||||||
QT_ANDROID_KEYSTORE_STORE_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
|
ANDROID_KEYSTORE_KEY_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }}
|
||||||
|
ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
|
||||||
shell: bash
|
shell: bash
|
||||||
|
run: ./deploy/build_android.sh --aab --apk all --build-platform ${{ env.ANDROID_BUILD_PLATFORM }}
|
||||||
|
|
||||||
|
- name: 'Rename Android APKs'
|
||||||
run: |
|
run: |
|
||||||
deploy/build.sh -t android --sign --aab
|
cd deploy/build
|
||||||
|
mv AmneziaVPN-x86_64-release.apk AmneziaVPN_${VERSION}_android9+_x86_64.apk
|
||||||
|
mv AmneziaVPN-x86-release.apk AmneziaVPN_${VERSION}_android9+_x86.apk
|
||||||
|
mv AmneziaVPN-arm64-v8a-release.apk AmneziaVPN_${VERSION}_android9+_arm64-v8a.apk
|
||||||
|
mv AmneziaVPN-armeabi-v7a-release.apk AmneziaVPN_${VERSION}_android9+_armeabi-v7a.apk
|
||||||
|
cd ../..
|
||||||
|
|
||||||
VERSION=$(grep CMAKE_PROJECT_VERSION:STATIC deploy/build/CMakeCache.txt | cut -d= -f2)
|
- name: 'Upload x86_64 apk'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
(cd deploy/build/client/android-build && mv AmneziaVPN.apk AmneziaVPN_${VERSION}_android9+_universal.apk)
|
|
||||||
(cd deploy/build/client/android-build/build/outputs/bundle/release && mv android-build-release.aab AmneziaVPN_${VERSION}.aab)
|
|
||||||
|
|
||||||
for abi in arm64-v8a armeabi-v7a x86 x86_64; do
|
|
||||||
deploy/build.sh -t android --sign --abi ${abi} --build ./deploy/build/${abi}
|
|
||||||
(cd deploy/build/${abi}/client/android-build && mv AmneziaVPN.apk AmneziaVPN_${VERSION}_android9+_${abi}.apk)
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: 'Upload universal APK'
|
|
||||||
uses: actions/upload-artifact@v7
|
|
||||||
with:
|
with:
|
||||||
path: deploy/build/client/android-build/*.apk
|
name: AmneziaVPN_${{ env.VERSION }}_android9+_x86_64.apk
|
||||||
archive: false
|
path: deploy/build/AmneziaVPN_${{ env.VERSION }}_android9+_x86_64.apk
|
||||||
|
compression-level: 0
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
- name: 'Upload AAB'
|
- name: 'Upload x86 apk'
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: deploy/build/client/android-build/build/outputs/bundle/release/*.aab
|
name: AmneziaVPN_${{ env.VERSION }}_android9+_x86.apk
|
||||||
archive: false
|
path: deploy/build/AmneziaVPN_${{ env.VERSION }}_android9+_x86.apk
|
||||||
|
compression-level: 0
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
- name: 'Upload arm64-v8a APK'
|
- name: 'Upload arm64-v8a apk'
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: deploy/build/arm64-v8a/client/android-build/*.apk
|
name: AmneziaVPN_${{ env.VERSION }}_android9+_arm64-v8a.apk
|
||||||
archive: false
|
path: deploy/build/AmneziaVPN_${{ env.VERSION }}_android9+_arm64-v8a.apk
|
||||||
|
compression-level: 0
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
- name: 'Upload armeabi-v7a APK'
|
- name: 'Upload armeabi-v7a apk'
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: deploy/build/armeabi-v7a/client/android-build/*.apk
|
name: AmneziaVPN_${{ env.VERSION }}_android9+_armeabi-v7a.apk
|
||||||
archive: false
|
path: deploy/build/AmneziaVPN_${{ env.VERSION }}_android9+_armeabi-v7a.apk
|
||||||
|
compression-level: 0
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
- name: 'Upload x86 APK'
|
- name: 'Upload aab'
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: deploy/build/x86/client/android-build/*.apk
|
name: AmneziaVPN-android
|
||||||
archive: false
|
path: deploy/build/AmneziaVPN-release.aab
|
||||||
retention-days: 7
|
compression-level: 0
|
||||||
|
|
||||||
- name: 'Upload x86_64 APK'
|
|
||||||
uses: actions/upload-artifact@v7
|
|
||||||
with:
|
|
||||||
path: deploy/build/x86_64/client/android-build/*.apk
|
|
||||||
archive: false
|
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
Extra:
|
Extra:
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ client/.DS_Store
|
|||||||
._.DS_Store
|
._.DS_Store
|
||||||
._*
|
._*
|
||||||
*.dmg
|
*.dmg
|
||||||
deploy/data/macos/pf/amn.400.allowPIA.conf
|
|
||||||
|
|
||||||
# tmp files
|
# tmp files
|
||||||
*.*~
|
*.*~
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
[submodule "client/3rd/SortFilterProxyModel"]
|
[submodule "client/3rd/SortFilterProxyModel"]
|
||||||
path = client/3rd/SortFilterProxyModel
|
path = client/3rd/SortFilterProxyModel
|
||||||
url = https://github.com/mitchcurtis/SortFilterProxyModel.git
|
url = https://github.com/mitchcurtis/SortFilterProxyModel.git
|
||||||
|
[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"]
|
[submodule "client/3rd/amneziawg-apple"]
|
||||||
path = client/3rd/amneziawg-apple
|
path = client/3rd/amneziawg-apple
|
||||||
url = https://github.com/amnezia-vpn/amneziawg-apple
|
url = https://github.com/amnezia-vpn/amneziawg-apple
|
||||||
|
|||||||
+46
-37
@@ -1,34 +1,18 @@
|
|||||||
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
|
|
||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
set(AMNEZIAVPN_VERSION 4.9.0.0)
|
set(AMNEZIAVPN_VERSION 4.8.15.4)
|
||||||
|
|
||||||
set(QT_CREATOR_SKIP_PACKAGE_MANAGER_SETUP ON CACHE BOOL "" FORCE)
|
|
||||||
set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES
|
|
||||||
${CMAKE_SOURCE_DIR}/cmake/platform_settings.cmake
|
|
||||||
${CMAKE_SOURCE_DIR}/cmake/recipes_bootstrap.cmake
|
|
||||||
${CMAKE_SOURCE_DIR}/cmake/conan_provider.cmake
|
|
||||||
CACHE STRING "" FORCE)
|
|
||||||
|
|
||||||
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
|
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
|
||||||
DESCRIPTION "AmneziaVPN"
|
DESCRIPTION "AmneziaVPN"
|
||||||
HOMEPAGE_URL "https://amnezia.org/"
|
HOMEPAGE_URL "https://amnezia.org/"
|
||||||
)
|
)
|
||||||
|
|
||||||
if (PREBUILTS_ONLY)
|
|
||||||
# trigger conan to kick off `conan install`
|
|
||||||
find_package(OpenSSL REQUIRED)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
||||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||||
|
|
||||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||||
set(APP_ANDROID_VERSION_CODE 2122)
|
set(APP_ANDROID_VERSION_CODE 2120)
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(MZ_PLATFORM_NAME "linux")
|
set(MZ_PLATFORM_NAME "linux")
|
||||||
@@ -45,34 +29,59 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
|
set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
if(APPLE AND NOT IOS)
|
if(APPLE)
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if(IOS)
|
||||||
set(AMN_PF_RULE_IDENTITY "user { root }")
|
set(CMAKE_OSX_ARCHITECTURES "arm64")
|
||||||
|
elseif(MACOS_NE)
|
||||||
|
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64")
|
||||||
else()
|
else()
|
||||||
set(AMN_PF_RULE_IDENTITY "group { amnvpn }")
|
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
configure_file(
|
|
||||||
"${CMAKE_SOURCE_DIR}/deploy/data/pf-templates/amn.400.allowPIA.conf.in"
|
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/amn.400.allowPIA.conf"
|
|
||||||
@ONLY
|
|
||||||
)
|
|
||||||
|
|
||||||
file(COPY_FILE
|
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/amn.400.allowPIA.conf"
|
|
||||||
"${CMAKE_SOURCE_DIR}/deploy/data/macos/pf/amn.400.allowPIA.conf"
|
|
||||||
ONLY_IF_DIFFERENT
|
|
||||||
)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
add_subdirectory(client)
|
add_subdirectory(client)
|
||||||
|
|
||||||
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
||||||
add_subdirectory(service)
|
add_subdirectory(service)
|
||||||
|
|
||||||
|
include(${CMAKE_SOURCE_DIR}/deploy/installer/config.cmake)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if ((LINUX AND NOT ANDROID) OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (WIN32))
|
set(AMNEZIA_STAGE_DIR "${CMAKE_BINARY_DIR}/stage")
|
||||||
include(${CMAKE_SOURCE_DIR}/cmake/CPack.cmake)
|
|
||||||
|
if(WIN32 AND NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
||||||
|
file(TO_CMAKE_PATH "${AMNEZIA_STAGE_DIR}" AMNEZIA_STAGE_DIR_CMAKE)
|
||||||
|
|
||||||
|
set(CPACK_GENERATOR "WIX")
|
||||||
|
set(CPACK_WIX_VERSION 4)
|
||||||
|
set(CPACK_PACKAGE_NAME "AmneziaVPN")
|
||||||
|
set(CPACK_PACKAGE_VENDOR "AmneziaVPN")
|
||||||
|
set(CPACK_PACKAGE_VERSION ${AMNEZIAVPN_VERSION})
|
||||||
|
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "AmneziaVPN client")
|
||||||
|
set(AMNEZIA_LICENSE_TXT "${CMAKE_BINARY_DIR}/LICENSE.txt")
|
||||||
|
configure_file("${CMAKE_SOURCE_DIR}/LICENSE" "${AMNEZIA_LICENSE_TXT}" COPYONLY)
|
||||||
|
set(CPACK_RESOURCE_FILE_LICENSE "${AMNEZIA_LICENSE_TXT}")
|
||||||
|
set(CPACK_PACKAGE_INSTALL_DIRECTORY "AmneziaVPN")
|
||||||
|
set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||||
|
set(CPACK_PACKAGE_EXECUTABLES "AmneziaVPN" "AmneziaVPN")
|
||||||
|
set(CPACK_WIX_UPGRADE_GUID "{2D55AC62-96D6-4692-8C05-0D85BBF95485}")
|
||||||
|
set(CPACK_WIX_PRODUCT_ICON "${CMAKE_SOURCE_DIR}/client/images/app.ico")
|
||||||
|
|
||||||
|
# WiX patches
|
||||||
|
set(_AMNEZIA_WIX_PATCH_SERVICE "${CMAKE_SOURCE_DIR}/deploy/installer/wix/service_install_patch.xml")
|
||||||
|
set(_AMNEZIA_WIX_PATCH_CLOSE_APP "${CMAKE_SOURCE_DIR}/deploy/installer/wix/close_client_patch.xml")
|
||||||
|
file(TO_CMAKE_PATH "${_AMNEZIA_WIX_PATCH_SERVICE}" _AMNEZIA_WIX_PATCH_SERVICE_CMAKE)
|
||||||
|
file(TO_CMAKE_PATH "${_AMNEZIA_WIX_PATCH_CLOSE_APP}" _AMNEZIA_WIX_PATCH_CLOSE_APP_CMAKE)
|
||||||
|
set(CPACK_WIX_PATCH_FILE "${_AMNEZIA_WIX_PATCH_SERVICE_CMAKE};${_AMNEZIA_WIX_PATCH_CLOSE_APP_CMAKE}")
|
||||||
|
|
||||||
|
# WiX v4 Util extension for CloseApplication + namespace for util
|
||||||
|
set(CPACK_WIX_EXTENSIONS "${CPACK_WIX_EXTENSIONS};WixToolset.Util.wixext")
|
||||||
|
set(CPACK_WIX_CUSTOM_XMLNS "util=http://wixtoolset.org/schemas/v4/wxs/util")
|
||||||
|
|
||||||
|
set(CPACK_INSTALLED_DIRECTORIES "${AMNEZIA_STAGE_DIR_CMAKE};/")
|
||||||
|
|
||||||
|
include(CPack)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -53,14 +53,24 @@ AmneziaVPN uses several open-source projects to work:
|
|||||||
|
|
||||||
- [OpenSSL](https://www.openssl.org/)
|
- [OpenSSL](https://www.openssl.org/)
|
||||||
- [OpenVPN](https://openvpn.net/)
|
- [OpenVPN](https://openvpn.net/)
|
||||||
|
- [Shadowsocks](https://shadowsocks.org/)
|
||||||
- [Qt](https://www.qt.io/)
|
- [Qt](https://www.qt.io/)
|
||||||
- [LibSsh](https://libssh.org)
|
- [LibSsh](https://libssh.org) - forked from Qt Creator
|
||||||
- [WireGuard](https://www.wireguard.com/)
|
|
||||||
- [Xray-core](https://xtls.github.io/en/)
|
|
||||||
- [Conan](https://conan.io/)
|
|
||||||
- and more...
|
- and more...
|
||||||
|
|
||||||
## Help us with translations
|
## Checking out the source code
|
||||||
|
|
||||||
|
Make sure to pull all submodules after checking out the repo.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git submodule update --init --recursive
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
Want to contribute? Welcome!
|
||||||
|
|
||||||
|
### Help with translations
|
||||||
|
|
||||||
Download the most actual translation files.
|
Download the most actual translation files.
|
||||||
|
|
||||||
@@ -73,98 +83,99 @@ Each *.ts file contains strings for one corresponding language.
|
|||||||
Translate or correct some strings in one or multiple *.ts files and commit them back to this repository into the ``client/translations`` folder.
|
Translate or correct some strings in one or multiple *.ts files and commit them back to this repository into the ``client/translations`` folder.
|
||||||
You can do it via a web-interface or any other method you're familiar with.
|
You can do it via a web-interface or any other method you're familiar with.
|
||||||
|
|
||||||
## Checking out the source code
|
### Building sources and deployment
|
||||||
|
|
||||||
Make sure to pull all submodules after checking out the repo.
|
Check deploy folder for build scripts.
|
||||||
|
|
||||||
```bash
|
### How to build an iOS app from source code on MacOS
|
||||||
git submodule update --init --recursive
|
|
||||||
```
|
|
||||||
|
|
||||||
## Hacking guide
|
1. First, make sure you have [XCode](https://developer.apple.com/xcode/) installed, at least version 14 or higher.
|
||||||
|
|
||||||
Want to contribute? Welcome!
|
2. We use QT to generate the XCode project. We need QT version 6.6.2. Install QT for MacOS [here](https://doc.qt.io/qt-6/macos.html) or [QT Online Installer](https://www.qt.io/download-open-source). Required modules:
|
||||||
|
- MacOS
|
||||||
### Build requirements
|
- iOS
|
||||||
|
- Qt 5 Compatibility Module
|
||||||
* [`CMake`](https://cmake.org/download/)
|
- Qt Shader Tools
|
||||||
* Compiler and underlying build system, depending on the target:
|
- Additional Libraries:
|
||||||
- [Linux] Any of `make` and `gcc`
|
- Qt Image Formats
|
||||||
- [Apple] [`Xcode`](https://developer.apple.com/xcode/) or [`Xcode command line tools`](https://developer.apple.com/xcode/)
|
- Qt Multimedia
|
||||||
- [Windows] [`Visual Studio 2022`](https://aka.ms/vs/17/release/vs_community.exe) or [`VS 2022 Build Tools`](https://aka.ms/vs/17/release/vs_buildtools.exe)
|
|
||||||
- [Android] [`Android SDK`](#installing-android-sdk) and [`Ninja`](https://ninja-build.org/)
|
|
||||||
* [`Qt 6.10+`](https://www.qt.io/download-open-source) with the following modules:
|
|
||||||
- Core module for targeting platform (Desktop/Android/iOS)
|
|
||||||
- Qt 5 Compatibility module
|
|
||||||
- Qt Remote Objects
|
- Qt Remote Objects
|
||||||
* [`Conan`](https://conan.io/downloads) package manager
|
|
||||||
- On MacOS is enough just to use `homebrew` or install it in `.venv` in project root
|
|
||||||
- Other systems must have it in `PATH`
|
|
||||||
* (Optional) Installer dependencies:
|
|
||||||
- [Windows/Linux] [`Qt Installer Framework`](https://www.qt.io/download-open-source)
|
|
||||||
- [Windows] [`WIX toolset`](https://github.com/wixtoolset/wix/releases)
|
|
||||||
|
|
||||||
### Building the project using scripts
|
3. Install CMake if required. We recommend CMake version 3.25. You can install CMake [here](https://cmake.org/download/)
|
||||||
|
|
||||||
* Run scripts located in `deploy` directory
|
4. You also need to install go >= v1.16. If you don't have it installed already,
|
||||||
* Basically, if dependencies are located in default installation paths, the scripts will find them automatically.
|
download go from the [official website](https://golang.org/dl/) or use Homebrew.
|
||||||
* If they differ, specify them using the following variables:
|
The latest version is recommended. Install gomobile
|
||||||
- `QT_INSTALL_DIR` - Qt root installation folder
|
|
||||||
- `QT_ROOT_PATH` - Qt framework root directory
|
|
||||||
- `QIF_ROOT_PATH` - Qt Installer Framework root path
|
|
||||||
- `ANDROID_HOME` - Path to Android SDK root folder
|
|
||||||
- and others. Check scripts for more
|
|
||||||
|
|
||||||
Unix-like:
|
|
||||||
```bash
|
```bash
|
||||||
# Build executables for the host platform
|
export PATH=$PATH:~/go/bin
|
||||||
deploy/build.sh
|
go install golang.org/x/mobile/cmd/gomobile@latest
|
||||||
|
gomobile init
|
||||||
# Or just
|
|
||||||
deploy/build.sh
|
|
||||||
|
|
||||||
# Build executables and installers for the host platform
|
|
||||||
deploy/build.sh --installer all
|
|
||||||
|
|
||||||
# Build Android APK and AAB
|
|
||||||
deploy/build.sh -t android --aab
|
|
||||||
|
|
||||||
# Call for help
|
|
||||||
deploy/build.sh -h
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Windows:
|
5. Build the project
|
||||||
```batch
|
```bash
|
||||||
:: Build executables for Windows
|
export QT_BIN_DIR="<PATH-TO-QT-FOLDER>/Qt/<QT-VERSION>/ios/bin"
|
||||||
deploy/build.bat
|
export QT_MACOS_ROOT_DIR="<PATH-TO-QT-FOLDER>/Qt/<QT-VERSION>/macos"
|
||||||
|
export QT_IOS_BIN=$QT_BIN_DIR
|
||||||
|
export PATH=$PATH:~/go/bin
|
||||||
|
mkdir build-ios
|
||||||
|
$QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR
|
||||||
|
```
|
||||||
|
Replace PATH-TO-QT-FOLDER and QT-VERSION to your environment
|
||||||
|
|
||||||
:: Build executables with IFW installer for Windows
|
|
||||||
deploy/build.bat --installer ifw
|
|
||||||
|
|
||||||
:: Build executables with IFW and WIX installer for Windows
|
If you get `gomobile: command not found` make sure to set PATH to the location
|
||||||
deploy/build.bat --installer ifw --installer wix
|
of the bin folder where gomobile was installed. Usually, it's in `GOPATH`.
|
||||||
|
```bash
|
||||||
:: Or just
|
export PATH=$(PATH):/path/to/GOPATH/bin
|
||||||
deploy/build.bat --installer all
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Developing the project in IDEs
|
6. Open the XCode project. You can then run /test/archive/ship the app.
|
||||||
|
|
||||||
* Basically, you can use any IDE that handles CMake and Qt kits properly to run configure and build steps, and to navigate through the code nicely. For example:
|
If the build fails with the following error
|
||||||
- `Qt Creator`
|
```
|
||||||
- `Visual Studio Code` with `Qt Extension Pack`
|
make: ***
|
||||||
- and so on
|
[$(PROJECTDIR)/client/build/AmneziaVPN.build/Debug-iphoneos/wireguard-go-bridge/goroot/.prepared]
|
||||||
|
Error 1
|
||||||
|
```
|
||||||
|
Add a user-defined variable to both AmneziaVPN and WireGuardNetworkExtension targets' build settings with
|
||||||
|
key `PATH` and value `${PATH}/path/to/bin/folder/with/go/executable`, e.g. `${PATH}:/usr/local/go/bin`.
|
||||||
|
|
||||||
* To use `Xcode`, you have to configure project first by using `cmake`. The easiest way to do it is to use `Qt Creator` for configuration. Then open `AmneziaVPN.xcodeproj` file from the build folder by using `Xcode`. Note that none of the files changed are saved - the files actually getting changed in build directory. Copy them manually if necessary
|
if the above error persists on your M1 Mac, then most probably you need to install arch based CMake
|
||||||
|
```
|
||||||
|
arch -arm64 brew install cmake
|
||||||
|
```
|
||||||
|
|
||||||
* `Android studio` could be used in the same way - just configure the project by using `cmake` manually or by using `Qt Creator`. Open `<build-dir>/client/android-build` in `Android studio` then. Do not forget to copy the changes - everything you do is saved under the build directory actually.
|
Build might fail with the "source files not found" error the first time you try it, because the modern XCode build system compiles dependencies in parallel, and some dependencies end up being built after the ones that
|
||||||
|
require them. In this case, simply restart the build.
|
||||||
|
|
||||||
### Installing Android SDK
|
## How to build the Android app
|
||||||
|
|
||||||
* Android SDK could be installed using the following methods:
|
_Tested on Mac OS_
|
||||||
- Using `Qt Creator`. Use `Preferences`->`SDKs`
|
|
||||||
- Using `Android studio`. By default it installs necessary `SDKs` automatically during the installation
|
The Android app has the following requirements:
|
||||||
- Manually by using `sdk-manager`. Check [this](https://developer.android.com/tools) page for details
|
* JDK 11
|
||||||
|
* Android platform SDK 33
|
||||||
|
* CMake 3.25.0
|
||||||
|
|
||||||
|
After you have installed QT, QT Creator, and Android Studio, you need to configure QT Creator correctly.
|
||||||
|
|
||||||
|
- Click in the top menu bar on `QT Creator` -> `Preferences` -> `Devices` and select the tab `Android`.
|
||||||
|
- Set path to JDK 11
|
||||||
|
- Set path to Android SDK (`$ANDROID_HOME`)
|
||||||
|
|
||||||
|
In case you get errors regarding missing SDK or 'SDK manager not running', you cannot fix them by correcting the paths. If you have some spare GBs on your disk, you can let QT Creator install all requirements by choosing an empty folder for `Android SDK location` and clicking on `Set Up SDK`. Be aware: This will install a second Android SDK and NDK on your machine!
|
||||||
|
Double-check that the right CMake version is configured: Click on `QT Creator` -> `Preferences` and click on the side menu on `Kits`. Under the center content view's `Kits` tab, you'll find an entry for `CMake Tool`. If the default selected CMake version is lower than 3.25.0, install on your system CMake >= 3.25.0 and choose `System CMake at <path>` from the drop-down list. If this entry is missing, you either have not installed CMake yet or QT Creator hasn't found the path to it. In that case, click in the preferences window on the side menu item `CMake`, then on the tab `Tools` in the center content view, and finally on the button `Add` to set the path to your installed CMake.
|
||||||
|
Please make sure that you have selected Android Platform SDK 33 for your project: click in the main view's side menu on `Projects`, and on the left, you'll see a section `Build & Run` showing different Android build targets. You can select any of them, Amnezia VPN's project setup is designed in a way that all Android targets will be built. Click on the targets submenu item `Build` and scroll in the center content view to `Build Steps`. Click on `Details` at the end of the headline `Build Android APK` (the `Details` button might be hidden in case the QT Creator Window is not running in full screen!). Here we are: Choose `android-33` as `Android Build Platform SDK`.
|
||||||
|
|
||||||
|
That's it! You should be ready to compile the project from QT Creator!
|
||||||
|
|
||||||
|
### Development flow
|
||||||
|
|
||||||
|
After you've hit the build button, QT-Creator copies the whole project to a folder in the repository parent directory. The folder should look something like `build-amnezia-client-Android_Qt_<version>_Clang_<architecture>-<BuildType>`.
|
||||||
|
If you want to develop Amnezia VPNs Android components written in Kotlin, such as components using system APIs, you need to import the generated project in Android Studio with `build-amnezia-client-Android_Qt_<version>_Clang_<architecture>-<BuildType>/client/android-build` as the projects root directory. While you should be able to compile the generated project from Android Studio, you cannot work directly in the repository's Android project. So whenever you are confident with your work in the generated project, you'll need to copy and paste the affected files to the corresponding path in the repository's Android project so that you can add and commit your changes!
|
||||||
|
|
||||||
|
You may face compiling issues in QT Creator after you've worked in Android Studio on the generated project. Just do a `./gradlew clean` in the generated project's root directory (`<path>/client/android-build/.`) and you should be good to go.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
+81
-80
@@ -50,14 +50,23 @@ AmneziaVPN использует несколько проектов с откр
|
|||||||
|
|
||||||
- [OpenSSL](https://www.openssl.org/)
|
- [OpenSSL](https://www.openssl.org/)
|
||||||
- [OpenVPN](https://openvpn.net/)
|
- [OpenVPN](https://openvpn.net/)
|
||||||
|
- [Shadowsocks](https://shadowsocks.org/)
|
||||||
- [Qt](https://www.qt.io/)
|
- [Qt](https://www.qt.io/)
|
||||||
- [LibSsh](https://libssh.org)
|
- [LibSsh](https://libssh.org)
|
||||||
- [WireGuard](https://www.wireguard.com/)
|
|
||||||
- [Xray-core](https://xtls.github.io/en/)
|
|
||||||
- [Conan](https://conan.io/)
|
|
||||||
- и другие...
|
- и другие...
|
||||||
|
|
||||||
## Помощь с переводами
|
## Проверка исходного кода
|
||||||
|
После клонирования репозитория обязательно загрузите все подмодули.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git submodule update --init --recursive
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Разработка
|
||||||
|
Хотите внести свой вклад? Добро пожаловать!
|
||||||
|
|
||||||
|
### Помощь с переводами
|
||||||
|
|
||||||
Загрузите самые актуальные файлы перевода.
|
Загрузите самые актуальные файлы перевода.
|
||||||
|
|
||||||
@@ -67,98 +76,90 @@ AmneziaVPN использует несколько проектов с откр
|
|||||||
|
|
||||||
Переведите или исправьте строки в одном или нескольких файлах *.ts и загрузите их обратно в этот репозиторий в папку ``client/translations``. Это можно сделать через веб-интерфейс или любым другим знакомым вам способом.
|
Переведите или исправьте строки в одном или нескольких файлах *.ts и загрузите их обратно в этот репозиторий в папку ``client/translations``. Это можно сделать через веб-интерфейс или любым другим знакомым вам способом.
|
||||||
|
|
||||||
## Проверка исходного кода
|
### Сборка исходного кода и деплой
|
||||||
|
Проверьте папку deploy для скриптов сборки.
|
||||||
|
|
||||||
После клонирования репозитория обязательно загрузите все подмодули.
|
### Как собрать iOS-приложение из исходного кода на MacOS
|
||||||
|
1. Убедитесь, что у вас установлен Xcode версии 14 или выше.
|
||||||
```bash
|
2. Для генерации проекта Xcode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули:
|
||||||
git submodule update --init --recursive
|
- MacOS
|
||||||
```
|
- iOS
|
||||||
|
- Модуль совместимости с Qt 5
|
||||||
## Руководство по разработке
|
- Qt Shader Tools
|
||||||
|
- Дополнительные библиотеки:
|
||||||
Хотите внести свой вклад? Добро пожаловать!
|
- Qt Image Formats
|
||||||
|
- Qt Multimedia
|
||||||
### Требования для сборки
|
|
||||||
|
|
||||||
* [`CMake`](https://cmake.org/download/)
|
|
||||||
* Компилятор и система сборки, в зависимости от таргета:
|
|
||||||
- [Linux] Любые `make` и `gcc`
|
|
||||||
- [Apple] [`Xcode`](https://developer.apple.com/xcode/) или [`Xcode command line tools`](https://developer.apple.com/xcode/)
|
|
||||||
- [Windows] [`Visual Studio 2022`](https://aka.ms/vs/17/release/vs_community.exe) или [`VS 2022 Build Tools`](https://aka.ms/vs/17/release/vs_buildtools.exe)
|
|
||||||
- [Android] [`Android SDK`](#установка-android-sdk) и [`Ninja`](https://ninja-build.org/)
|
|
||||||
* [`Qt 6.10+`](https://www.qt.io/download-open-source) со следующими модулями:
|
|
||||||
- Основные модули для таргета (Desktop/Android/iOS)
|
|
||||||
- Qt 5 Compatibility module
|
|
||||||
- Qt Remote Objects
|
- Qt Remote Objects
|
||||||
* Пакетный менеджер [`Conan`](https://conan.io/downloads)
|
|
||||||
- На MacOS достаточно использовать `homebrew` или установить в `.venv` в корень проекта
|
|
||||||
- Для остальных систем необходимо прописать пути в `PATH`
|
|
||||||
* (Необязательно) Заивисимости для установщиков:
|
|
||||||
- [Windows/Linux] [`Qt Installer Framework`](https://www.qt.io/download-open-source)
|
|
||||||
- [Windows] [`WIX toolset`](https://github.com/wixtoolset/wix/releases)
|
|
||||||
|
|
||||||
### Сборка проекта через скрипты
|
|
||||||
|
|
||||||
* Запустите скрипты, находящиеся в папке `deploy`
|
3. Установите CMake, если это необходимо. Рекомендуемая версия — 3.25. Скачать CMake можно здесь.
|
||||||
* Если все зависимости установлены в стандартных локациях, скрипт найдёт их самостоятельно
|
4. Установите Go версии >= v1.16. Если Go ещё не установлен, скачайте его с [официального сайта](https://golang.org/dl/) или используйте Homebrew. Установите gomobile:
|
||||||
* Если пути отличаются, их нужно явно указать используя:
|
|
||||||
- `QT_INSTALL_DIR` - корневая папка установки Qt
|
|
||||||
- `QT_ROOT_PATH` - корневая папка Qt Framework
|
|
||||||
- `QIF_ROOT_PATH` - корневая папка Qt Installer Framework
|
|
||||||
- `ANDROID_HOME` - путь к Android SDK
|
|
||||||
- и другие. Их можно получить из вышеуказанных скриптов
|
|
||||||
|
|
||||||
Unix-like:
|
|
||||||
```bash
|
```bash
|
||||||
# Build executables for the host platform
|
export PATH=$PATH:~/go/bin
|
||||||
deploy/build.sh
|
go install golang.org/x/mobile/cmd/gomobile@latest
|
||||||
|
gomobile init
|
||||||
# Or just
|
|
||||||
deploy/build.sh
|
|
||||||
|
|
||||||
# Build executables and installers for the host platform
|
|
||||||
deploy/build.sh --installer all
|
|
||||||
|
|
||||||
# Build Android APK and AAB
|
|
||||||
deploy/build.sh -t android --aab
|
|
||||||
|
|
||||||
# Call for help
|
|
||||||
deploy/build.sh -h
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Windows:
|
5. Соберите проект:
|
||||||
```batch
|
```bash
|
||||||
:: Build executables for Windows
|
export QT_BIN_DIR="<PATH-TO-QT-FOLDER>/Qt/<QT-VERSION>/ios/bin"
|
||||||
deploy/build.bat
|
export QT_MACOS_ROOT_DIR="<PATH-TO-QT-FOLDER>/Qt/<QT-VERSION>/macos"
|
||||||
|
export QT_IOS_BIN=$QT_BIN_DIR
|
||||||
|
export PATH=$PATH:~/go/bin
|
||||||
|
mkdir build-ios
|
||||||
|
$QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR
|
||||||
|
```
|
||||||
|
Замените <PATH-TO-QT-FOLDER> и <QT-VERSION> на ваши значения.
|
||||||
|
|
||||||
:: Build executables with IFW installer for Windows
|
Если появляется ошибка gomobile: command not found, убедитесь, что PATH настроен на папку bin, где установлен gomobile:
|
||||||
deploy/build.bat --installer ifw
|
```bash
|
||||||
|
export PATH=$(PATH):/path/to/GOPATH/bin
|
||||||
:: Build executables with IFW and WIX installer for Windows
|
|
||||||
deploy/build.bat --installer ifw --installer wix
|
|
||||||
|
|
||||||
:: Or just
|
|
||||||
deploy/build.bat --installer all
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Разработка в IDE
|
6. Откройте проект в Xcode. Теперь вы можете тестировать, архивировать или публиковать приложение.
|
||||||
|
|
||||||
* Можно использовать любые IDE которые умеют работать с CMake и находить Qt Kits. Например:
|
Если сборка завершится с ошибкой:
|
||||||
- `Qt Creator`
|
```
|
||||||
- `Visual Studio Code` with `Qt Extension Pack`
|
make: ***
|
||||||
- и так далее
|
[$(PROJECTDIR)/client/build/AmneziaVPN.build/Debug-iphoneos/wireguard-go-bridge/goroot/.prepared]
|
||||||
|
Error 1
|
||||||
|
```
|
||||||
|
Добавьте пользовательскую переменную PATH в настройки сборки для целей AmneziaVPN и WireGuardNetworkExtension с ключом `PATH` и значением `${PATH}/path/to/bin/folder/with/go/executable`, e.g. `${PATH}:/usr/local/go/bin`.
|
||||||
|
|
||||||
* Для использования `Xcode` нужно сконфигурировать проект с помощью `cmake`. Самый простой способ это сделать - использовать `Qt Creator` для конфигурации. Затем, нужно открыть файл `AmneziaVPN.xcodeproj` из папки сборки с помощью `Xcode`. Учтите, что никакие файлы фактически не сохраняются - они сохраняются в директории сборки. Если требуется, скопируйте файлы вручную
|
Если ошибка повторяется на Mac с M1, установите версию CMake для архитектуры ARM:
|
||||||
|
```
|
||||||
|
arch -arm64 brew install cmake
|
||||||
|
```
|
||||||
|
|
||||||
* `Android studio` может быть использована подобным вышеуказанному способу - нужно использовать `cmake` вручную или через `Qt Creator` для конфигурации. Далее, откройте `<build-dir>/client/android-build` в `Android studio`. Не забудьте скопировать изменённые файлы в папку с исходным кодом - все файлы, изменённые в IDE, сохраняются фактически в папке сборки.
|
При первой попытке сборка может завершиться с ошибкой source files not found. Это происходит из-за параллельной компиляции зависимостей в XCode. Просто перезапустите сборку.
|
||||||
|
|
||||||
### Установка Android SDK
|
|
||||||
|
|
||||||
* Android SDK может быть установлен следующими способами:
|
## Как собрать Android-приложение
|
||||||
- Используя `Qt Creator`, через настройки в пунктах `Preferences`->`SDKs`
|
Сборка тестировалась на MacOS. Требования:
|
||||||
- Используя `Android studio`. По умолчанию необходимые `SDK` устанавливаются автоматически.
|
- JDK 11
|
||||||
- Вручную, используя `sdk-manager`. Подробности можно найти [здесь](https://developer.android.com/tools)
|
- Android SDK 33
|
||||||
|
- CMake 3.25.0
|
||||||
|
|
||||||
|
Установите QT, QT Creator и Android Studio.
|
||||||
|
Настройте QT Creator:
|
||||||
|
|
||||||
|
- В меню QT Creator перейдите в `QT Creator` -> `Preferences` -> `Devices` ->`Android`.
|
||||||
|
- Укажите путь к JDK 11.
|
||||||
|
- Укажите путь к Android SDK (`$ANDROID_HOME`)
|
||||||
|
|
||||||
|
Если вы сталкиваетесь с ошибками, связанными с отсутствием SDK или сообщением «SDK manager not running», их нельзя исправить просто корректировкой путей. Если у вас есть несколько свободных гигабайт на диске, вы можете позволить Qt Creator установить все необходимые компоненты, выбрав пустую папку для расположения Android SDK и нажав кнопку **Set Up SDK**. Учтите: это установит второй Android SDK и NDK на вашем компьютере!
|
||||||
|
|
||||||
|
Убедитесь, что настроена правильная версия CMake: перейдите в **Qt Creator -> Preferences** и в боковом меню выберите пункт **Kits**. В центральной части окна, на вкладке **Kits**, найдите запись для инструмента **CMake Tool**. Если выбранная по умолчанию версия CMake ниже 3.25.0, установите на свою систему CMake версии 3.25.0 или выше, а затем выберите опцию **System CMake at <путь>** из выпадающего списка. Если этот пункт отсутствует, это может означать, что вы еще не установили CMake, или Qt Creator не смог найти путь к нему. В таком случае в окне **Preferences** перейдите в боковое меню **CMake**, затем во вкладку **Tools** в центральной части окна и нажмите кнопку **Add**, чтобы указать путь к установленному CMake.
|
||||||
|
|
||||||
|
Убедитесь, что для вашего проекта выбрана Android Platform SDK 33: в главном окне на боковой панели выберите пункт **Projects**, и слева вы увидите раздел **Build & Run**, показывающий различные целевые Android-платформы. Вы можете выбрать любую из них, так как настройка проекта Amnezia VPN разработана таким образом, чтобы все Android-цели могли быть собраны. Перейдите в подраздел **Build** и прокрутите центральную часть окна до раздела **Build Steps**. Нажмите **Details** в заголовке **Build Android APK** (кнопка **Details** может быть скрыта, если окно Qt Creator не запущено в полноэкранном режиме!). Вот здесь выберите **android-33** в качестве Android Build Platform SDK.
|
||||||
|
|
||||||
|
### Разработка Android-компонентов
|
||||||
|
|
||||||
|
После сборки QT Creator копирует проект в отдельную папку, например, `build-amnezia-client-Android_Qt_<version>_Clang_<architecture>-<BuildType>`. Для разработки Android-компонентов откройте сгенерированный проект в Android Studio, указав папку `build-amnezia-client-Android_Qt_<version>_Clang_<architecture>-<BuildType>/client/android-build` в качестве корневой.
|
||||||
|
Изменения в сгенерированном проекте нужно вручную перенести в репозиторий. После этого можно коммитить изменения.
|
||||||
|
Если возникают проблемы со сборкой в QT Creator после работы в Android Studio, выполните команду `./gradlew clean` в корневой папке сгенерированного проекта (`<path>/client/android-build/.`).
|
||||||
|
|
||||||
|
|
||||||
## Лицензия
|
## Лицензия
|
||||||
|
|
||||||
|
|||||||
Submodule
+1
Submodule client/3rd-prebuilt added at 51bb4703a4
+49
-60
@@ -65,12 +65,13 @@ endif()
|
|||||||
qt6_add_resources(QRC ${QRC}
|
qt6_add_resources(QRC ${QRC}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/images/images.qrc
|
${CMAKE_CURRENT_LIST_DIR}/images/images.qrc
|
||||||
${CMAKE_CURRENT_LIST_DIR}/images/flagKit.qrc
|
${CMAKE_CURRENT_LIST_DIR}/images/flagKit.qrc
|
||||||
${CMAKE_CURRENT_LIST_DIR}/client_scripts/clientScripts.qrc
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ui/qml/qml.qrc
|
${CMAKE_CURRENT_LIST_DIR}/ui/qml/qml.qrc
|
||||||
${CMAKE_CURRENT_LIST_DIR}/server_scripts/serverScripts.qrc
|
${CMAKE_CURRENT_LIST_DIR}/server_scripts/serverScripts.qrc
|
||||||
)
|
)
|
||||||
|
|
||||||
# -- i18n begin
|
# -- i18n begin
|
||||||
|
set(CMAKE_AUTORCC ON)
|
||||||
|
|
||||||
set(AMNEZIAVPN_TS_FILES
|
set(AMNEZIAVPN_TS_FILES
|
||||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru_RU.ts
|
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru_RU.ts
|
||||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_zh_CN.ts
|
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_zh_CN.ts
|
||||||
@@ -82,10 +83,20 @@ set(AMNEZIAVPN_TS_FILES
|
|||||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_hi_IN.ts
|
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_hi_IN.ts
|
||||||
)
|
)
|
||||||
|
|
||||||
qt6_add_translations(${PROJECT}
|
file(GLOB_RECURSE AMNEZIAVPN_TS_SOURCES *.qrc *.cpp *.h *.ui)
|
||||||
TS_FILES ${AMNEZIAVPN_TS_FILES}
|
list(FILTER AMNEZIAVPN_TS_SOURCES EXCLUDE REGEX "qtgamepad/examples")
|
||||||
RESOURCE_PREFIX "/translations"
|
|
||||||
)
|
qt_create_translation(AMNEZIAVPN_QM_FILES ${AMNEZIAVPN_TS_SOURCES} ${AMNEZIAVPN_TS_FILES})
|
||||||
|
|
||||||
|
set(QM_FILE_LIST "")
|
||||||
|
foreach(FILE ${AMNEZIAVPN_QM_FILES})
|
||||||
|
get_filename_component(QM_FILE_NAME ${FILE} NAME)
|
||||||
|
list(APPEND QM_FILE_LIST "<file>${QM_FILE_NAME}</file>")
|
||||||
|
endforeach()
|
||||||
|
string(REPLACE ";" "" QM_FILE_LIST ${QM_FILE_LIST})
|
||||||
|
|
||||||
|
configure_file(${CMAKE_CURRENT_LIST_DIR}/translations/translations.qrc.in ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)
|
||||||
|
qt6_add_resources(QRC ${I18NQRC} ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)
|
||||||
# -- i18n end
|
# -- i18n end
|
||||||
|
|
||||||
set(IS_CI ${CI})
|
set(IS_CI ${CI})
|
||||||
@@ -164,10 +175,6 @@ if(APPLE)
|
|||||||
set(CMAKE_XCODE_GENERATE_SCHEME FALSE)
|
set(CMAKE_XCODE_GENERATE_SCHEME FALSE)
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${BUILD_VPN_DEVELOPMENT_TEAM})
|
set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${BUILD_VPN_DEVELOPMENT_TEAM})
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_GROUP_ID_IOS ${BUILD_IOS_GROUP_IDENTIFIER})
|
set(CMAKE_XCODE_ATTRIBUTE_GROUP_ID_IOS ${BUILD_IOS_GROUP_IDENTIFIER})
|
||||||
|
|
||||||
if (BUILD_VPN_KEYCHAIN)
|
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--keychain ${BUILD_VPN_KEYCHAIN}")
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(LINUX AND NOT ANDROID)
|
if(LINUX AND NOT ANDROID)
|
||||||
@@ -193,15 +200,45 @@ elseif(APPLE)
|
|||||||
include(cmake/macos.cmake)
|
include(cmake/macos.cmake)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT} PRIVATE ${LIBS})
|
||||||
|
target_compile_definitions(${PROJECT} PRIVATE "MZ_$<UPPER_CASE:${MZ_PLATFORM_NAME}>")
|
||||||
|
|
||||||
|
# deploy artifacts required to run the application to the debug build folder
|
||||||
|
if(WIN32)
|
||||||
|
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
|
||||||
|
set(DEPLOY_PLATFORM_PATH "windows/x64")
|
||||||
|
else()
|
||||||
|
set(DEPLOY_PLATFORM_PATH "windows/x32")
|
||||||
|
endif()
|
||||||
|
elseif(LINUX)
|
||||||
|
set(DEPLOY_PLATFORM_PATH "linux/client")
|
||||||
|
elseif(APPLE AND NOT IOS)
|
||||||
|
set(DEPLOY_PLATFORM_PATH "macos")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ${PROJECT} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E $<IF:$<CONFIG:Debug>,copy_directory,true>
|
||||||
|
${CMAKE_SOURCE_DIR}/deploy/data/${DEPLOY_PLATFORM_PATH}
|
||||||
|
$<TARGET_FILE_DIR:${PROJECT}>
|
||||||
|
COMMAND_EXPAND_LISTS
|
||||||
|
)
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ${PROJECT} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E $<IF:$<CONFIG:Debug>,copy_directory,true>
|
||||||
|
${CMAKE_SOURCE_DIR}/client/3rd-prebuilt/deploy-prebuilt/${DEPLOY_PLATFORM_PATH}
|
||||||
|
$<TARGET_FILE_DIR:${PROJECT}>
|
||||||
|
COMMAND_EXPAND_LISTS
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
list(APPEND SOURCES ${CMAKE_CURRENT_LIST_DIR}/main.cpp)
|
list(APPEND SOURCES ${CMAKE_CURRENT_LIST_DIR}/main.cpp)
|
||||||
|
|
||||||
target_link_libraries(${PROJECT} PRIVATE ${LIBS})
|
|
||||||
target_compile_definitions(${PROJECT} PRIVATE "MZ_$<UPPER_CASE:${MZ_PLATFORM_NAME}>")
|
|
||||||
|
|
||||||
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
||||||
|
|
||||||
# Finalize the executable so Qt can gather/deploy QML modules and plugins correctly (Android needs this).
|
# Finalize the executable so Qt can gather/deploy QML modules and plugins correctly (Android needs this).
|
||||||
@@ -213,51 +250,3 @@ if(COMMAND qt_finalize_executable)
|
|||||||
else()
|
else()
|
||||||
qt_finalize_target(${PROJECT})
|
qt_finalize_target(${PROJECT})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
install(TARGETS ${PROJECT}
|
|
||||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
||||||
COMPONENT AmneziaVPN
|
|
||||||
)
|
|
||||||
install(FILES $<TARGET_RUNTIME_DLLS:${PROJECT}>
|
|
||||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
||||||
COMPONENT AmneziaVPN
|
|
||||||
)
|
|
||||||
|
|
||||||
set(deploy_tool_options "")
|
|
||||||
if(WIN32)
|
|
||||||
set(deploy_tool_options "--force-openssl --force")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
qt_generate_deploy_qml_app_script(
|
|
||||||
TARGET ${PROJECT}
|
|
||||||
OUTPUT_SCRIPT QT_DEPLOY_SCRIPT
|
|
||||||
NO_UNSUPPORTED_PLATFORM_ERROR
|
|
||||||
DEPLOY_TOOL_OPTIONS ${deploy_tool_options}
|
|
||||||
)
|
|
||||||
install(SCRIPT ${QT_DEPLOY_SCRIPT}
|
|
||||||
COMPONENT AmneziaVPN
|
|
||||||
)
|
|
||||||
|
|
||||||
if (APPLE AND NOT IOS AND NOT MACOS_NE)
|
|
||||||
list(APPEND OVPN_SCRIPTS "${CMAKE_SOURCE_DIR}/deploy/data/macos/update-resolv-conf.sh")
|
|
||||||
endif()
|
|
||||||
if (LINUX AND NOT ANDROID)
|
|
||||||
list(APPEND OVPN_SCRIPTS "${CMAKE_SOURCE_DIR}/deploy/data/linux/update-resolv-conf.sh")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(OVPN_SCRIPTS)
|
|
||||||
add_custom_command(TARGET ${PROJECT} POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
|
||||||
${OVPN_SCRIPTS}
|
|
||||||
"$<TARGET_FILE_DIR:${PROJECT}>"
|
|
||||||
)
|
|
||||||
|
|
||||||
install(FILES ${OVPN_SCRIPTS}
|
|
||||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
||||||
COMPONENT AmneziaVPN
|
|
||||||
PERMISSIONS
|
|
||||||
OWNER_READ OWNER_EXECUTE
|
|
||||||
GROUP_READ GROUP_EXECUTE
|
|
||||||
WORLD_READ WORLD_EXECUTE
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ android {
|
|||||||
|
|
||||||
// keeps language resources for only the locales specified below
|
// keeps language resources for only the locales specified below
|
||||||
resourceConfigurations += listOf("en", "ru", "b+zh+Hans")
|
resourceConfigurations += listOf("en", "ru", "b+zh+Hans")
|
||||||
ndk.abiFilters += qtTargetAbiList.split(",")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@@ -53,12 +52,50 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
register("release") {
|
||||||
|
storeFile = providers.environmentVariable("ANDROID_KEYSTORE_PATH").orNull?.let { file(it) }
|
||||||
|
storePassword = providers.environmentVariable("ANDROID_KEYSTORE_KEY_PASS").orNull
|
||||||
|
keyAlias = providers.environmentVariable("ANDROID_KEYSTORE_KEY_ALIAS").orNull
|
||||||
|
keyPassword = providers.environmentVariable("ANDROID_KEYSTORE_KEY_PASS").orNull
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
// exclude coroutine debug resource from release build
|
// exclude coroutine debug resource from release build
|
||||||
packaging {
|
packaging {
|
||||||
resources.excludes += "DebugProbesKt.bin"
|
resources.excludes += "DebugProbesKt.bin"
|
||||||
}
|
}
|
||||||
|
signingConfig = signingConfigs["release"]
|
||||||
|
}
|
||||||
|
|
||||||
|
create("fdroid") {
|
||||||
|
initWith(getByName("release"))
|
||||||
|
signingConfig = null
|
||||||
|
matchingFallbacks += "release"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
splits {
|
||||||
|
abi {
|
||||||
|
isEnable = true
|
||||||
|
reset()
|
||||||
|
include(*qtTargetAbiList.split(',').toTypedArray())
|
||||||
|
isUniversalApk = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fix for Qt Creator to allow deploying the application to a device
|
||||||
|
// to enable this fix, add the line outputBaseName=android-build to local.properties
|
||||||
|
if (outputBaseName.isNotEmpty()) {
|
||||||
|
applicationVariants.all {
|
||||||
|
outputs.map { it as BaseVariantOutputImpl }
|
||||||
|
.forEach { output ->
|
||||||
|
if (output.outputFileName.endsWith(".apk")) {
|
||||||
|
output.outputFileName = "$outputBaseName-${buildType.name}.apk"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "8.6.1"
|
agp = "8.5.2"
|
||||||
kotlin = "1.9.24"
|
kotlin = "1.9.24"
|
||||||
androidx-core = "1.13.1"
|
androidx-core = "1.13.1"
|
||||||
androidx-activity = "1.9.1"
|
androidx-activity = "1.9.1"
|
||||||
androidx-annotation = "1.8.2"
|
androidx-annotation = "1.8.2"
|
||||||
androidx-biometric = "1.2.0-alpha05"
|
androidx-biometric = "1.2.0-alpha05"
|
||||||
androidx-camera = "1.5.3"
|
androidx-camera = "1.3.4"
|
||||||
androidx-fragment = "1.8.2"
|
androidx-fragment = "1.8.2"
|
||||||
androidx-security-crypto = "1.1.0-alpha06"
|
androidx-security-crypto = "1.1.0-alpha06"
|
||||||
androidx-datastore = "1.1.1"
|
androidx-datastore = "1.1.1"
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ plugins {
|
|||||||
id("settings-property-delegate")
|
id("settings-property-delegate")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rootProject.name = "AmneziaVPN"
|
||||||
rootProject.buildFileName = "build.gradle.kts"
|
rootProject.buildFileName = "build.gradle.kts"
|
||||||
|
|
||||||
include(":qt")
|
include(":qt")
|
||||||
@@ -46,7 +47,15 @@ val qtMinSdkVersion: String by gradleProperties
|
|||||||
// set default values for all modules
|
// set default values for all modules
|
||||||
configure<SettingsExtension> {
|
configure<SettingsExtension> {
|
||||||
buildToolsVersion = androidBuildToolsVersion
|
buildToolsVersion = androidBuildToolsVersion
|
||||||
compileSdk = androidCompileSdkVersion.split('-')[1].toInt()
|
compileSdk = androidCompileSdkVersion.substringAfter('-').toInt()
|
||||||
minSdk = qtMinSdkVersion.toInt()
|
minSdk = qtMinSdkVersion.toInt()
|
||||||
ndkVersion = androidNdkVersion
|
ndkVersion = androidNdkVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stop Gradle running by androiddeployqt
|
||||||
|
gradle.taskGraph.whenReady {
|
||||||
|
if (providers.environmentVariable("ANDROIDDEPLOYQT_RUN").isPresent
|
||||||
|
&& !providers.systemProperty("explicitRun").isPresent) {
|
||||||
|
allTasks.forEach { it.enabled = false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -214,7 +214,10 @@ class AmneziaActivity : QtActivity() {
|
|||||||
|
|
||||||
private fun loadLibs() {
|
private fun loadLibs() {
|
||||||
listOf(
|
listOf(
|
||||||
"rsapss"
|
"rsapss",
|
||||||
|
"crypto_3",
|
||||||
|
"ssl_3",
|
||||||
|
"ssh"
|
||||||
).forEach {
|
).forEach {
|
||||||
loadSharedLibrary(this.applicationContext, it)
|
loadSharedLibrary(this.applicationContext, it)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
<RCC>
|
|
||||||
<qresource prefix="/client_scripts">
|
|
||||||
<file>mac_installer.sh</file>
|
|
||||||
</qresource>
|
|
||||||
</RCC>
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
EXTRACT_DIR="$1"
|
|
||||||
INSTALLER_PATH="$2"
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "[AmneziaVPN] Installer package: $INSTALLER_PATH"
|
|
||||||
|
|
||||||
if [ ! -f "$INSTALLER_PATH" ]; then
|
|
||||||
echo "[AmneziaVPN] ERROR: Installer package not found: $INSTALLER_PATH"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
PKG_PATH="$INSTALLER_PATH"
|
|
||||||
echo "[AmneziaVPN] Using PKG: $PKG_PATH"
|
|
||||||
|
|
||||||
# Optional: basic signature/gatekeeper checks (non-fatal)
|
|
||||||
if command -v pkgutil >/dev/null 2>&1; then
|
|
||||||
pkgutil --check-signature "$PKG_PATH" || true
|
|
||||||
fi
|
|
||||||
if command -v spctl >/dev/null 2>&1; then
|
|
||||||
spctl -a -vvv -t install "$PKG_PATH" || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run installer with admin privileges via AppleScript (prompts for password)
|
|
||||||
echo "[AmneziaVPN] Running installer..."
|
|
||||||
OSA_CMD='do shell script "/usr/sbin/installer -pkg '"$PKG_PATH"' -target /" with administrator privileges'
|
|
||||||
osascript -e "$OSA_CMD"
|
|
||||||
|
|
||||||
STATUS=$?
|
|
||||||
if [ $STATUS -ne 0 ]; then
|
|
||||||
echo "[AmneziaVPN] ERROR: installer exited with status $STATUS"
|
|
||||||
exit $STATUS
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[AmneziaVPN] Cleaning up..."
|
|
||||||
rm -f "$INSTALLER_PATH" || true
|
|
||||||
rm -rf "$EXTRACT_DIR" 2>/dev/null || true
|
|
||||||
|
|
||||||
echo "[AmneziaVPN] Installation completed successfully"
|
|
||||||
exit 0
|
|
||||||
@@ -8,11 +8,81 @@ include(${CLIENT_ROOT_DIR}/cmake/QSimpleCrypto.cmake)
|
|||||||
|
|
||||||
include(${CLIENT_ROOT_DIR}/3rd/qrcodegen/qrcodegen.cmake)
|
include(${CLIENT_ROOT_DIR}/3rd/qrcodegen/qrcodegen.cmake)
|
||||||
|
|
||||||
|
set(LIBSSH_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/libssh/")
|
||||||
|
set(OPENSSL_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/openssl/")
|
||||||
|
|
||||||
|
set(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/lib")
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/windows/include")
|
||||||
|
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
|
||||||
|
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/windows/x86_64/ssh.lib")
|
||||||
|
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/windows/x86_64")
|
||||||
|
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/windows/win64/libssl.lib")
|
||||||
|
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win64/libcrypto.lib")
|
||||||
|
else()
|
||||||
|
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/windows/x86/ssh.lib")
|
||||||
|
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/windows/x86")
|
||||||
|
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libssl.lib")
|
||||||
|
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib")
|
||||||
|
endif()
|
||||||
|
elseif(APPLE AND NOT IOS)
|
||||||
|
if(MACOS_NE)
|
||||||
|
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/universal2/libssh.a")
|
||||||
|
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/universal2/libz.a")
|
||||||
|
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/universal2")
|
||||||
|
else()
|
||||||
|
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libssh.a")
|
||||||
|
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libz.a")
|
||||||
|
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64")
|
||||||
|
endif()
|
||||||
|
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/macos/include")
|
||||||
|
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libssl.a")
|
||||||
|
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libcrypto.a")
|
||||||
|
elseif(IOS)
|
||||||
|
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/ios/arm64")
|
||||||
|
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/ios/arm64/libssh.a")
|
||||||
|
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/ios/arm64/libz.a")
|
||||||
|
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/ios/iphone/include")
|
||||||
|
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/ios/iphone/lib/libssl.a")
|
||||||
|
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/ios/iphone/lib/libcrypto.a")
|
||||||
|
elseif(ANDROID)
|
||||||
|
set(abi ${CMAKE_ANDROID_ARCH_ABI})
|
||||||
|
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/android/${abi}")
|
||||||
|
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/android/${abi}/libssh.so")
|
||||||
|
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/android/include")
|
||||||
|
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/android/${abi}/libssl.a")
|
||||||
|
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/android/${abi}/libcrypto.a")
|
||||||
|
set(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/android/${abi}")
|
||||||
|
elseif(LINUX)
|
||||||
|
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/linux/x86_64")
|
||||||
|
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/linux/x86_64/libz.a")
|
||||||
|
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/linux/x86_64/libssh.a")
|
||||||
|
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/linux/include")
|
||||||
|
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/linux/x86_64/libssl.a")
|
||||||
|
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/linux/x86_64/libcrypto.a")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(COPY ${OPENSSL_LIB_SSL_PATH} ${OPENSSL_LIB_CRYPTO_PATH}
|
||||||
|
DESTINATION ${OPENSSL_LIBRARIES_DIR})
|
||||||
|
|
||||||
|
set(OPENSSL_USE_STATIC_LIBS TRUE)
|
||||||
|
|
||||||
|
set(LIBS ${LIBS}
|
||||||
|
${LIBSSH_LIB_PATH}
|
||||||
|
${ZLIB_LIB_PATH}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(LIBS ${LIBS}
|
||||||
|
${OPENSSL_LIB_SSL_PATH}
|
||||||
|
${OPENSSL_LIB_CRYPTO_PATH}
|
||||||
|
)
|
||||||
|
|
||||||
add_compile_definitions(_WINSOCKAPI_)
|
add_compile_definitions(_WINSOCKAPI_)
|
||||||
|
|
||||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||||
set(BUILD_WITH_QT6 ON)
|
set(BUILD_WITH_QT6 ON)
|
||||||
add_subdirectory(${CLIENT_ROOT_DIR}/3rd/qtkeychain EXCLUDE_FROM_ALL)
|
add_subdirectory(${CLIENT_ROOT_DIR}/3rd/qtkeychain)
|
||||||
|
|
||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
# Use qtgamepad from amnezia-vpn/qtgamepad repository
|
# Use qtgamepad from amnezia-vpn/qtgamepad repository
|
||||||
@@ -36,13 +106,12 @@ endif()
|
|||||||
set(LIBS ${LIBS} qt6keychain)
|
set(LIBS ${LIBS} qt6keychain)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
|
${OPENSSL_INCLUDE_DIR}
|
||||||
|
${LIBSSH_INCLUDE_DIR}/include
|
||||||
|
${LIBSSH_ROOT_DIR}/include
|
||||||
|
${CLIENT_ROOT_DIR}/3rd/libssh/include
|
||||||
${CLIENT_ROOT_DIR}/3rd/QSimpleCrypto/src/include
|
${CLIENT_ROOT_DIR}/3rd/QSimpleCrypto/src/include
|
||||||
${CLIENT_ROOT_DIR}/3rd/qtkeychain/qtkeychain
|
${CLIENT_ROOT_DIR}/3rd/qtkeychain/qtkeychain
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/3rd/qtkeychain
|
${CMAKE_CURRENT_BINARY_DIR}/3rd/qtkeychain
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/3rd/libssh/include
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(OpenSSL REQUIRED)
|
|
||||||
list(APPEND LIBS OpenSSL::SSL OpenSSL::Crypto)
|
|
||||||
|
|
||||||
find_package(libssh REQUIRED)
|
|
||||||
list(APPEND LIBS ssh::ssh)
|
|
||||||
|
|||||||
+14
-10
@@ -42,14 +42,18 @@ set(SOURCES ${SOURCES}
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/core/utils/installedAppsImageProvider.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/core/utils/installedAppsImageProvider.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
foreach(abi IN ITEMS ${QT_ANDROID_ABIS})
|
||||||
|
set_property(TARGET ${PROJECT} PROPERTY QT_ANDROID_EXTRA_LIBS
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/amneziawg/android/${abi}/libwg-go.so
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/libck-ovpn-plugin.so
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/libovpn3.so
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/libovpnutil.so
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/librsapss.so
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openssl/android/${abi}/libcrypto_3.so
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openssl/android/${abi}/libssl_3.so
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/libssh/android/${abi}/libssh.so
|
||||||
|
)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
find_package(awg-android REQUIRED)
|
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/xray/android/libxray.aar
|
||||||
set(LIBS ${LIBS} amnezia::awg-android)
|
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/android/xray/libXray)
|
||||||
set_property(TARGET ${PROJECT} APPEND PROPERTY QT_ANDROID_EXTRA_LIBS ${AMNEZIA_ANDROID_LIBWG_PATH} ${AMNEZIA_ANDROID_LIBWG_QUICK_PATH})
|
|
||||||
|
|
||||||
find_package(amnezia-libxray REQUIRED)
|
|
||||||
file(COPY ${AMNEZIA_LIBXRAY_PATH} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/android/xray/libXray)
|
|
||||||
|
|
||||||
find_package(openvpn-pt-android REQUIRED)
|
|
||||||
set(LIBS ${LIBS} amnezia::openvpn-pt-android)
|
|
||||||
set_property(TARGET ${PROJECT} APPEND PROPERTY QT_ANDROID_EXTRA_LIBS ${OPENVPN_PT_ANDROID_LIBCK_OVPN_PLUGIN_PATH})
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
message("Client iOS build")
|
message("Client iOS build")
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 13.0)
|
||||||
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||||
|
|
||||||
|
|
||||||
enable_language(OBJC)
|
enable_language(OBJC)
|
||||||
enable_language(OBJCXX)
|
enable_language(OBJCXX)
|
||||||
enable_language(Swift)
|
enable_language(Swift)
|
||||||
@@ -130,8 +132,17 @@ target_sources(${PROJECT} PRIVATE
|
|||||||
|
|
||||||
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard
|
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Media.xcassets
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(ios/networkextension)
|
add_subdirectory(ios/networkextension)
|
||||||
add_dependencies(${PROJECT} 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ set_target_properties(${PROJECT} PROPERTIES
|
|||||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}"
|
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}"
|
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||||
)
|
)
|
||||||
|
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||||
|
|
||||||
|
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ui/utils/macosUtil.h
|
${CMAKE_CURRENT_SOURCE_DIR}/ui/utils/macosUtil.h
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
message("Client ==> MacOS NE build")
|
message("Client ==> MacOS NE build")
|
||||||
|
|
||||||
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
|
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||||
|
|
||||||
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||||
|
|
||||||
@@ -139,6 +140,7 @@ target_sources(${PROJECT} PRIVATE
|
|||||||
)
|
)
|
||||||
|
|
||||||
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Images.xcassets
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -151,6 +153,19 @@ message(${QtCore_location})
|
|||||||
|
|
||||||
get_filename_component(QT_BIN_DIR_DETECTED "${QtCore_location}/../../../../../bin" ABSOLUTE)
|
get_filename_component(QT_BIN_DIR_DETECTED "${QtCore_location}/../../../../../bin" ABSOLUTE)
|
||||||
|
|
||||||
add_custom_command(TARGET ${PROJECT} POST_BUILD
|
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
||||||
COMMAND ${QT_BIN_DIR_DETECTED}/macdeployqt $<TARGET_BUNDLE_DIR:AmneziaVPN> -appstore-compliant -qmldir=${CMAKE_CURRENT_SOURCE_DIR}
|
"${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos/OpenVPNAdapter.framework"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos)
|
||||||
|
target_link_libraries("AmneziaVPNNetworkExtension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos/OpenVPNAdapter.framework")
|
||||||
|
|
||||||
|
add_custom_command(TARGET ${PROJECT} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||||
|
$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks
|
||||||
|
COMMAND /usr/bin/find "$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework" -name "*.sha256" -delete
|
||||||
|
COMMAND /usr/bin/codesign --force --sign "Apple Distribution: Privacy Technologies OU"
|
||||||
|
"$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework/Versions/Current/OpenVPNAdapter"
|
||||||
|
COMMAND ${QT_BIN_DIR_DETECTED}/macdeployqt $<TARGET_BUNDLE_DIR:AmneziaVPN> -appstore-compliant -qmldir=${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
COMMENT "Signing OpenVPNAdapter framework"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ set(HEADERS ${HEADERS}
|
|||||||
${CLIENT_ROOT_DIR}/core/utils/constants/protocolConstants.h
|
${CLIENT_ROOT_DIR}/core/utils/constants/protocolConstants.h
|
||||||
${CLIENT_ROOT_DIR}/core/utils/constants/apiKeys.h
|
${CLIENT_ROOT_DIR}/core/utils/constants/apiKeys.h
|
||||||
${CLIENT_ROOT_DIR}/core/utils/constants/apiConstants.h
|
${CLIENT_ROOT_DIR}/core/utils/constants/apiConstants.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/api/apiEnums.h
|
||||||
${CLIENT_ROOT_DIR}/core/utils/errorStrings.h
|
${CLIENT_ROOT_DIR}/core/utils/errorStrings.h
|
||||||
${CLIENT_ROOT_DIR}/core/utils/selfhosted/scriptsRegistry.h
|
${CLIENT_ROOT_DIR}/core/utils/selfhosted/scriptsRegistry.h
|
||||||
${CLIENT_ROOT_DIR}/core/utils/qrCodeUtils.h
|
${CLIENT_ROOT_DIR}/core/utils/qrCodeUtils.h
|
||||||
@@ -35,8 +36,6 @@ set(HEADERS ${HEADERS}
|
|||||||
${CLIENT_ROOT_DIR}/core/installers/torInstaller.h
|
${CLIENT_ROOT_DIR}/core/installers/torInstaller.h
|
||||||
${CLIENT_ROOT_DIR}/core/installers/sftpInstaller.h
|
${CLIENT_ROOT_DIR}/core/installers/sftpInstaller.h
|
||||||
${CLIENT_ROOT_DIR}/core/installers/socks5Installer.h
|
${CLIENT_ROOT_DIR}/core/installers/socks5Installer.h
|
||||||
${CLIENT_ROOT_DIR}/core/installers/mtProxyInstaller.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/installers/telemtInstaller.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/appSplitTunnelingController.h
|
${CLIENT_ROOT_DIR}/core/controllers/appSplitTunnelingController.h
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/ipSplitTunnelingController.h
|
${CLIENT_ROOT_DIR}/core/controllers/ipSplitTunnelingController.h
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/allowedDnsController.h
|
${CLIENT_ROOT_DIR}/core/controllers/allowedDnsController.h
|
||||||
@@ -46,7 +45,6 @@ set(HEADERS ${HEADERS}
|
|||||||
${CLIENT_ROOT_DIR}/core/controllers/api/servicesCatalogController.h
|
${CLIENT_ROOT_DIR}/core/controllers/api/servicesCatalogController.h
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/api/subscriptionController.h
|
${CLIENT_ROOT_DIR}/core/controllers/api/subscriptionController.h
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/api/newsController.h
|
${CLIENT_ROOT_DIR}/core/controllers/api/newsController.h
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/updateController.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/repositories/secureServersRepository.h
|
${CLIENT_ROOT_DIR}/core/repositories/secureServersRepository.h
|
||||||
${CLIENT_ROOT_DIR}/core/repositories/secureAppSettingsRepository.h
|
${CLIENT_ROOT_DIR}/core/repositories/secureAppSettingsRepository.h
|
||||||
${CLIENT_ROOT_DIR}/core/protocols/qmlRegisterProtocols.h
|
${CLIENT_ROOT_DIR}/core/protocols/qmlRegisterProtocols.h
|
||||||
@@ -112,8 +110,6 @@ set(SOURCES ${SOURCES}
|
|||||||
${CLIENT_ROOT_DIR}/core/installers/torInstaller.cpp
|
${CLIENT_ROOT_DIR}/core/installers/torInstaller.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/installers/sftpInstaller.cpp
|
${CLIENT_ROOT_DIR}/core/installers/sftpInstaller.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/installers/socks5Installer.cpp
|
${CLIENT_ROOT_DIR}/core/installers/socks5Installer.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/installers/mtProxyInstaller.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/installers/telemtInstaller.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/appSplitTunnelingController.cpp
|
${CLIENT_ROOT_DIR}/core/controllers/appSplitTunnelingController.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/ipSplitTunnelingController.cpp
|
${CLIENT_ROOT_DIR}/core/controllers/ipSplitTunnelingController.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/allowedDnsController.cpp
|
${CLIENT_ROOT_DIR}/core/controllers/allowedDnsController.cpp
|
||||||
@@ -123,7 +119,6 @@ set(SOURCES ${SOURCES}
|
|||||||
${CLIENT_ROOT_DIR}/core/controllers/api/servicesCatalogController.cpp
|
${CLIENT_ROOT_DIR}/core/controllers/api/servicesCatalogController.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/api/subscriptionController.cpp
|
${CLIENT_ROOT_DIR}/core/controllers/api/subscriptionController.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/api/newsController.cpp
|
${CLIENT_ROOT_DIR}/core/controllers/api/newsController.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/updateController.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/repositories/secureServersRepository.cpp
|
${CLIENT_ROOT_DIR}/core/repositories/secureServersRepository.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/repositories/secureAppSettingsRepository.cpp
|
${CLIENT_ROOT_DIR}/core/repositories/secureAppSettingsRepository.cpp
|
||||||
${CLIENT_ROOT_DIR}/ui/utils/qAutoStart.cpp
|
${CLIENT_ROOT_DIR}/ui/utils/qAutoStart.cpp
|
||||||
@@ -141,7 +136,6 @@ set(SOURCES ${SOURCES}
|
|||||||
${CLIENT_ROOT_DIR}/../common/logger/logger.cpp
|
${CLIENT_ROOT_DIR}/../common/logger/logger.cpp
|
||||||
${CLIENT_ROOT_DIR}/ui/utils/qmlUtils.cpp
|
${CLIENT_ROOT_DIR}/ui/utils/qmlUtils.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/utils/api/apiUtils.cpp
|
${CLIENT_ROOT_DIR}/core/utils/api/apiUtils.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/utils/serverConfigUtils.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/utils/osSignalHandler.cpp
|
${CLIENT_ROOT_DIR}/core/utils/osSignalHandler.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/utils/utilities.cpp
|
${CLIENT_ROOT_DIR}/core/utils/utilities.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/utils/managementServer.cpp
|
${CLIENT_ROOT_DIR}/core/utils/managementServer.cpp
|
||||||
@@ -205,14 +199,12 @@ file(GLOB UI_MODELS_H CONFIGURE_DEPENDS
|
|||||||
${CLIENT_ROOT_DIR}/ui/models/*.h
|
${CLIENT_ROOT_DIR}/ui/models/*.h
|
||||||
${CLIENT_ROOT_DIR}/ui/models/protocols/*.h
|
${CLIENT_ROOT_DIR}/ui/models/protocols/*.h
|
||||||
${CLIENT_ROOT_DIR}/ui/models/services/*.h
|
${CLIENT_ROOT_DIR}/ui/models/services/*.h
|
||||||
${CLIENT_ROOT_DIR}/ui/models/utils/*.h
|
|
||||||
${CLIENT_ROOT_DIR}/ui/models/api/*.h
|
${CLIENT_ROOT_DIR}/ui/models/api/*.h
|
||||||
)
|
)
|
||||||
file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS
|
file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS
|
||||||
${CLIENT_ROOT_DIR}/ui/models/*.cpp
|
${CLIENT_ROOT_DIR}/ui/models/*.cpp
|
||||||
${CLIENT_ROOT_DIR}/ui/models/protocols/*.cpp
|
${CLIENT_ROOT_DIR}/ui/models/protocols/*.cpp
|
||||||
${CLIENT_ROOT_DIR}/ui/models/services/*.cpp
|
${CLIENT_ROOT_DIR}/ui/models/services/*.cpp
|
||||||
${CLIENT_ROOT_DIR}/ui/models/utils/*.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/ui/models/api/*.cpp
|
${CLIENT_ROOT_DIR}/ui/models/api/*.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
|
|||||||
connData.clientId = Utils::getRandomString(32);
|
connData.clientId = Utils::getRandomString(32);
|
||||||
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int nVersion = 0;
|
int nVersion = 1;
|
||||||
|
|
||||||
QByteArray clientIdUtf8 = connData.clientId.toUtf8();
|
QByteArray clientIdUtf8 = connData.clientId.toUtf8();
|
||||||
|
|
||||||
|
|||||||
@@ -21,122 +21,13 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
Logger logger("XrayConfigurator");
|
Logger logger("XrayConfigurator");
|
||||||
|
|
||||||
QString normalizeXhttpMode(const QString &m) {
|
|
||||||
const QString t = m.trimmed();
|
|
||||||
if (t.isEmpty() || t.compare(QLatin1String("Auto"), Qt::CaseInsensitive) == 0) {
|
|
||||||
return QStringLiteral("auto");
|
|
||||||
}
|
}
|
||||||
if (t.compare(QLatin1String("Packet-up"), Qt::CaseInsensitive) == 0)
|
|
||||||
return QStringLiteral("packet-up");
|
|
||||||
if (t.compare(QLatin1String("Stream-up"), Qt::CaseInsensitive) == 0)
|
|
||||||
return QStringLiteral("stream-up");
|
|
||||||
if (t.compare(QLatin1String("Stream-one"), Qt::CaseInsensitive) == 0)
|
|
||||||
return QStringLiteral("stream-one");
|
|
||||||
return t.toLower();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Xray-core: empty → path; "None" in UI → omit (core default path)
|
|
||||||
QString normalizeSessionSeqPlacement(const QString &p)
|
|
||||||
{
|
|
||||||
if (p.isEmpty() || p.compare(QLatin1String("None"), Qt::CaseInsensitive) == 0)
|
|
||||||
return {};
|
|
||||||
return p.toLower();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString normalizeUplinkDataPlacement(const QString &p)
|
|
||||||
{
|
|
||||||
if (p.isEmpty() || p.compare(QLatin1String("Body"), Qt::CaseInsensitive) == 0)
|
|
||||||
return QStringLiteral("body");
|
|
||||||
if (p.compare(QLatin1String("Auto"), Qt::CaseInsensitive) == 0)
|
|
||||||
return QStringLiteral("auto");
|
|
||||||
if (p.compare(QLatin1String("Query"), Qt::CaseInsensitive) == 0)
|
|
||||||
// "Query" is not valid for uplink payload in splithttp; closest documented mode
|
|
||||||
return QStringLiteral("header");
|
|
||||||
return p.toLower();
|
|
||||||
}
|
|
||||||
|
|
||||||
// splithttp: cookie | header | query | queryInHeader (not "body")
|
|
||||||
QString normalizeXPaddingPlacement(const QString &p)
|
|
||||||
{
|
|
||||||
QString t = p.trimmed();
|
|
||||||
if (t.isEmpty())
|
|
||||||
return QString::fromLatin1(amnezia::protocols::xray::defaultXPaddingPlacement).toLower();
|
|
||||||
if (t.compare(QLatin1String("Body"), Qt::CaseInsensitive) == 0)
|
|
||||||
return QStringLiteral("queryInHeader");
|
|
||||||
if (t.contains(QLatin1String("queryInHeader"), Qt::CaseInsensitive)
|
|
||||||
|| t.compare(QLatin1String("Query in header"), Qt::CaseInsensitive) == 0)
|
|
||||||
return QStringLiteral("queryInHeader");
|
|
||||||
return t.toLower();
|
|
||||||
}
|
|
||||||
|
|
||||||
// splithttp: repeat-x | tokenish
|
|
||||||
QString normalizeXPaddingMethod(const QString &m)
|
|
||||||
{
|
|
||||||
QString t = m.trimmed();
|
|
||||||
if (t.isEmpty() || t.compare(QLatin1String("Repeat-x"), Qt::CaseInsensitive) == 0)
|
|
||||||
return QStringLiteral("repeat-x");
|
|
||||||
if (t.compare(QLatin1String("Tokenish"), Qt::CaseInsensitive) == 0)
|
|
||||||
return QStringLiteral("tokenish");
|
|
||||||
if (t.compare(QLatin1String("Random"), Qt::CaseInsensitive) == 0
|
|
||||||
|| t.compare(QLatin1String("Zero"), Qt::CaseInsensitive) == 0)
|
|
||||||
return QStringLiteral("repeat-x");
|
|
||||||
return t.toLower();
|
|
||||||
}
|
|
||||||
|
|
||||||
void putIntRangeIfAny(QJsonObject &obj, const char *key, QString minV, QString maxV, const char *fallbackMin,
|
|
||||||
const char *fallbackMax)
|
|
||||||
{
|
|
||||||
if (minV.isEmpty() && maxV.isEmpty())
|
|
||||||
return;
|
|
||||||
if (minV.isEmpty())
|
|
||||||
minV = QString::fromLatin1(fallbackMin);
|
|
||||||
if (maxV.isEmpty())
|
|
||||||
maxV = QString::fromLatin1(fallbackMax);
|
|
||||||
QJsonObject r;
|
|
||||||
r[QStringLiteral("from")] = minV.toInt();
|
|
||||||
r[QStringLiteral("to")] = maxV.toInt();
|
|
||||||
obj[QString::fromUtf8(key)] = r;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Desktop applies this in XrayProtocol::start(); iOS/Android pass JSON straight to libxray — same fixes here.
|
|
||||||
void sanitizeXrayNativeConfig(amnezia::ProtocolConfig &pc)
|
|
||||||
{
|
|
||||||
QString c = pc.nativeConfig();
|
|
||||||
if (c.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bool changed = false;
|
|
||||||
if (c.contains(QLatin1String("Mozilla/5.0"), Qt::CaseInsensitive)) {
|
|
||||||
c.replace(QLatin1String("Mozilla/5.0"), QString::fromLatin1(amnezia::protocols::xray::defaultFingerprint),
|
|
||||||
Qt::CaseInsensitive);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
const QString legacyListen = QString::fromLatin1(amnezia::protocols::xray::defaultLocalAddr);
|
|
||||||
const QString listenOk = QString::fromLatin1(amnezia::protocols::xray::defaultLocalListenAddr);
|
|
||||||
if (c.contains(legacyListen)) {
|
|
||||||
c.replace(legacyListen, listenOk);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if (changed) {
|
|
||||||
pc.setNativeConfig(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
XrayConfigurator::XrayConfigurator(SshSession* sshSession, QObject *parent)
|
XrayConfigurator::XrayConfigurator(SshSession* sshSession, QObject *parent)
|
||||||
: ConfiguratorBase(sshSession, parent)
|
: ConfiguratorBase(sshSession, parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
amnezia::ProtocolConfig XrayConfigurator::processConfigWithLocalSettings(const amnezia::ConnectionSettings &settings,
|
|
||||||
amnezia::ProtocolConfig protocolConfig)
|
|
||||||
{
|
|
||||||
applyDnsToNativeConfig(settings.dns, protocolConfig);
|
|
||||||
sanitizeXrayNativeConfig(protocolConfig);
|
|
||||||
return protocolConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentials, DockerContainer container,
|
QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
const ContainerConfig &containerConfig,
|
const ContainerConfig &containerConfig,
|
||||||
const DnsSettings &dnsSettings,
|
const DnsSettings &dnsSettings,
|
||||||
@@ -145,14 +36,6 @@ QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentia
|
|||||||
// Generate new UUID for client
|
// Generate new UUID for client
|
||||||
QString clientId = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
QString clientId = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
||||||
|
|
||||||
// Get flow value from settings (default xtls-rprx-vision)
|
|
||||||
QString flowValue = "xtls-rprx-vision";
|
|
||||||
if (const auto *xrayCfg = containerConfig.protocolConfig.as<XrayProtocolConfig>()) {
|
|
||||||
if (!xrayCfg->serverConfig.flow.isEmpty()) {
|
|
||||||
flowValue = xrayCfg->serverConfig.flow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current server config
|
// Get current server config
|
||||||
QString currentConfig = m_sshSession->getTextFileFromContainer(
|
QString currentConfig = m_sshSession->getTextFileFromContainer(
|
||||||
container, credentials, amnezia::protocols::xray::serverConfigPath, errorCode);
|
container, credentials, amnezia::protocols::xray::serverConfigPath, errorCode);
|
||||||
@@ -205,11 +88,8 @@ QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentia
|
|||||||
// Create configuration for new client
|
// Create configuration for new client
|
||||||
QJsonObject clientConfig {
|
QJsonObject clientConfig {
|
||||||
{amnezia::protocols::xray::id, clientId},
|
{amnezia::protocols::xray::id, clientId},
|
||||||
|
{amnezia::protocols::xray::flow, "xtls-rprx-vision"}
|
||||||
};
|
};
|
||||||
clientConfig[amnezia::protocols::xray::id] = clientId;
|
|
||||||
if (!flowValue.isEmpty()) {
|
|
||||||
clientConfig[amnezia::protocols::xray::flow] = flowValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
clients.append(clientConfig);
|
clients.append(clientConfig);
|
||||||
|
|
||||||
@@ -248,283 +128,72 @@ QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentia
|
|||||||
return clientId;
|
return clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject XrayConfigurator::buildStreamSettings(const XrayServerConfig &srv, const QString &clientId) const
|
|
||||||
{
|
|
||||||
QJsonObject streamSettings;
|
|
||||||
const auto &xhttp = srv.xhttp;
|
|
||||||
const auto &mkcp = srv.mkcp;
|
|
||||||
namespace px = amnezia::protocols::xray;
|
|
||||||
|
|
||||||
QString networkValue = QStringLiteral("tcp");
|
|
||||||
if (srv.transport == QLatin1String("xhttp"))
|
|
||||||
networkValue = QStringLiteral("xhttp");
|
|
||||||
else if (srv.transport == QLatin1String("mkcp"))
|
|
||||||
networkValue = QStringLiteral("kcp");
|
|
||||||
streamSettings[px::network] = networkValue;
|
|
||||||
|
|
||||||
streamSettings[px::security] = srv.security;
|
|
||||||
|
|
||||||
if (srv.security == QLatin1String("tls")) {
|
|
||||||
QJsonObject tlsSettings;
|
|
||||||
const QString sniEff = srv.sni.isEmpty() ? QString::fromLatin1(px::defaultSni) : srv.sni;
|
|
||||||
tlsSettings[px::serverName] = sniEff;
|
|
||||||
const QString alpnEff = srv.alpn.isEmpty() ? QString::fromLatin1(px::defaultAlpn) : srv.alpn;
|
|
||||||
QJsonArray alpnArray;
|
|
||||||
for (const QString &a : alpnEff.split(QLatin1Char(','))) {
|
|
||||||
const QString t = a.trimmed();
|
|
||||||
if (!t.isEmpty())
|
|
||||||
alpnArray.append(t);
|
|
||||||
}
|
|
||||||
if (!alpnArray.isEmpty())
|
|
||||||
tlsSettings[QStringLiteral("alpn")] = alpnArray;
|
|
||||||
const QString fpEff = srv.fingerprint.isEmpty() ? QString::fromLatin1(px::defaultFingerprint) : srv.fingerprint;
|
|
||||||
tlsSettings[px::fingerprint] = fpEff;
|
|
||||||
streamSettings[QStringLiteral("tlsSettings")] = tlsSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (srv.security == QLatin1String("reality")) {
|
|
||||||
QJsonObject realSettings;
|
|
||||||
const QString fpEff = srv.fingerprint.isEmpty() ? QString::fromLatin1(px::defaultFingerprint) : srv.fingerprint;
|
|
||||||
realSettings[px::fingerprint] = fpEff;
|
|
||||||
const QString sniEff = srv.sni.isEmpty() ? QString::fromLatin1(px::defaultSni) : srv.sni;
|
|
||||||
realSettings[px::serverName] = sniEff;
|
|
||||||
streamSettings[px::realitySettings] = realSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
// XHTTP — JSON must match Xray-core SplitHTTPConfig (flat xPadding fields, see transport_internet.go)
|
|
||||||
if (srv.transport == QLatin1String("xhttp")) {
|
|
||||||
QJsonObject xo;
|
|
||||||
const QString hostEff = xhttp.host.isEmpty() ? QString::fromLatin1(px::defaultXhttpHost) : xhttp.host;
|
|
||||||
xo[QStringLiteral("host")] = hostEff;
|
|
||||||
if (!xhttp.path.isEmpty())
|
|
||||||
xo[QStringLiteral("path")] = xhttp.path;
|
|
||||||
xo[QStringLiteral("mode")] = normalizeXhttpMode(xhttp.mode);
|
|
||||||
|
|
||||||
if (xhttp.headersTemplate.compare(QLatin1String("HTTP"), Qt::CaseInsensitive) == 0) {
|
|
||||||
QJsonObject headers;
|
|
||||||
headers[QStringLiteral("Host")] = hostEff;
|
|
||||||
xo[QStringLiteral("headers")] = headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString methodEff =
|
|
||||||
xhttp.uplinkMethod.isEmpty() ? QString::fromLatin1(px::defaultXhttpUplinkMethod) : xhttp.uplinkMethod;
|
|
||||||
xo[QStringLiteral("uplinkHTTPMethod")] = methodEff.toUpper();
|
|
||||||
|
|
||||||
xo[QStringLiteral("noGRPCHeader")] = xhttp.disableGrpc;
|
|
||||||
xo[QStringLiteral("noSSEHeader")] = xhttp.disableSse;
|
|
||||||
|
|
||||||
const QString sessPl = normalizeSessionSeqPlacement(xhttp.sessionPlacement);
|
|
||||||
if (!sessPl.isEmpty())
|
|
||||||
xo[QStringLiteral("sessionPlacement")] = sessPl;
|
|
||||||
const QString seqPl = normalizeSessionSeqPlacement(xhttp.seqPlacement);
|
|
||||||
if (!seqPl.isEmpty())
|
|
||||||
xo[QStringLiteral("seqPlacement")] = seqPl;
|
|
||||||
if (!xhttp.sessionKey.isEmpty())
|
|
||||||
xo[QStringLiteral("sessionKey")] = xhttp.sessionKey;
|
|
||||||
if (!xhttp.seqKey.isEmpty())
|
|
||||||
xo[QStringLiteral("seqKey")] = xhttp.seqKey;
|
|
||||||
|
|
||||||
xo[QStringLiteral("uplinkDataPlacement")] = normalizeUplinkDataPlacement(xhttp.uplinkDataPlacement);
|
|
||||||
if (!xhttp.uplinkDataKey.isEmpty())
|
|
||||||
xo[QStringLiteral("uplinkDataKey")] = xhttp.uplinkDataKey;
|
|
||||||
|
|
||||||
const QString ucs = xhttp.uplinkChunkSize.isEmpty() ? QString::fromLatin1(px::defaultXhttpUplinkChunkSize)
|
|
||||||
: xhttp.uplinkChunkSize;
|
|
||||||
if (!ucs.isEmpty() && ucs != QLatin1String("0")) {
|
|
||||||
const int v = ucs.toInt();
|
|
||||||
QJsonObject chunkR;
|
|
||||||
chunkR[QStringLiteral("from")] = v;
|
|
||||||
chunkR[QStringLiteral("to")] = v;
|
|
||||||
xo[QStringLiteral("uplinkChunkSize")] = chunkR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!xhttp.scMaxBufferedPosts.isEmpty())
|
|
||||||
xo[QStringLiteral("scMaxBufferedPosts")] = xhttp.scMaxBufferedPosts.toLongLong();
|
|
||||||
|
|
||||||
putIntRangeIfAny(xo, "scMaxEachPostBytes", xhttp.scMaxEachPostBytesMin, xhttp.scMaxEachPostBytesMax,
|
|
||||||
px::defaultXhttpScMaxEachPostBytesMin, px::defaultXhttpScMaxEachPostBytesMax);
|
|
||||||
putIntRangeIfAny(xo, "scMinPostsIntervalMs", xhttp.scMinPostsIntervalMsMin, xhttp.scMinPostsIntervalMsMax,
|
|
||||||
px::defaultXhttpScMinPostsIntervalMsMin, px::defaultXhttpScMinPostsIntervalMsMax);
|
|
||||||
putIntRangeIfAny(xo, "scStreamUpServerSecs", xhttp.scStreamUpServerSecsMin, xhttp.scStreamUpServerSecsMax,
|
|
||||||
px::defaultXhttpScStreamUpServerSecsMin, px::defaultXhttpScStreamUpServerSecsMax);
|
|
||||||
|
|
||||||
const auto &pad = xhttp.xPadding;
|
|
||||||
xo[QStringLiteral("xPaddingObfsMode")] = pad.obfsMode;
|
|
||||||
if (pad.obfsMode) {
|
|
||||||
if (!pad.bytesMin.isEmpty() || !pad.bytesMax.isEmpty()) {
|
|
||||||
QJsonObject br;
|
|
||||||
br[QStringLiteral("from")] = pad.bytesMin.isEmpty() ? 1 : pad.bytesMin.toInt();
|
|
||||||
br[QStringLiteral("to")] = pad.bytesMax.isEmpty() ? (pad.bytesMin.isEmpty() ? 256 : pad.bytesMin.toInt())
|
|
||||||
: pad.bytesMax.toInt();
|
|
||||||
xo[QStringLiteral("xPaddingBytes")] = br;
|
|
||||||
}
|
|
||||||
xo[QStringLiteral("xPaddingKey")] = pad.key.isEmpty() ? QStringLiteral("x_padding") : pad.key;
|
|
||||||
xo[QStringLiteral("xPaddingHeader")] = pad.header.isEmpty() ? QStringLiteral("X-Padding") : pad.header;
|
|
||||||
xo[QStringLiteral("xPaddingPlacement")] = normalizeXPaddingPlacement(
|
|
||||||
pad.placement.isEmpty() ? QString::fromLatin1(px::defaultXPaddingPlacement) : pad.placement);
|
|
||||||
xo[QStringLiteral("xPaddingMethod")] = normalizeXPaddingMethod(
|
|
||||||
pad.method.isEmpty() ? QString::fromLatin1(px::defaultXPaddingMethod) : pad.method);
|
|
||||||
}
|
|
||||||
|
|
||||||
// xmux: Xray has no "enabled" flag; omit object when UI disables multiplex tuning.
|
|
||||||
if (xhttp.xmux.enabled) {
|
|
||||||
QJsonObject mux;
|
|
||||||
auto addMuxRange = [&](const char *key, const QString &a, const QString &b) {
|
|
||||||
if (a.isEmpty() && b.isEmpty())
|
|
||||||
return;
|
|
||||||
QJsonObject r;
|
|
||||||
r[QStringLiteral("from")] = a.isEmpty() ? 0 : a.toInt();
|
|
||||||
r[QStringLiteral("to")] = b.isEmpty() ? 0 : b.toInt();
|
|
||||||
mux[QString::fromUtf8(key)] = r;
|
|
||||||
};
|
|
||||||
addMuxRange("maxConcurrency", xhttp.xmux.maxConcurrencyMin, xhttp.xmux.maxConcurrencyMax);
|
|
||||||
addMuxRange("maxConnections", xhttp.xmux.maxConnectionsMin, xhttp.xmux.maxConnectionsMax);
|
|
||||||
addMuxRange("cMaxReuseTimes", xhttp.xmux.cMaxReuseTimesMin, xhttp.xmux.cMaxReuseTimesMax);
|
|
||||||
addMuxRange("hMaxRequestTimes", xhttp.xmux.hMaxRequestTimesMin, xhttp.xmux.hMaxRequestTimesMax);
|
|
||||||
addMuxRange("hMaxReusableSecs", xhttp.xmux.hMaxReusableSecsMin, xhttp.xmux.hMaxReusableSecsMax);
|
|
||||||
if (!xhttp.xmux.hKeepAlivePeriod.isEmpty())
|
|
||||||
mux[QStringLiteral("hKeepAlivePeriod")] = xhttp.xmux.hKeepAlivePeriod.toLongLong();
|
|
||||||
if (!mux.isEmpty())
|
|
||||||
xo[QStringLiteral("xmux")] = mux;
|
|
||||||
}
|
|
||||||
|
|
||||||
streamSettings[QStringLiteral("xhttpSettings")] = xo;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (srv.transport == QLatin1String("mkcp")) {
|
|
||||||
QJsonObject kcpObj;
|
|
||||||
const QString ttiEff = mkcp.tti.isEmpty() ? QString::fromLatin1(px::defaultMkcpTti) : mkcp.tti;
|
|
||||||
const QString upEff = mkcp.uplinkCapacity.isEmpty() ? QString::fromLatin1(px::defaultMkcpUplinkCapacity)
|
|
||||||
: mkcp.uplinkCapacity;
|
|
||||||
const QString downEff = mkcp.downlinkCapacity.isEmpty() ? QString::fromLatin1(px::defaultMkcpDownlinkCapacity)
|
|
||||||
: mkcp.downlinkCapacity;
|
|
||||||
const QString rbufEff = mkcp.readBufferSize.isEmpty() ? QString::fromLatin1(px::defaultMkcpReadBufferSize)
|
|
||||||
: mkcp.readBufferSize;
|
|
||||||
const QString wbufEff = mkcp.writeBufferSize.isEmpty() ? QString::fromLatin1(px::defaultMkcpWriteBufferSize)
|
|
||||||
: mkcp.writeBufferSize;
|
|
||||||
kcpObj[QStringLiteral("tti")] = ttiEff.toInt();
|
|
||||||
kcpObj[QStringLiteral("uplinkCapacity")] = upEff.toInt();
|
|
||||||
kcpObj[QStringLiteral("downlinkCapacity")] = downEff.toInt();
|
|
||||||
kcpObj[QStringLiteral("readBufferSize")] = rbufEff.toInt();
|
|
||||||
kcpObj[QStringLiteral("writeBufferSize")] = wbufEff.toInt();
|
|
||||||
kcpObj[QStringLiteral("congestion")] = mkcp.congestion;
|
|
||||||
streamSettings[QStringLiteral("kcpSettings")] = kcpObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
return streamSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProtocolConfig XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
ProtocolConfig XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
const ContainerConfig &containerConfig,
|
const ContainerConfig &containerConfig,
|
||||||
const DnsSettings &dnsSettings,
|
const DnsSettings &dnsSettings,
|
||||||
ErrorCode &errorCode)
|
ErrorCode &errorCode)
|
||||||
{
|
{
|
||||||
const XrayServerConfig* serverConfig = nullptr;
|
const XrayServerConfig* serverConfig = nullptr;
|
||||||
if (const auto *xrayCfg = containerConfig.protocolConfig.as<XrayProtocolConfig>()) {
|
if (auto* xrayConfig = containerConfig.protocolConfig.as<XrayProtocolConfig>()) {
|
||||||
serverConfig = &xrayCfg->serverConfig;
|
serverConfig = &xrayConfig->serverConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!serverConfig) {
|
|
||||||
logger.error() << "No XrayProtocolConfig found";
|
|
||||||
errorCode = ErrorCode::InternalError;
|
|
||||||
return XrayProtocolConfig{};
|
|
||||||
}
|
|
||||||
|
|
||||||
const XrayServerConfig &srv = *serverConfig;
|
|
||||||
|
|
||||||
QString xrayClientId = prepareServerConfig(credentials, container, containerConfig, dnsSettings, errorCode);
|
QString xrayClientId = prepareServerConfig(credentials, container, containerConfig, dnsSettings, errorCode);
|
||||||
if (errorCode != ErrorCode::NoError || xrayClientId.isEmpty()) {
|
if (errorCode != ErrorCode::NoError || xrayClientId.isEmpty()) {
|
||||||
logger.error() << "Failed to prepare server config";
|
logger.error() << "Failed to prepare server config";
|
||||||
if (errorCode == ErrorCode::NoError) {
|
|
||||||
errorCode = ErrorCode::InternalError;
|
errorCode = ErrorCode::InternalError;
|
||||||
}
|
|
||||||
return XrayProtocolConfig{};
|
return XrayProtocolConfig{};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch server keys (Reality only)
|
amnezia::ScriptVars vars = amnezia::genBaseVars(credentials, container, dnsSettings.primaryDns, dnsSettings.secondaryDns);
|
||||||
QString xrayPublicKey;
|
vars.append(amnezia::genProtocolVarsForContainer(container, containerConfig));
|
||||||
QString xrayShortId;
|
QString config = m_sshSession->replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container), vars);
|
||||||
|
|
||||||
if (srv.security == "reality") {
|
if (config.isEmpty()) {
|
||||||
xrayPublicKey = m_sshSession->getTextFileFromContainer(container, credentials,
|
logger.error() << "Failed to get config template";
|
||||||
amnezia::protocols::xray::PublicKeyPath, errorCode);
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return XrayProtocolConfig{};
|
||||||
|
}
|
||||||
|
|
||||||
|
QString xrayPublicKey =
|
||||||
|
m_sshSession->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::PublicKeyPath, errorCode);
|
||||||
if (errorCode != ErrorCode::NoError || xrayPublicKey.isEmpty()) {
|
if (errorCode != ErrorCode::NoError || xrayPublicKey.isEmpty()) {
|
||||||
logger.error() << "Failed to get public key";
|
logger.error() << "Failed to get public key";
|
||||||
if (errorCode == ErrorCode::NoError) {
|
|
||||||
errorCode = ErrorCode::InternalError;
|
errorCode = ErrorCode::InternalError;
|
||||||
}
|
|
||||||
return XrayProtocolConfig{};
|
return XrayProtocolConfig{};
|
||||||
}
|
}
|
||||||
xrayPublicKey.replace("\n", "");
|
xrayPublicKey.replace("\n", "");
|
||||||
|
|
||||||
xrayShortId = m_sshSession->getTextFileFromContainer(container, credentials,
|
QString xrayShortId =
|
||||||
amnezia::protocols::xray::shortidPath, errorCode);
|
m_sshSession->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::shortidPath, errorCode);
|
||||||
if (errorCode != ErrorCode::NoError || xrayShortId.isEmpty()) {
|
if (errorCode != ErrorCode::NoError || xrayShortId.isEmpty()) {
|
||||||
logger.error() << "Failed to get short ID";
|
logger.error() << "Failed to get short ID";
|
||||||
if (errorCode == ErrorCode::NoError) {
|
|
||||||
errorCode = ErrorCode::InternalError;
|
errorCode = ErrorCode::InternalError;
|
||||||
}
|
|
||||||
return XrayProtocolConfig{};
|
return XrayProtocolConfig{};
|
||||||
}
|
}
|
||||||
xrayShortId.replace("\n", "");
|
xrayShortId.replace("\n", "");
|
||||||
|
|
||||||
|
if (!config.contains("$XRAY_CLIENT_ID") || !config.contains("$XRAY_PUBLIC_KEY") || !config.contains("$XRAY_SHORT_ID")) {
|
||||||
|
logger.error() << "Config template missing required variables:"
|
||||||
|
<< "XRAY_CLIENT_ID:" << !config.contains("$XRAY_CLIENT_ID")
|
||||||
|
<< "XRAY_PUBLIC_KEY:" << !config.contains("$XRAY_PUBLIC_KEY")
|
||||||
|
<< "XRAY_SHORT_ID:" << !config.contains("$XRAY_SHORT_ID");
|
||||||
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return XrayProtocolConfig{};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build outbound
|
config.replace("$XRAY_CLIENT_ID", xrayClientId);
|
||||||
QJsonObject userObj;
|
config.replace("$XRAY_PUBLIC_KEY", xrayPublicKey);
|
||||||
userObj[amnezia::protocols::xray::id] = xrayClientId;
|
config.replace("$XRAY_SHORT_ID", xrayShortId);
|
||||||
userObj[amnezia::protocols::xray::encryption] = "none";
|
|
||||||
if (!srv.flow.isEmpty()) {
|
|
||||||
userObj[amnezia::protocols::xray::flow] = srv.flow;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject vnextEntry;
|
|
||||||
vnextEntry[amnezia::protocols::xray::address] = credentials.hostName;
|
|
||||||
vnextEntry[amnezia::protocols::xray::port] = srv.port.toInt();
|
|
||||||
vnextEntry[amnezia::protocols::xray::users] = QJsonArray { userObj };
|
|
||||||
|
|
||||||
QJsonObject outboundSettings;
|
|
||||||
outboundSettings[amnezia::protocols::xray::vnext] = QJsonArray { vnextEntry };
|
|
||||||
|
|
||||||
QJsonObject outbound;
|
|
||||||
outbound["protocol"] = "vless";
|
|
||||||
outbound[amnezia::protocols::xray::settings] = outboundSettings;
|
|
||||||
|
|
||||||
// Build streamSettings
|
|
||||||
QJsonObject streamObj = buildStreamSettings(srv, xrayClientId);
|
|
||||||
|
|
||||||
// Inject Reality keys
|
|
||||||
if (srv.security == "reality") {
|
|
||||||
QJsonObject rs = streamObj[amnezia::protocols::xray::realitySettings].toObject();
|
|
||||||
rs[amnezia::protocols::xray::publicKey] = xrayPublicKey;
|
|
||||||
rs[amnezia::protocols::xray::shortId] = xrayShortId;
|
|
||||||
rs[amnezia::protocols::xray::spiderX] = "";
|
|
||||||
streamObj[amnezia::protocols::xray::realitySettings] = rs;
|
|
||||||
}
|
|
||||||
|
|
||||||
outbound[amnezia::protocols::xray::streamSettings] = streamObj;
|
|
||||||
|
|
||||||
// Build full client config
|
|
||||||
QJsonObject inboundObj;
|
|
||||||
inboundObj["listen"] = amnezia::protocols::xray::defaultLocalListenAddr;
|
|
||||||
inboundObj[amnezia::protocols::xray::port] = amnezia::protocols::xray::defaultLocalProxyPort;
|
|
||||||
inboundObj["protocol"] = "socks";
|
|
||||||
inboundObj[amnezia::protocols::xray::settings] = QJsonObject { { "udp", true } };
|
|
||||||
|
|
||||||
QJsonObject clientJson;
|
|
||||||
clientJson["log"] = QJsonObject { { "loglevel", "error" } };
|
|
||||||
clientJson[amnezia::protocols::xray::inbounds] = QJsonArray { inboundObj };
|
|
||||||
clientJson[amnezia::protocols::xray::outbounds] = QJsonArray { outbound };
|
|
||||||
|
|
||||||
QString config = QString::fromUtf8(QJsonDocument(clientJson).toJson(QJsonDocument::Compact));
|
|
||||||
|
|
||||||
// Return
|
|
||||||
XrayProtocolConfig protocolConfig;
|
XrayProtocolConfig protocolConfig;
|
||||||
protocolConfig.serverConfig = srv;
|
if (serverConfig) {
|
||||||
|
protocolConfig.serverConfig = *serverConfig;
|
||||||
|
}
|
||||||
|
|
||||||
XrayClientConfig clientConfig;
|
XrayClientConfig clientConfig;
|
||||||
clientConfig.nativeConfig = config;
|
clientConfig.nativeConfig = config;
|
||||||
qDebug() << "config:" << config;
|
clientConfig.localPort = "";
|
||||||
clientConfig.localPort = QString(amnezia::protocols::xray::defaultLocalProxyPort);
|
|
||||||
clientConfig.id = xrayClientId;
|
clientConfig.id = xrayClientId;
|
||||||
|
|
||||||
protocolConfig.setClientConfig(clientConfig);
|
protocolConfig.setClientConfig(clientConfig);
|
||||||
|
|||||||
@@ -2,13 +2,11 @@
|
|||||||
#define XRAY_CONFIGURATOR_H
|
#define XRAY_CONFIGURATOR_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QJsonObject>
|
|
||||||
|
|
||||||
#include "configuratorBase.h"
|
#include "configuratorBase.h"
|
||||||
#include "core/utils/errorCodes.h"
|
#include "core/utils/errorCodes.h"
|
||||||
#include "core/utils/routeModes.h"
|
#include "core/utils/routeModes.h"
|
||||||
#include "core/utils/commonStructs.h"
|
#include "core/utils/commonStructs.h"
|
||||||
#include "core/models/protocols/xrayProtocolConfig.h"
|
|
||||||
|
|
||||||
class XrayConfigurator : public ConfiguratorBase
|
class XrayConfigurator : public ConfiguratorBase
|
||||||
{
|
{
|
||||||
@@ -20,17 +18,10 @@ public:
|
|||||||
const amnezia::DnsSettings &dnsSettings,
|
const amnezia::DnsSettings &dnsSettings,
|
||||||
amnezia::ErrorCode &errorCode) override;
|
amnezia::ErrorCode &errorCode) override;
|
||||||
|
|
||||||
amnezia::ProtocolConfig processConfigWithLocalSettings(const amnezia::ConnectionSettings &settings,
|
|
||||||
amnezia::ProtocolConfig protocolConfig) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString prepareServerConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container, const amnezia::ContainerConfig &containerConfig,
|
QString prepareServerConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container, const amnezia::ContainerConfig &containerConfig,
|
||||||
const amnezia::DnsSettings &dnsSettings,
|
const amnezia::DnsSettings &dnsSettings,
|
||||||
amnezia::ErrorCode &errorCode);
|
amnezia::ErrorCode &errorCode);
|
||||||
|
|
||||||
// Builds the native xray "streamSettings" JSON object from XrayServerConfig
|
|
||||||
QJsonObject buildStreamSettings(const amnezia::XrayServerConfig &srv,
|
|
||||||
const QString &clientId) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // XRAY_CONFIGURATOR_H
|
#endif // XRAY_CONFIGURATOR_H
|
||||||
|
|||||||
@@ -1,75 +1,32 @@
|
|||||||
#include "newsController.h"
|
#include "newsController.h"
|
||||||
|
|
||||||
#include "core/controllers/gatewayController.h"
|
#include "core/controllers/gatewayController.h"
|
||||||
#include "core/repositories/secureServersRepository.h"
|
#include "core/utils/api/apiEnums.h"
|
||||||
#include "core/utils/constants/apiKeys.h"
|
#include "core/utils/constants/apiKeys.h"
|
||||||
#include "core/utils/constants/apiConstants.h"
|
#include "core/utils/constants/apiConstants.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
#include <QtConcurrent/QtConcurrent>
|
#include <QtConcurrent/QtConcurrent>
|
||||||
#include <QJsonArray>
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QSet>
|
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
|
|
||||||
NewsController::NewsController(SecureAppSettingsRepository* appSettingsRepository,
|
NewsController::NewsController(SecureAppSettingsRepository* appSettingsRepository,
|
||||||
SecureServersRepository *serversRepository)
|
ServersController* serversController)
|
||||||
: m_appSettingsRepository(appSettingsRepository),
|
: m_appSettingsRepository(appSettingsRepository), m_serversController(serversController)
|
||||||
m_serversRepository(serversRepository)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject NewsController::getServicesList() const
|
|
||||||
{
|
|
||||||
if (!m_serversRepository) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
QSet<QString> userCountryCodes;
|
|
||||||
QSet<QString> serviceTypes;
|
|
||||||
const QVector<QString> ids = m_serversRepository->orderedServerIds();
|
|
||||||
for (const QString &id : ids) {
|
|
||||||
const auto apiV2 = m_serversRepository->apiV2Config(id);
|
|
||||||
if (!apiV2.has_value()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!apiV2->apiConfig.userCountryCode.isEmpty()) {
|
|
||||||
userCountryCodes.insert(apiV2->apiConfig.userCountryCode);
|
|
||||||
}
|
|
||||||
const QString serviceType = apiV2->serviceType();
|
|
||||||
if (!serviceType.isEmpty()) {
|
|
||||||
serviceTypes.insert(serviceType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (userCountryCodes.isEmpty() && serviceTypes.isEmpty()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
QJsonObject json;
|
|
||||||
|
|
||||||
QJsonArray userCountryCodesArray;
|
|
||||||
for (const QString &code : userCountryCodes) {
|
|
||||||
userCountryCodesArray.append(code);
|
|
||||||
}
|
|
||||||
json[apiDefs::key::userCountryCode] = userCountryCodesArray;
|
|
||||||
|
|
||||||
QJsonArray serviceTypesArray;
|
|
||||||
for (const QString &type : serviceTypes) {
|
|
||||||
serviceTypesArray.append(type);
|
|
||||||
}
|
|
||||||
json[apiDefs::key::serviceType] = serviceTypesArray;
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
QFuture<QPair<ErrorCode, QJsonArray>> NewsController::fetchNews()
|
QFuture<QPair<ErrorCode, QJsonArray>> NewsController::fetchNews()
|
||||||
{
|
{
|
||||||
if (!m_serversRepository) {
|
if (!m_serversController) {
|
||||||
qWarning() << "SecureServersRepository is null, skip fetchNews";
|
qWarning() << "ServersController is null, skip fetchNews";
|
||||||
return QtFuture::makeReadyFuture(qMakePair(ErrorCode::InternalError, QJsonArray()));
|
return QtFuture::makeReadyFuture(qMakePair(ErrorCode::InternalError, QJsonArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
const QJsonObject services = getServicesList();
|
const auto stacks = m_serversController->gatewayStacks();
|
||||||
if (services.isEmpty()) {
|
if (stacks.isEmpty()) {
|
||||||
qDebug() << "No Gateway stacks, skip fetchNews";
|
qDebug() << "No Gateway stacks, skip fetchNews";
|
||||||
return QtFuture::makeReadyFuture(qMakePair(ErrorCode::NoError, QJsonArray()));
|
return QtFuture::makeReadyFuture(qMakePair(ErrorCode::NoError, QJsonArray()));
|
||||||
}
|
}
|
||||||
@@ -83,11 +40,12 @@ QFuture<QPair<ErrorCode, QJsonArray>> NewsController::fetchNews()
|
|||||||
QJsonObject payload;
|
QJsonObject payload;
|
||||||
payload.insert("locale", m_appSettingsRepository->getAppLanguage().name().split("_").first());
|
payload.insert("locale", m_appSettingsRepository->getAppLanguage().name().split("_").first());
|
||||||
|
|
||||||
if (services.contains(apiDefs::key::userCountryCode)) {
|
const QJsonObject stacksJson = stacks.toJson();
|
||||||
payload.insert(apiDefs::key::userCountryCode, services.value(apiDefs::key::userCountryCode));
|
if (stacksJson.contains(apiDefs::key::userCountryCode)) {
|
||||||
|
payload.insert(apiDefs::key::userCountryCode, stacksJson.value(apiDefs::key::userCountryCode));
|
||||||
}
|
}
|
||||||
if (services.contains(apiDefs::key::serviceType)) {
|
if (stacksJson.contains(apiDefs::key::serviceType)) {
|
||||||
payload.insert(apiDefs::key::serviceType, services.value(apiDefs::key::serviceType));
|
payload.insert(apiDefs::key::serviceType, stacksJson.value(apiDefs::key::serviceType));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto future = gatewayController->postAsync(QString("%1v1/news"), payload);
|
auto future = gatewayController->postAsync(QString("%1v1/news"), payload);
|
||||||
@@ -111,3 +69,4 @@ QFuture<QPair<ErrorCode, QJsonArray>> NewsController::fetchNews()
|
|||||||
return qMakePair(ErrorCode::NoError, newsArray);
|
return qMakePair(ErrorCode::NoError, newsArray);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,28 +3,26 @@
|
|||||||
|
|
||||||
#include <QFuture>
|
#include <QFuture>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
|
|
||||||
#include "core/utils/errorCodes.h"
|
#include "core/utils/errorCodes.h"
|
||||||
#include "core/utils/routeModes.h"
|
#include "core/utils/routeModes.h"
|
||||||
#include "core/utils/commonStructs.h"
|
#include "core/utils/commonStructs.h"
|
||||||
#include "core/repositories/secureAppSettingsRepository.h"
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
#include "core/repositories/secureServersRepository.h"
|
#include "core/controllers/serversController.h"
|
||||||
|
|
||||||
class NewsController
|
class NewsController
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit NewsController(SecureAppSettingsRepository* appSettingsRepository,
|
explicit NewsController(SecureAppSettingsRepository* appSettingsRepository,
|
||||||
SecureServersRepository* serversRepository);
|
ServersController* serversController);
|
||||||
|
|
||||||
QFuture<QPair<ErrorCode, QJsonArray>> fetchNews();
|
QFuture<QPair<ErrorCode, QJsonArray>> fetchNews();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QJsonObject getServicesList() const;
|
|
||||||
|
|
||||||
SecureAppSettingsRepository* m_appSettingsRepository;
|
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||||
SecureServersRepository* m_serversRepository;
|
ServersController* m_serversController;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // NEWSCONTROLLER_H
|
#endif // NEWSCONTROLLER_H
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
#include "core/controllers/gatewayController.h"
|
#include "core/controllers/gatewayController.h"
|
||||||
#include "core/utils/serverConfigUtils.h"
|
#include "core/utils/api/apiEnums.h"
|
||||||
#include "core/utils/constants/apiKeys.h"
|
#include "core/utils/constants/apiKeys.h"
|
||||||
#include "core/utils/constants/apiConstants.h"
|
#include "core/utils/constants/apiConstants.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
#include "core/utils/containerEnum.h"
|
#include "core/utils/containerEnum.h"
|
||||||
#include "core/utils/containers/containerUtils.h"
|
#include "core/utils/containers/containerUtils.h"
|
||||||
#include "core/utils/protocolEnum.h"
|
#include "core/utils/protocolEnum.h"
|
||||||
#include "core/utils/serverConfigUtils.h"
|
#include "core/utils/api/apiEnums.h"
|
||||||
#include "core/utils/constants/apiKeys.h"
|
#include "core/utils/constants/apiKeys.h"
|
||||||
#include "core/utils/constants/apiConstants.h"
|
#include "core/utils/constants/apiConstants.h"
|
||||||
#include "core/utils/api/apiUtils.h"
|
#include "core/utils/api/apiUtils.h"
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
#include "core/utils/constants/configKeys.h"
|
#include "core/utils/constants/configKeys.h"
|
||||||
#include "core/utils/constants/protocolConstants.h"
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
#include "core/models/containerConfig.h"
|
#include "core/models/containerConfig.h"
|
||||||
#include "core/models/api/apiConfig.h"
|
#include "core/models/api/apiConfig.h"
|
||||||
|
|
||||||
@@ -195,7 +196,7 @@ void SubscriptionController::updateApiConfigInJson(QJsonObject &serverConfigJson
|
|||||||
apiConfig[apiDefs::key::serviceProtocol] = serviceProtocol;
|
apiConfig[apiDefs::key::serviceProtocol] = serviceProtocol;
|
||||||
apiConfig[apiDefs::key::userCountryCode] = userCountryCode;
|
apiConfig[apiDefs::key::userCountryCode] = userCountryCode;
|
||||||
|
|
||||||
if (serverConfigJson.value(configKey::configVersion).toInt() == serverConfigUtils::ConfigSource::AmneziaGateway) {
|
if (serverConfigJson.value(configKey::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) {
|
||||||
QJsonObject responseObj = QJsonDocument::fromJson(apiResponseBody).object();
|
QJsonObject responseObj = QJsonDocument::fromJson(apiResponseBody).object();
|
||||||
if (responseObj.contains(apiDefs::key::supportedProtocols)) {
|
if (responseObj.contains(apiDefs::key::supportedProtocols)) {
|
||||||
apiConfig.insert(apiDefs::key::supportedProtocols, responseObj.value(apiDefs::key::supportedProtocols).toArray());
|
apiConfig.insert(apiDefs::key::supportedProtocols, responseObj.value(apiDefs::key::supportedProtocols).toArray());
|
||||||
@@ -216,7 +217,8 @@ ErrorCode SubscriptionController::executeRequest(const QString &endpoint, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode SubscriptionController::importServiceFromGateway(const QString &userCountryCode, const QString &serviceType,
|
ErrorCode SubscriptionController::importServiceFromGateway(const QString &userCountryCode, const QString &serviceType,
|
||||||
const QString &serviceProtocol, const ProtocolData &protocolData)
|
const QString &serviceProtocol, const ProtocolData &protocolData,
|
||||||
|
ServerConfig &serverConfig)
|
||||||
{
|
{
|
||||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||||
QString(APP_VERSION),
|
QString(APP_VERSION),
|
||||||
@@ -245,18 +247,20 @@ ErrorCode SubscriptionController::importServiceFromGateway(const QString &userCo
|
|||||||
|
|
||||||
updateApiConfigInJson(serverConfigJson, serviceType, serviceProtocol, userCountryCode, responseBody);
|
updateApiConfigInJson(serverConfigJson, serviceType, serviceProtocol, userCountryCode, responseBody);
|
||||||
|
|
||||||
if (serverConfigJson.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) {
|
ServerConfig serverConfigModel = ServerConfig::fromJson(serverConfigJson);
|
||||||
|
|
||||||
|
if (!serverConfigModel.isApiV2()) {
|
||||||
return ErrorCode::InternalError;
|
return ErrorCode::InternalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ApiV2ServerConfig apiV2ServerConfig = ApiV2ServerConfig::fromJson(serverConfigJson);
|
m_serversRepository->addServer(serverConfigModel);
|
||||||
m_serversRepository->addServer(QString(), apiV2ServerConfig.toJson(),
|
serverConfig = serverConfigModel;
|
||||||
serverConfigUtils::configTypeFromJson(apiV2ServerConfig.toJson()));
|
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode SubscriptionController::importTrialFromGateway(const QString &userCountryCode, const QString &serviceType,
|
ErrorCode SubscriptionController::importTrialFromGateway(const QString &userCountryCode, const QString &serviceType,
|
||||||
const QString &serviceProtocol, const QString &email)
|
const QString &serviceProtocol, const QString &email,
|
||||||
|
ServerConfig &serverConfig)
|
||||||
{
|
{
|
||||||
const QString trimmedEmail = email.trimmed();
|
const QString trimmedEmail = email.trimmed();
|
||||||
if (trimmedEmail.isEmpty()) {
|
if (trimmedEmail.isEmpty()) {
|
||||||
@@ -302,19 +306,16 @@ ErrorCode SubscriptionController::importTrialFromGateway(const QString &userCoun
|
|||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject configObject = QJsonDocument::fromJson(configBytes).object();
|
QJsonObject configObject = QJsonDocument::fromJson(configBytes).object();
|
||||||
if (configObject.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) {
|
ServerConfig serverConfigModel = ServerConfig::fromJson(configObject);
|
||||||
return ErrorCode::InternalError;
|
m_serversRepository->addServer(serverConfigModel);
|
||||||
}
|
serverConfig = serverConfigModel;
|
||||||
|
|
||||||
ApiV2ServerConfig apiV2ServerConfig = ApiV2ServerConfig::fromJson(configObject);
|
|
||||||
m_serversRepository->addServer(QString(), apiV2ServerConfig.toJson(),
|
|
||||||
serverConfigUtils::configTypeFromJson(apiV2ServerConfig.toJson()));
|
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userCountryCode, const QString &serviceType,
|
ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userCountryCode, const QString &serviceType,
|
||||||
const QString &serviceProtocol, const ProtocolData &protocolData,
|
const QString &serviceProtocol, const ProtocolData &protocolData,
|
||||||
const QString &transactionId, bool isTestPurchase,
|
const QString &transactionId, bool isTestPurchase,
|
||||||
|
ServerConfig &serverConfig,
|
||||||
int *duplicateServerIndex)
|
int *duplicateServerIndex)
|
||||||
{
|
{
|
||||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||||
@@ -331,8 +332,13 @@ ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userC
|
|||||||
appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload);
|
appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload);
|
||||||
apiPayload[apiDefs::key::transactionId] = transactionId;
|
apiPayload[apiDefs::key::transactionId] = transactionId;
|
||||||
|
|
||||||
|
GatewayController gatewayController(m_appSettingsRepository->getGatewayEndpoint(),
|
||||||
|
m_appSettingsRepository->isDevGatewayEnv(),
|
||||||
|
apiDefs::requestTimeoutMsecs,
|
||||||
|
m_appSettingsRepository->isStrictKillSwitchEnabled());
|
||||||
|
|
||||||
QByteArray responseBody;
|
QByteArray responseBody;
|
||||||
ErrorCode errorCode = executeRequest(QString("%1v1/subscriptions"), apiPayload, responseBody, isTestPurchase);
|
ErrorCode errorCode = gatewayController.post(QString("%1v1/subscriptions"), apiPayload, responseBody);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
@@ -345,15 +351,18 @@ ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userC
|
|||||||
return ErrorCode::ApiPurchaseError;
|
return ErrorCode::ApiPurchaseError;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString normalizedKey = key;
|
|
||||||
normalizedKey.replace(QStringLiteral("vpn://"), QString());
|
|
||||||
|
|
||||||
// Check if server with this VPN key already exists
|
// Check if server with this VPN key already exists
|
||||||
for (int i = 0; i < m_serversRepository->serversCount(); ++i) {
|
for (int i = 0; i < m_serversRepository->serversCount(); ++i) {
|
||||||
const auto apiV2 = m_serversRepository->apiV2Config(m_serversRepository->serverIdAt(i));
|
ServerConfig existingServerConfig = m_serversRepository->server(i);
|
||||||
QString existingVpnKey = apiV2.has_value() ? apiV2->vpnKey() : QString();
|
QString existingVpnKey;
|
||||||
existingVpnKey.replace(QStringLiteral("vpn://"), QString());
|
if (existingServerConfig.isApiV1()) {
|
||||||
if (!existingVpnKey.isEmpty() && existingVpnKey == normalizedKey) {
|
const ApiV1ServerConfig* apiV1 = existingServerConfig.as<ApiV1ServerConfig>();
|
||||||
|
existingVpnKey = apiV1 ? apiV1->vpnKey() : QString();
|
||||||
|
} else if (existingServerConfig.isApiV2()) {
|
||||||
|
const ApiV2ServerConfig* apiV2 = existingServerConfig.as<ApiV2ServerConfig>();
|
||||||
|
existingVpnKey = apiV2 ? apiV2->vpnKey() : QString();
|
||||||
|
}
|
||||||
|
if (existingVpnKey == key) {
|
||||||
if (duplicateServerIndex) {
|
if (duplicateServerIndex) {
|
||||||
*duplicateServerIndex = i;
|
*duplicateServerIndex = i;
|
||||||
}
|
}
|
||||||
@@ -362,6 +371,9 @@ ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString normalizedKey = key;
|
||||||
|
normalizedKey.replace(QStringLiteral("vpn://"), QString());
|
||||||
|
|
||||||
QByteArray configString = QByteArray::fromBase64(normalizedKey.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
QByteArray configString = QByteArray::fromBase64(normalizedKey.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
||||||
QByteArray configUncompressed = qUncompress(configString);
|
QByteArray configUncompressed = qUncompress(configString);
|
||||||
if (!configUncompressed.isEmpty()) {
|
if (!configUncompressed.isEmpty()) {
|
||||||
@@ -377,31 +389,40 @@ ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userC
|
|||||||
|
|
||||||
quint16 crc = qChecksum(QJsonDocument(configObject).toJson());
|
quint16 crc = qChecksum(QJsonDocument(configObject).toJson());
|
||||||
|
|
||||||
if (configObject.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) {
|
ServerConfig serverConfigModel = ServerConfig::fromJson(configObject);
|
||||||
|
|
||||||
|
if (!serverConfigModel.isApiV2()) {
|
||||||
return ErrorCode::InternalError;
|
return ErrorCode::InternalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ApiV2ServerConfig apiV2ServerConfig = ApiV2ServerConfig::fromJson(configObject);
|
ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
|
||||||
ApiV2ServerConfig* apiV2 = &apiV2ServerConfig;
|
if (!apiV2) {
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
apiV2->apiConfig.vpnKey = normalizedKey;
|
apiV2->apiConfig.vpnKey = normalizedKey;
|
||||||
apiV2->apiConfig.isTestPurchase = isTestPurchase;
|
apiV2->apiConfig.isTestPurchase = isTestPurchase;
|
||||||
apiV2->apiConfig.isInAppPurchase = true;
|
apiV2->apiConfig.isInAppPurchase = true;
|
||||||
apiV2->apiConfig.subscriptionExpiredByServer = false;
|
apiV2->apiConfig.subscriptionExpiredByServer = false;
|
||||||
apiV2->crc = crc;
|
apiV2->crc = crc;
|
||||||
|
|
||||||
m_serversRepository->addServer(QString(), apiV2ServerConfig.toJson(),
|
m_serversRepository->addServer(serverConfigModel);
|
||||||
serverConfigUtils::configTypeFromJson(apiV2ServerConfig.toJson()));
|
serverConfig = serverConfigModel;
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode SubscriptionController::updateServiceFromGateway(const QString &serverId, const QString &newCountryCode, bool isConnectEvent)
|
ErrorCode SubscriptionController::updateServiceFromGateway(int serverIndex, const QString &newCountryCode, bool isConnectEvent)
|
||||||
{
|
{
|
||||||
auto apiV2 = m_serversRepository->apiV2Config(serverId);
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
if (!apiV2.has_value()) {
|
|
||||||
|
if (!serverConfigModel.isApiV2()) {
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
|
||||||
|
if (!apiV2) {
|
||||||
return ErrorCode::InternalError;
|
return ErrorCode::InternalError;
|
||||||
}
|
}
|
||||||
const bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
|
||||||
QString serviceProtocol = apiV2->serviceProtocol();
|
QString serviceProtocol = apiV2->serviceProtocol();
|
||||||
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
||||||
|
|
||||||
@@ -424,13 +445,15 @@ ErrorCode SubscriptionController::updateServiceFromGateway(const QString &server
|
|||||||
}
|
}
|
||||||
|
|
||||||
QByteArray responseBody;
|
QByteArray responseBody;
|
||||||
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody, isTestPurchase);
|
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
if (errorCode == ErrorCode::ApiSubscriptionExpiredError && !apiV2->apiConfig.isInAppPurchase) {
|
if (errorCode == ErrorCode::ApiSubscriptionExpiredError && !apiV2->apiConfig.isInAppPurchase) {
|
||||||
ApiV2ServerConfig expiredApiV2 = *apiV2;
|
ServerConfig expiredServerConfig = serverConfigModel;
|
||||||
expiredApiV2.apiConfig.subscriptionExpiredByServer = true;
|
ApiV2ServerConfig *expiredApiV2 = expiredServerConfig.as<ApiV2ServerConfig>();
|
||||||
m_serversRepository->editServer(serverId, expiredApiV2.toJson(),
|
if (expiredApiV2) {
|
||||||
serverConfigUtils::configTypeFromJson(expiredApiV2.toJson()));
|
expiredApiV2->apiConfig.subscriptionExpiredByServer = true;
|
||||||
|
m_serversRepository->editServer(serverIndex, expiredServerConfig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
@@ -443,12 +466,16 @@ ErrorCode SubscriptionController::updateServiceFromGateway(const QString &server
|
|||||||
|
|
||||||
updateApiConfigInJson(serverConfigJson, apiV2->apiConfig.serviceType, serviceProtocol, apiV2->apiConfig.userCountryCode, responseBody);
|
updateApiConfigInJson(serverConfigJson, apiV2->apiConfig.serviceType, serviceProtocol, apiV2->apiConfig.userCountryCode, responseBody);
|
||||||
|
|
||||||
if (serverConfigJson.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) {
|
ServerConfig newServerConfigModel = ServerConfig::fromJson(serverConfigJson);
|
||||||
|
|
||||||
|
if (!newServerConfigModel.isApiV2()) {
|
||||||
return ErrorCode::InternalError;
|
return ErrorCode::InternalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ApiV2ServerConfig newApiV2Config = ApiV2ServerConfig::fromJson(serverConfigJson);
|
ApiV2ServerConfig* newApiV2 = newServerConfigModel.as<ApiV2ServerConfig>();
|
||||||
ApiV2ServerConfig* newApiV2 = &newApiV2Config;
|
if (!newApiV2) {
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
newApiV2->apiConfig.vpnKey = apiV2->apiConfig.vpnKey;
|
newApiV2->apiConfig.vpnKey = apiV2->apiConfig.vpnKey;
|
||||||
newApiV2->apiConfig.isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
newApiV2->apiConfig.isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
||||||
@@ -463,15 +490,20 @@ ErrorCode SubscriptionController::updateServiceFromGateway(const QString &server
|
|||||||
newApiV2->nameOverriddenByUser = true;
|
newApiV2->nameOverriddenByUser = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_serversRepository->editServer(serverId, newApiV2Config.toJson(),
|
m_serversRepository->editServer(serverIndex, newServerConfigModel);
|
||||||
serverConfigUtils::configTypeFromJson(newApiV2Config.toJson()));
|
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode SubscriptionController::deactivateDevice(const QString &serverId)
|
ErrorCode SubscriptionController::deactivateDevice(int serverIndex, bool isRemoveEvent)
|
||||||
{
|
{
|
||||||
auto apiV2 = m_serversRepository->apiV2Config(serverId);
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
if (!apiV2.has_value()) {
|
|
||||||
|
if (!serverConfigModel.isApiV2()) {
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
|
||||||
|
if (!apiV2) {
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,23 +524,29 @@ ErrorCode SubscriptionController::deactivateDevice(const QString &serverId)
|
|||||||
|
|
||||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||||
|
|
||||||
const bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
|
||||||
QByteArray responseBody;
|
QByteArray responseBody;
|
||||||
ErrorCode errorCode = executeRequest(QString("%1v1/revoke_config"), apiPayload, responseBody, isTestPurchase);
|
ErrorCode errorCode = executeRequest(QString("%1v1/revoke_config"), apiPayload, responseBody);
|
||||||
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
|
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
apiV2->containers.clear();
|
serverConfigModel.visit([](auto& arg) {
|
||||||
m_serversRepository->editServer(serverId, apiV2->toJson(),
|
arg.containers.clear();
|
||||||
serverConfigUtils::configTypeFromJson(apiV2->toJson()));
|
});
|
||||||
|
m_serversRepository->editServer(serverIndex, serverConfigModel);
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode SubscriptionController::deactivateExternalDevice(const QString &serverId, const QString &uuid, const QString &serverCountryCode)
|
ErrorCode SubscriptionController::deactivateExternalDevice(int serverIndex, const QString &uuid, const QString &serverCountryCode)
|
||||||
{
|
{
|
||||||
auto apiV2 = m_serversRepository->apiV2Config(serverId);
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
if (!apiV2.has_value()) {
|
|
||||||
|
if (!serverConfigModel.isApiV2()) {
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
|
||||||
|
if (!apiV2) {
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,29 +567,34 @@ ErrorCode SubscriptionController::deactivateExternalDevice(const QString &server
|
|||||||
|
|
||||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||||
|
|
||||||
const bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
|
||||||
QByteArray responseBody;
|
QByteArray responseBody;
|
||||||
ErrorCode errorCode = executeRequest(QString("%1v1/revoke_config"), apiPayload, responseBody, isTestPurchase);
|
ErrorCode errorCode = executeRequest(QString("%1v1/revoke_config"), apiPayload, responseBody);
|
||||||
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
|
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uuid == m_appSettingsRepository->getInstallationUuid(true)) {
|
if (uuid == m_appSettingsRepository->getInstallationUuid(true)) {
|
||||||
apiV2->containers.clear();
|
serverConfigModel.visit([](auto& arg) {
|
||||||
m_serversRepository->editServer(serverId, apiV2->toJson(),
|
arg.containers.clear();
|
||||||
serverConfigUtils::configTypeFromJson(apiV2->toJson()));
|
});
|
||||||
|
m_serversRepository->editServer(serverIndex, serverConfigModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode SubscriptionController::exportNativeConfig(const QString &serverId, const QString &serverCountryCode, QString &nativeConfig)
|
ErrorCode SubscriptionController::exportNativeConfig(int serverIndex, const QString &serverCountryCode, QString &nativeConfig)
|
||||||
{
|
{
|
||||||
auto apiV2 = m_serversRepository->apiV2Config(serverId);
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
if (!apiV2.has_value()) {
|
|
||||||
|
if (!serverConfigModel.isApiV2()) {
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
|
||||||
|
if (!apiV2) {
|
||||||
return ErrorCode::InternalError;
|
return ErrorCode::InternalError;
|
||||||
}
|
}
|
||||||
const bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
|
||||||
QString protocol = configKey::awg;
|
QString protocol = configKey::awg;
|
||||||
ProtocolData protocolData = generateProtocolData(protocol);
|
ProtocolData protocolData = generateProtocolData(protocol);
|
||||||
|
|
||||||
@@ -570,7 +613,7 @@ ErrorCode SubscriptionController::exportNativeConfig(const QString &serverId, co
|
|||||||
appendProtocolDataToApiPayload(protocol, protocolData, apiPayload);
|
appendProtocolDataToApiPayload(protocol, protocolData, apiPayload);
|
||||||
|
|
||||||
QByteArray responseBody;
|
QByteArray responseBody;
|
||||||
ErrorCode errorCode = executeRequest(QString("%1v1/native_config"), apiPayload, responseBody, isTestPurchase);
|
ErrorCode errorCode = executeRequest(QString("%1v1/native_config"), apiPayload, responseBody);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
@@ -581,13 +624,18 @@ ErrorCode SubscriptionController::exportNativeConfig(const QString &serverId, co
|
|||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode SubscriptionController::revokeNativeConfig(const QString &serverId, const QString &serverCountryCode)
|
ErrorCode SubscriptionController::revokeNativeConfig(int serverIndex, const QString &serverCountryCode)
|
||||||
{
|
{
|
||||||
auto apiV2 = m_serversRepository->apiV2Config(serverId);
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
if (!apiV2.has_value()) {
|
|
||||||
|
if (!serverConfigModel.isApiV2()) {
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
|
||||||
|
if (!apiV2) {
|
||||||
return ErrorCode::InternalError;
|
return ErrorCode::InternalError;
|
||||||
}
|
}
|
||||||
const bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
|
||||||
QString protocol = configKey::awg;
|
QString protocol = configKey::awg;
|
||||||
|
|
||||||
QJsonObject authDataJson = apiV2->authData.toJson();
|
QJsonObject authDataJson = apiV2->authData.toJson();
|
||||||
@@ -604,7 +652,7 @@ ErrorCode SubscriptionController::revokeNativeConfig(const QString &serverId, co
|
|||||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||||
|
|
||||||
QByteArray responseBody;
|
QByteArray responseBody;
|
||||||
ErrorCode errorCode = executeRequest(QString("%1v1/revoke_native_config"), apiPayload, responseBody, isTestPurchase);
|
ErrorCode errorCode = executeRequest(QString("%1v1/revoke_native_config"), apiPayload, responseBody);
|
||||||
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
|
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
@@ -612,54 +660,126 @@ ErrorCode SubscriptionController::revokeNativeConfig(const QString &serverId, co
|
|||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode SubscriptionController::prepareVpnKeyExport(const QString &serverId, QString &vpnKey)
|
ErrorCode SubscriptionController::updateServiceFromTelegram(int serverIndex)
|
||||||
{
|
{
|
||||||
auto apiV2 = m_serversRepository->apiV2Config(serverId);
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
if (!apiV2.has_value()) {
|
|
||||||
return ErrorCode::ApiConfigEmptyError;
|
if (!serverConfigModel.isApiV1()) {
|
||||||
|
return ErrorCode::InternalError;
|
||||||
}
|
}
|
||||||
vpnKey = apiV2->vpnKey();
|
|
||||||
|
const ApiV1ServerConfig* apiV1 = serverConfigModel.as<ApiV1ServerConfig>();
|
||||||
|
if (!apiV1) {
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
QString serviceProtocol = apiV1->protocol;
|
||||||
|
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
||||||
|
QString installationUuid = m_appSettingsRepository->getInstallationUuid(true);
|
||||||
|
|
||||||
|
GatewayController gatewayController(m_appSettingsRepository->getGatewayEndpoint(), m_appSettingsRepository->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||||
|
m_appSettingsRepository->isStrictKillSwitchEnabled());
|
||||||
|
|
||||||
|
QJsonObject apiPayload;
|
||||||
|
appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload);
|
||||||
|
apiPayload[apiDefs::key::uuid] = installationUuid;
|
||||||
|
apiPayload[apiDefs::key::osVersion] = QSysInfo::productType();
|
||||||
|
apiPayload[apiDefs::key::appVersion] = QString(APP_VERSION);
|
||||||
|
apiPayload[configKey::accessToken] = apiV1->apiKey;
|
||||||
|
apiPayload[apiDefs::key::apiEndpoint] = apiV1->apiEndpoint;
|
||||||
|
|
||||||
|
QByteArray responseBody;
|
||||||
|
ErrorCode errorCode = gatewayController.post(QString("%1v1/proxy_config"), apiPayload, responseBody);
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject serverConfigJson;
|
||||||
|
errorCode = extractServerConfigJsonFromResponse(responseBody, serviceProtocol, protocolData, serverConfigJson);
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerConfig newServerConfigModel = ServerConfig::fromJson(serverConfigJson);
|
||||||
|
|
||||||
|
if (!newServerConfigModel.isApiV1()) {
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiV1ServerConfig* newApiV1 = newServerConfigModel.as<ApiV1ServerConfig>();
|
||||||
|
if (!newApiV1) {
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
newApiV1->apiKey = apiV1->apiKey;
|
||||||
|
newApiV1->apiEndpoint = apiV1->apiEndpoint;
|
||||||
|
newApiV1->crc = apiV1->crc;
|
||||||
|
|
||||||
|
m_serversRepository->editServer(serverIndex, newServerConfigModel);
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode SubscriptionController::prepareVpnKeyExport(int serverIndex, QString &vpnKey)
|
||||||
|
{
|
||||||
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
|
|
||||||
|
if (serverConfigModel.isApiV1()) {
|
||||||
|
const ApiV1ServerConfig* apiV1 = serverConfigModel.as<ApiV1ServerConfig>();
|
||||||
|
vpnKey = apiV1 ? apiV1->vpnKey() : QString();
|
||||||
|
} else if (serverConfigModel.isApiV2()) {
|
||||||
|
ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
|
||||||
|
vpnKey = apiV2 ? apiV2->vpnKey() : QString();
|
||||||
if (vpnKey.isEmpty()) {
|
if (vpnKey.isEmpty()) {
|
||||||
vpnKey = apiUtils::getPremiumV2VpnKey(apiV2->toJson());
|
QJsonObject serverJson = serverConfigModel.toJson();
|
||||||
|
vpnKey = apiUtils::getPremiumV2VpnKey(serverJson);
|
||||||
if (vpnKey.isEmpty()) {
|
if (vpnKey.isEmpty()) {
|
||||||
return ErrorCode::ApiConfigEmptyError;
|
return ErrorCode::ApiConfigEmptyError;
|
||||||
}
|
}
|
||||||
apiV2->apiConfig.vpnKey = vpnKey;
|
apiV2->apiConfig.vpnKey = vpnKey;
|
||||||
m_serversRepository->editServer(serverId, apiV2->toJson(),
|
m_serversRepository->editServer(serverIndex, serverConfigModel);
|
||||||
serverConfigUtils::configTypeFromJson(apiV2->toJson()));
|
}
|
||||||
|
} else {
|
||||||
|
return ErrorCode::ApiConfigEmptyError;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode SubscriptionController::validateAndUpdateConfig(const QString &serverId, bool hasInstalledContainers)
|
ErrorCode SubscriptionController::validateAndUpdateConfig(int serverIndex, bool hasInstalledContainers)
|
||||||
{
|
{
|
||||||
if (!m_serversRepository->apiV2Config(serverId).has_value()) {
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
|
|
||||||
|
apiDefs::ConfigSource configSource;
|
||||||
|
if (serverConfigModel.isApiV1()) {
|
||||||
|
configSource = apiDefs::ConfigSource::Telegram;
|
||||||
|
} else if (serverConfigModel.isApiV2()) {
|
||||||
|
configSource = apiDefs::ConfigSource::AmneziaGateway;
|
||||||
|
} else {
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasInstalledContainers) {
|
if (configSource == apiDefs::ConfigSource::Telegram && !hasInstalledContainers) {
|
||||||
return updateServiceFromGateway(serverId, "", true);
|
removeApiConfig(serverIndex);
|
||||||
}
|
return updateServiceFromTelegram(serverIndex);
|
||||||
|
} else if (configSource == apiDefs::ConfigSource::AmneziaGateway && !hasInstalledContainers) {
|
||||||
if (isApiKeyExpired(serverId)) {
|
return updateServiceFromGateway(serverIndex, "", false);
|
||||||
|
} else if (configSource && isApiKeyExpired(serverIndex)) {
|
||||||
qDebug() << "attempt to update api config by expires_at event";
|
qDebug() << "attempt to update api config by expires_at event";
|
||||||
return updateServiceFromGateway(serverId, "", true);
|
if (configSource == apiDefs::ConfigSource::AmneziaGateway) {
|
||||||
|
return updateServiceFromGateway(serverIndex, "", false);
|
||||||
|
} else {
|
||||||
|
removeApiConfig(serverIndex);
|
||||||
|
return updateServiceFromTelegram(serverIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SubscriptionController::removeApiConfig(const QString &serverId)
|
void SubscriptionController::removeApiConfig(int serverIndex)
|
||||||
{
|
{
|
||||||
auto apiV2 = m_serversRepository->apiV2Config(serverId);
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
if (!apiV2.has_value()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
QString description = apiV2->description;
|
QString description = serverConfigModel.description();
|
||||||
QString hostName = apiV2->hostName;
|
QString hostName = serverConfigModel.hostName();
|
||||||
QString vpncName = QString("%1 (%2) %3")
|
QString vpncName = QString("%1 (%2) %3")
|
||||||
.arg(description)
|
.arg(description)
|
||||||
.arg(hostName)
|
.arg(hostName)
|
||||||
@@ -668,42 +788,34 @@ void SubscriptionController::removeApiConfig(const QString &serverId)
|
|||||||
AmneziaVPN::removeVPNC(vpncName.toStdString());
|
AmneziaVPN::removeVPNC(vpncName.toStdString());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
apiV2->dns1.clear();
|
serverConfigModel.visit([](auto& arg) {
|
||||||
apiV2->dns2.clear();
|
arg.dns1.clear();
|
||||||
apiV2->containers.clear();
|
arg.dns2.clear();
|
||||||
apiV2->hostName.clear();
|
arg.containers.clear();
|
||||||
apiV2->defaultContainer = DockerContainer::None;
|
arg.hostName.clear();
|
||||||
|
arg.defaultContainer = DockerContainer::None;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (serverConfigModel.isApiV2()) {
|
||||||
|
ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
|
||||||
|
if (apiV2) {
|
||||||
apiV2->apiConfig.publicKey = ApiConfig::PublicKeyInfo{};
|
apiV2->apiConfig.publicKey = ApiConfig::PublicKeyInfo{};
|
||||||
|
}
|
||||||
m_serversRepository->editServer(serverId, apiV2->toJson(),
|
|
||||||
serverConfigUtils::configTypeFromJson(apiV2->toJson()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SubscriptionController::removeServer(const QString &serverId)
|
m_serversRepository->editServer(serverIndex, serverConfigModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SubscriptionController::isApiKeyExpired(int serverIndex) const
|
||||||
{
|
{
|
||||||
if (serverId.isEmpty()) {
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
|
|
||||||
|
if (!serverConfigModel.isApiV2()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_serversRepository->apiV2Config(serverId).has_value()) {
|
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
|
||||||
qWarning().noquote() << "SubscriptionController::removeServer: not an Api V2 server, id" << serverId;
|
if (!apiV2) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ErrorCode revokeError = deactivateDevice(serverId);
|
|
||||||
if (revokeError != ErrorCode::NoError && revokeError != ErrorCode::ApiNotFoundError) {
|
|
||||||
qWarning().noquote() << "SubscriptionController::removeServer: deactivateDevice failed (error"
|
|
||||||
<< static_cast<int>(revokeError) << "); removing locally anyway.";
|
|
||||||
}
|
|
||||||
|
|
||||||
m_serversRepository->removeServer(serverId);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SubscriptionController::isApiKeyExpired(const QString &serverId) const
|
|
||||||
{
|
|
||||||
auto apiV2 = m_serversRepository->apiV2Config(serverId);
|
|
||||||
if (!apiV2.has_value()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const QString expiresAt = apiV2->apiConfig.publicKey.expiresAt;
|
const QString expiresAt = apiV2->apiConfig.publicKey.expiresAt;
|
||||||
@@ -720,24 +832,31 @@ bool SubscriptionController::isApiKeyExpired(const QString &serverId) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SubscriptionController::setCurrentProtocol(const QString &serverId, const QString &protocolName)
|
void SubscriptionController::setCurrentProtocol(int serverIndex, const QString &protocolName)
|
||||||
{
|
{
|
||||||
auto apiV2 = m_serversRepository->apiV2Config(serverId);
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
if (apiV2.has_value()) {
|
if (serverConfigModel.isApiV2()) {
|
||||||
|
ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
|
||||||
|
if (apiV2) {
|
||||||
apiV2->apiConfig.serviceProtocol = protocolName;
|
apiV2->apiConfig.serviceProtocol = protocolName;
|
||||||
m_serversRepository->editServer(serverId, apiV2->toJson(),
|
}
|
||||||
serverConfigUtils::configTypeFromJson(apiV2->toJson()));
|
m_serversRepository->editServer(serverIndex, serverConfigModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SubscriptionController::isVlessProtocol(const QString &serverId) const
|
bool SubscriptionController::isVlessProtocol(int serverIndex) const
|
||||||
{
|
{
|
||||||
auto apiV2 = m_serversRepository->apiV2Config(serverId);
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
return apiV2.has_value() && apiV2->serviceProtocol() == "vless";
|
if (serverConfigModel.isApiV2()) {
|
||||||
|
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
|
||||||
|
return apiV2 && apiV2->serviceProtocol() == "vless";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode SubscriptionController::processAppStorePurchase(const QString &userCountryCode, const QString &serviceType,
|
ErrorCode SubscriptionController::processAppStorePurchase(const QString &userCountryCode, const QString &serviceType,
|
||||||
const QString &serviceProtocol, const QString &productId,
|
const QString &serviceProtocol, const QString &productId,
|
||||||
|
ServerConfig &serverConfig,
|
||||||
int *duplicateServerIndex)
|
int *duplicateServerIndex)
|
||||||
{
|
{
|
||||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
@@ -771,12 +890,13 @@ ErrorCode SubscriptionController::processAppStorePurchase(const QString &userCou
|
|||||||
|
|
||||||
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
||||||
return importServiceFromAppStore(userCountryCode, serviceType, serviceProtocol, protocolData,
|
return importServiceFromAppStore(userCountryCode, serviceType, serviceProtocol, protocolData,
|
||||||
originalTransactionId, isTestPurchase, duplicateServerIndex);
|
originalTransactionId, isTestPurchase, serverConfig, duplicateServerIndex);
|
||||||
#else
|
#else
|
||||||
Q_UNUSED(userCountryCode);
|
Q_UNUSED(userCountryCode);
|
||||||
Q_UNUSED(serviceType);
|
Q_UNUSED(serviceType);
|
||||||
Q_UNUSED(serviceProtocol);
|
Q_UNUSED(serviceProtocol);
|
||||||
Q_UNUSED(productId);
|
Q_UNUSED(productId);
|
||||||
|
Q_UNUSED(serverConfig);
|
||||||
return ErrorCode::ApiPurchaseError;
|
return ErrorCode::ApiPurchaseError;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -835,9 +955,10 @@ SubscriptionController::AppStoreRestoreResult SubscriptionController::processApp
|
|||||||
<< "originalTransactionId =" << originalTransactionId << "productId =" << transactionProductId;
|
<< "originalTransactionId =" << originalTransactionId << "productId =" << transactionProductId;
|
||||||
|
|
||||||
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
||||||
|
ServerConfig serverConfig;
|
||||||
int currentDuplicateServerIndex = -1;
|
int currentDuplicateServerIndex = -1;
|
||||||
ErrorCode errorCode = importServiceFromAppStore(userCountryCode, serviceType, serviceProtocol, protocolData,
|
ErrorCode errorCode = importServiceFromAppStore(userCountryCode, serviceType, serviceProtocol, protocolData,
|
||||||
originalTransactionId, isTestPurchase,
|
originalTransactionId, isTestPurchase, serverConfig,
|
||||||
¤tDuplicateServerIndex);
|
¤tDuplicateServerIndex);
|
||||||
|
|
||||||
if (errorCode == ErrorCode::ApiConfigAlreadyAdded) {
|
if (errorCode == ErrorCode::ApiConfigAlreadyAdded) {
|
||||||
@@ -869,10 +990,16 @@ SubscriptionController::AppStoreRestoreResult SubscriptionController::processApp
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode SubscriptionController::getAccountInfo(const QString &serverId, QJsonObject &accountInfo)
|
ErrorCode SubscriptionController::getAccountInfo(int serverIndex, QJsonObject &accountInfo)
|
||||||
{
|
{
|
||||||
auto apiV2 = m_serversRepository->apiV2Config(serverId);
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
if (!apiV2.has_value()) {
|
|
||||||
|
if (!serverConfigModel.isApiV2()) {
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ApiV2ServerConfig* apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
|
||||||
|
if (!apiV2) {
|
||||||
return ErrorCode::InternalError;
|
return ErrorCode::InternalError;
|
||||||
}
|
}
|
||||||
bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
||||||
@@ -902,13 +1029,20 @@ ErrorCode SubscriptionController::getAccountInfo(const QString &serverId, QJsonO
|
|||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
QFuture<QPair<ErrorCode, QString>> SubscriptionController::getRenewalLink(const QString &serverId)
|
QFuture<QPair<ErrorCode, QString>> SubscriptionController::getRenewalLink(int serverIndex)
|
||||||
{
|
{
|
||||||
auto promise = QSharedPointer<QPromise<QPair<ErrorCode, QString>>>::create();
|
auto promise = QSharedPointer<QPromise<QPair<ErrorCode, QString>>>::create();
|
||||||
promise->start();
|
promise->start();
|
||||||
|
|
||||||
auto apiV2 = m_serversRepository->apiV2Config(serverId);
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
if (!apiV2.has_value()) {
|
if (!serverConfigModel.isApiV2()) {
|
||||||
|
promise->addResult(qMakePair(ErrorCode::InternalError, QString()));
|
||||||
|
promise->finish();
|
||||||
|
return promise->future();
|
||||||
|
}
|
||||||
|
|
||||||
|
const ApiV2ServerConfig *apiV2 = serverConfigModel.as<ApiV2ServerConfig>();
|
||||||
|
if (!apiV2) {
|
||||||
promise->addResult(qMakePair(ErrorCode::InternalError, QString()));
|
promise->addResult(qMakePair(ErrorCode::InternalError, QString()));
|
||||||
promise->finish();
|
promise->finish();
|
||||||
return promise->future();
|
return promise->future();
|
||||||
@@ -928,7 +1062,6 @@ QFuture<QPair<ErrorCode, QString>> SubscriptionController::getRenewalLink(const
|
|||||||
|
|
||||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||||
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||||
apiPayload[apiDefs::key::subscriptionStatus] = getSubscriptionStatusForRenewal(apiV2->apiConfig);
|
|
||||||
|
|
||||||
auto gatewayController = QSharedPointer<GatewayController>::create(m_appSettingsRepository->getGatewayEndpoint(isTestPurchase),
|
auto gatewayController = QSharedPointer<GatewayController>::create(m_appSettingsRepository->getGatewayEndpoint(isTestPurchase),
|
||||||
m_appSettingsRepository->isDevGatewayEnv(isTestPurchase),
|
m_appSettingsRepository->isDevGatewayEnv(isTestPurchase),
|
||||||
@@ -948,7 +1081,11 @@ QFuture<QPair<ErrorCode, QString>> SubscriptionController::getRenewalLink(const
|
|||||||
|
|
||||||
QJsonObject responseJson = QJsonDocument::fromJson(responseBody).object();
|
QJsonObject responseJson = QJsonDocument::fromJson(responseBody).object();
|
||||||
const QString url = responseJson.value("renewal_url").toString();
|
const QString url = responseJson.value("renewal_url").toString();
|
||||||
|
if (url.isEmpty()) {
|
||||||
|
promise->addResult(qMakePair(ErrorCode::InternalError, QString()));
|
||||||
|
} else {
|
||||||
promise->addResult(qMakePair(ErrorCode::NoError, url));
|
promise->addResult(qMakePair(ErrorCode::NoError, url));
|
||||||
|
}
|
||||||
promise->finish();
|
promise->finish();
|
||||||
});
|
});
|
||||||
watcher->setFuture(postFuture);
|
watcher->setFuture(postFuture);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "core/utils/commonStructs.h"
|
#include "core/utils/commonStructs.h"
|
||||||
#include "core/repositories/secureServersRepository.h"
|
#include "core/repositories/secureServersRepository.h"
|
||||||
#include "core/repositories/secureAppSettingsRepository.h"
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
|
|
||||||
class ServersController;
|
class ServersController;
|
||||||
|
|
||||||
@@ -47,40 +48,44 @@ public:
|
|||||||
|
|
||||||
ProtocolData generateProtocolData(const QString &protocol);
|
ProtocolData generateProtocolData(const QString &protocol);
|
||||||
void appendProtocolDataToApiPayload(const QString &protocol, const ProtocolData &protocolData, QJsonObject &apiPayload);
|
void appendProtocolDataToApiPayload(const QString &protocol, const ProtocolData &protocolData, QJsonObject &apiPayload);
|
||||||
|
ErrorCode fillServerConfig(const QJsonObject &serverConfigJson, ServerConfig &serverConfig);
|
||||||
|
|
||||||
ErrorCode importServiceFromGateway(const QString &userCountryCode, const QString &serviceType,
|
ErrorCode importServiceFromGateway(const QString &userCountryCode, const QString &serviceType,
|
||||||
const QString &serviceProtocol, const ProtocolData &protocolData);
|
const QString &serviceProtocol, const ProtocolData &protocolData,
|
||||||
|
ServerConfig &serverConfig);
|
||||||
ErrorCode importTrialFromGateway(const QString &userCountryCode, const QString &serviceType,
|
ErrorCode importTrialFromGateway(const QString &userCountryCode, const QString &serviceType,
|
||||||
const QString &serviceProtocol, const QString &email);
|
const QString &serviceProtocol, const QString &email,
|
||||||
|
ServerConfig &serverConfig);
|
||||||
|
|
||||||
ErrorCode importServiceFromAppStore(const QString &userCountryCode, const QString &serviceType,
|
ErrorCode importServiceFromAppStore(const QString &userCountryCode, const QString &serviceType,
|
||||||
const QString &serviceProtocol, const ProtocolData &protocolData,
|
const QString &serviceProtocol, const ProtocolData &protocolData,
|
||||||
const QString &transactionId, bool isTestPurchase,
|
const QString &transactionId, bool isTestPurchase,
|
||||||
|
ServerConfig &serverConfig,
|
||||||
int *duplicateServerIndex = nullptr);
|
int *duplicateServerIndex = nullptr);
|
||||||
|
|
||||||
ErrorCode updateServiceFromGateway(const QString &serverId, const QString &newCountryCode, bool isConnectEvent);
|
ErrorCode updateServiceFromGateway(int serverIndex, const QString &newCountryCode, bool isConnectEvent);
|
||||||
|
|
||||||
ErrorCode deactivateDevice(const QString &serverId);
|
ErrorCode deactivateDevice(int serverIndex, bool isRemoveEvent);
|
||||||
|
|
||||||
ErrorCode deactivateExternalDevice(const QString &serverId, const QString &uuid, const QString &serverCountryCode);
|
ErrorCode deactivateExternalDevice(int serverIndex, const QString &uuid, const QString &serverCountryCode);
|
||||||
|
|
||||||
ErrorCode exportNativeConfig(const QString &serverId, const QString &serverCountryCode, QString &nativeConfig);
|
ErrorCode exportNativeConfig(int serverIndex, const QString &serverCountryCode, QString &nativeConfig);
|
||||||
|
|
||||||
ErrorCode revokeNativeConfig(const QString &serverId, const QString &serverCountryCode);
|
ErrorCode revokeNativeConfig(int serverIndex, const QString &serverCountryCode);
|
||||||
|
|
||||||
ErrorCode prepareVpnKeyExport(const QString &serverId, QString &vpnKey);
|
ErrorCode updateServiceFromTelegram(int serverIndex);
|
||||||
|
|
||||||
ErrorCode validateAndUpdateConfig(const QString &serverId, bool hasInstalledContainers);
|
ErrorCode prepareVpnKeyExport(int serverIndex, QString &vpnKey);
|
||||||
|
|
||||||
void removeApiConfig(const QString &serverId);
|
ErrorCode validateAndUpdateConfig(int serverIndex, bool hasInstalledContainers);
|
||||||
|
|
||||||
bool removeServer(const QString &serverId);
|
void removeApiConfig(int serverIndex);
|
||||||
|
|
||||||
void setCurrentProtocol(const QString &serverId, const QString &protocolName);
|
void setCurrentProtocol(int serverIndex, const QString &protocolName);
|
||||||
bool isVlessProtocol(const QString &serverId) const;
|
bool isVlessProtocol(int serverIndex) const;
|
||||||
|
|
||||||
ErrorCode getAccountInfo(const QString &serverId, QJsonObject &accountInfo);
|
ErrorCode getAccountInfo(int serverIndex, QJsonObject &accountInfo);
|
||||||
QFuture<QPair<ErrorCode, QString>> getRenewalLink(const QString &serverId);
|
QFuture<QPair<ErrorCode, QString>> getRenewalLink(int serverIndex);
|
||||||
|
|
||||||
struct AppStoreRestoreResult
|
struct AppStoreRestoreResult
|
||||||
{
|
{
|
||||||
@@ -93,6 +98,7 @@ public:
|
|||||||
|
|
||||||
ErrorCode processAppStorePurchase(const QString &userCountryCode, const QString &serviceType,
|
ErrorCode processAppStorePurchase(const QString &userCountryCode, const QString &serviceType,
|
||||||
const QString &serviceProtocol, const QString &productId,
|
const QString &serviceProtocol, const QString &productId,
|
||||||
|
ServerConfig &serverConfig,
|
||||||
int *duplicateServerIndex = nullptr);
|
int *duplicateServerIndex = nullptr);
|
||||||
|
|
||||||
AppStoreRestoreResult processAppStoreRestore(const QString &userCountryCode, const QString &serviceType,
|
AppStoreRestoreResult processAppStoreRestore(const QString &userCountryCode, const QString &serviceType,
|
||||||
@@ -100,7 +106,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody, bool isTestPurchase = false);
|
ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody, bool isTestPurchase = false);
|
||||||
bool isApiKeyExpired(const QString &serverId) const;
|
bool isApiKeyExpired(int serverIndex) const;
|
||||||
|
|
||||||
ErrorCode extractServerConfigJsonFromResponse(const QByteArray &apiResponseBody, const QString &protocol,
|
ErrorCode extractServerConfigJsonFromResponse(const QByteArray &apiResponseBody, const QString &protocol,
|
||||||
const ProtocolData &protocolData, QJsonObject &serverConfigJson);
|
const ProtocolData &protocolData, QJsonObject &serverConfigJson);
|
||||||
|
|||||||
@@ -9,11 +9,11 @@
|
|||||||
#include "core/utils/constants/protocolConstants.h"
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
#include "core/utils/utilities.h"
|
#include "core/utils/utilities.h"
|
||||||
#include "core/utils/networkUtilities.h"
|
#include "core/utils/networkUtilities.h"
|
||||||
#include "core/utils/serverConfigUtils.h"
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "core/utils/containerEnum.h"
|
#include "core/utils/containerEnum.h"
|
||||||
#include "core/utils/containers/containerUtils.h"
|
#include "core/utils/containers/containerUtils.h"
|
||||||
#include "core/utils/protocolEnum.h"
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
#include "core/models/containerConfig.h"
|
#include "core/models/containerConfig.h"
|
||||||
#include "core/models/protocolConfig.h"
|
#include "core/models/protocolConfig.h"
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ void ConnectionController::setConnectionState(Vpn::ConnectionState state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ConnectionController::prepareConnection(const QString &serverId,
|
ErrorCode ConnectionController::prepareConnection(int serverIndex,
|
||||||
QJsonObject& vpnConfiguration,
|
QJsonObject& vpnConfiguration,
|
||||||
DockerContainer& container)
|
DockerContainer& container)
|
||||||
{
|
{
|
||||||
@@ -59,98 +59,35 @@ ErrorCode ConnectionController::prepareConnection(const QString &serverId,
|
|||||||
return ErrorCode::AmneziaServiceNotRunning;
|
return ErrorCode::AmneziaServiceNotRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContainerConfig containerConfigModel;
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
QPair<QString, QString> dns;
|
container = serverConfigModel.defaultContainer();
|
||||||
QString hostName;
|
|
||||||
QString description;
|
|
||||||
int configVersion = 0;
|
|
||||||
bool isApiConfig = false;
|
|
||||||
|
|
||||||
const auto kind = m_serversRepository->serverKind(serverId);
|
|
||||||
switch (kind) {
|
|
||||||
case serverConfigUtils::ConfigType::SelfHostedAdmin: {
|
|
||||||
const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId);
|
|
||||||
if (!cfg.has_value()) return ErrorCode::InternalError;
|
|
||||||
container = cfg->defaultContainer;
|
|
||||||
containerConfigModel = cfg->containerConfig(container);
|
|
||||||
dns = { cfg->dns1, cfg->dns2 };
|
|
||||||
hostName = cfg->hostName;
|
|
||||||
description = cfg->description;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::SelfHostedUser: {
|
|
||||||
const auto cfg = m_serversRepository->selfHostedUserConfig(serverId);
|
|
||||||
if (!cfg.has_value()) return ErrorCode::InternalError;
|
|
||||||
container = cfg->defaultContainer;
|
|
||||||
containerConfigModel = cfg->containerConfig(container);
|
|
||||||
dns = { cfg->dns1, cfg->dns2 };
|
|
||||||
hostName = cfg->hostName;
|
|
||||||
description = cfg->description;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::Native: {
|
|
||||||
const auto cfg = m_serversRepository->nativeConfig(serverId);
|
|
||||||
if (!cfg.has_value()) return ErrorCode::InternalError;
|
|
||||||
container = cfg->defaultContainer;
|
|
||||||
containerConfigModel = cfg->containerConfig(container);
|
|
||||||
dns = { cfg->dns1, cfg->dns2 };
|
|
||||||
hostName = cfg->hostName;
|
|
||||||
description = cfg->description;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaPremiumV2:
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaFreeV3:
|
|
||||||
case serverConfigUtils::ConfigType::ExternalPremium: {
|
|
||||||
const auto cfg = m_serversRepository->apiV2Config(serverId);
|
|
||||||
if (!cfg.has_value()) return ErrorCode::InternalError;
|
|
||||||
container = cfg->defaultContainer;
|
|
||||||
containerConfigModel = cfg->containerConfig(container);
|
|
||||||
dns = { cfg->dns1, cfg->dns2 };
|
|
||||||
hostName = cfg->hostName;
|
|
||||||
description = cfg->description;
|
|
||||||
configVersion = serverConfigUtils::ConfigSource::AmneziaGateway;
|
|
||||||
isApiConfig = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaPremiumV1:
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaFreeV2:
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
case serverConfigUtils::ConfigType::Invalid:
|
|
||||||
default:
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isContainerSupported(container)) {
|
if (!isContainerSupported(container)) {
|
||||||
return ErrorCode::NotSupportedOnThisPlatform;
|
return ErrorCode::NotSupportedOnThisPlatform;
|
||||||
}
|
}
|
||||||
if (dns.first.isEmpty() || !NetworkUtilities::checkIPv4Format(dns.first)) {
|
|
||||||
if (m_appSettingsRepository->useAmneziaDns()) {
|
|
||||||
dns.first = protocols::dns::amneziaDnsIp;
|
|
||||||
} else {
|
|
||||||
dns.first = m_appSettingsRepository->primaryDns();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (dns.second.isEmpty() || !NetworkUtilities::checkIPv4Format(dns.second)) {
|
|
||||||
dns.second = m_appSettingsRepository->secondaryDns();
|
|
||||||
}
|
|
||||||
|
|
||||||
vpnConfiguration = createConnectionConfiguration(dns, isApiConfig, hostName, description, configVersion,
|
ContainerConfig containerConfigModel = m_serversRepository->containerConfig(serverIndex, container);
|
||||||
containerConfigModel, container);
|
|
||||||
|
auto dns = serverConfigModel.getDnsPair(m_appSettingsRepository->useAmneziaDns(),
|
||||||
|
m_appSettingsRepository->primaryDns(),
|
||||||
|
m_appSettingsRepository->secondaryDns());
|
||||||
|
|
||||||
|
vpnConfiguration = createConnectionConfiguration(dns, serverConfigModel, containerConfigModel, container);
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ConnectionController::openConnection(const QString &serverId)
|
ErrorCode ConnectionController::openConnection(int serverIndex)
|
||||||
{
|
{
|
||||||
QJsonObject vpnConfiguration;
|
QJsonObject vpnConfiguration;
|
||||||
DockerContainer container;
|
DockerContainer container;
|
||||||
|
|
||||||
ErrorCode errorCode = prepareConnection(serverId, vpnConfiguration, container);
|
ErrorCode errorCode = prepareConnection(serverIndex, vpnConfiguration, container);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit openConnectionRequested(serverId, container, vpnConfiguration);
|
emit openConnectionRequested(serverIndex, container, vpnConfiguration);
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,10 +120,7 @@ ErrorCode ConnectionController::lastConnectionError() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject ConnectionController::createConnectionConfiguration(const QPair<QString, QString> &dns,
|
QJsonObject ConnectionController::createConnectionConfiguration(const QPair<QString, QString> &dns,
|
||||||
bool isApiConfig,
|
const ServerConfig &serverConfig,
|
||||||
const QString &hostName,
|
|
||||||
const QString &description,
|
|
||||||
int configVersion,
|
|
||||||
const ContainerConfig &containerConfig,
|
const ContainerConfig &containerConfig,
|
||||||
DockerContainer container)
|
DockerContainer container)
|
||||||
{
|
{
|
||||||
@@ -200,7 +134,7 @@ QJsonObject ConnectionController::createConnectionConfiguration(const QPair<QStr
|
|||||||
|
|
||||||
ConnectionSettings connectionSettings = {
|
ConnectionSettings connectionSettings = {
|
||||||
{ dns.first, dns.second },
|
{ dns.first, dns.second },
|
||||||
isApiConfig,
|
serverConfig.isApiConfig(),
|
||||||
{
|
{
|
||||||
m_appSettingsRepository->isSitesSplitTunnelingEnabled(),
|
m_appSettingsRepository->isSitesSplitTunnelingEnabled(),
|
||||||
m_appSettingsRepository->routeMode()
|
m_appSettingsRepository->routeMode()
|
||||||
@@ -226,9 +160,10 @@ QJsonObject ConnectionController::createConnectionConfiguration(const QPair<QStr
|
|||||||
vpnConfiguration[configKey::dns1] = dns.first;
|
vpnConfiguration[configKey::dns1] = dns.first;
|
||||||
vpnConfiguration[configKey::dns2] = dns.second;
|
vpnConfiguration[configKey::dns2] = dns.second;
|
||||||
|
|
||||||
vpnConfiguration[configKey::hostName] = hostName;
|
vpnConfiguration[configKey::hostName] = serverConfig.hostName();
|
||||||
vpnConfiguration[configKey::description] = description;
|
vpnConfiguration[configKey::description] = serverConfig.description();
|
||||||
vpnConfiguration[configKey::configVersion] = configVersion;
|
|
||||||
|
vpnConfiguration[configKey::configVersion] = serverConfig.configVersion();
|
||||||
|
|
||||||
return vpnConfiguration;
|
return vpnConfiguration;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ public:
|
|||||||
QObject* parent = nullptr);
|
QObject* parent = nullptr);
|
||||||
~ConnectionController() = default;
|
~ConnectionController() = default;
|
||||||
|
|
||||||
ErrorCode prepareConnection(const QString &serverId,
|
ErrorCode prepareConnection(int serverIndex,
|
||||||
QJsonObject& vpnConfiguration,
|
QJsonObject& vpnConfiguration,
|
||||||
DockerContainer& container);
|
DockerContainer& container);
|
||||||
|
|
||||||
ErrorCode openConnection(const QString &serverId);
|
ErrorCode openConnection(int serverIndex);
|
||||||
|
|
||||||
void closeConnection();
|
void closeConnection();
|
||||||
|
|
||||||
@@ -50,10 +50,7 @@ public:
|
|||||||
void setConnectionState(Vpn::ConnectionState state);
|
void setConnectionState(Vpn::ConnectionState state);
|
||||||
|
|
||||||
QJsonObject createConnectionConfiguration(const QPair<QString, QString> &dns,
|
QJsonObject createConnectionConfiguration(const QPair<QString, QString> &dns,
|
||||||
bool isApiConfig,
|
const ServerConfig &serverConfig,
|
||||||
const QString &hostName,
|
|
||||||
const QString &description,
|
|
||||||
int configVersion,
|
|
||||||
const ContainerConfig &containerConfig,
|
const ContainerConfig &containerConfig,
|
||||||
DockerContainer container);
|
DockerContainer container);
|
||||||
|
|
||||||
@@ -63,7 +60,7 @@ public:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void connectionStateChanged(Vpn::ConnectionState state);
|
void connectionStateChanged(Vpn::ConnectionState state);
|
||||||
void openConnectionRequested(const QString &serverId, DockerContainer container, const QJsonObject &vpnConfiguration);
|
void openConnectionRequested(int serverIndex, DockerContainer container, const QJsonObject &vpnConfiguration);
|
||||||
void closeConnectionRequested();
|
void closeConnectionRequested();
|
||||||
void setConnectionStateRequested(Vpn::ConnectionState state);
|
void setConnectionStateRequested(Vpn::ConnectionState state);
|
||||||
void killSwitchModeChangedRequested(bool enabled);
|
void killSwitchModeChangedRequested(bool enabled);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "core/controllers/selfhosted/installController.h"
|
#include "core/controllers/selfhosted/installController.h"
|
||||||
#include "core/controllers/selfhosted/importController.h"
|
#include "core/controllers/selfhosted/importController.h"
|
||||||
#include "core/controllers/coreSignalHandlers.h"
|
#include "core/controllers/coreSignalHandlers.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "secureQSettings.h"
|
#include "secureQSettings.h"
|
||||||
|
|
||||||
@@ -86,9 +87,6 @@ void CoreController::initModels()
|
|||||||
m_xrayConfigModel = new XrayConfigModel(this);
|
m_xrayConfigModel = new XrayConfigModel(this);
|
||||||
setQmlContextProperty("XrayConfigModel", m_xrayConfigModel);
|
setQmlContextProperty("XrayConfigModel", m_xrayConfigModel);
|
||||||
|
|
||||||
m_xrayConfigSnapshotsModel = new XrayConfigSnapshotsModel(m_appSettingsRepository, m_xrayConfigModel, this);
|
|
||||||
setQmlContextProperty("XrayConfigSnapshotsModel", m_xrayConfigSnapshotsModel);
|
|
||||||
|
|
||||||
m_torConfigModel = new TorConfigModel(this);
|
m_torConfigModel = new TorConfigModel(this);
|
||||||
setQmlContextProperty("TorConfigModel", m_torConfigModel);
|
setQmlContextProperty("TorConfigModel", m_torConfigModel);
|
||||||
|
|
||||||
@@ -103,12 +101,6 @@ void CoreController::initModels()
|
|||||||
m_socks5ConfigModel = new Socks5ProxyConfigModel(this);
|
m_socks5ConfigModel = new Socks5ProxyConfigModel(this);
|
||||||
setQmlContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel);
|
setQmlContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel);
|
||||||
|
|
||||||
m_mtProxyConfigModel = new MtProxyConfigModel(this);
|
|
||||||
setQmlContextProperty("MtProxyConfigModel", m_mtProxyConfigModel);
|
|
||||||
|
|
||||||
m_telemtConfigModel = new TelemtConfigModel(this);
|
|
||||||
setQmlContextProperty("TelemtConfigModel", m_telemtConfigModel);
|
|
||||||
|
|
||||||
m_clientManagementModel = new ClientManagementModel(this);
|
m_clientManagementModel = new ClientManagementModel(this);
|
||||||
setQmlContextProperty("ClientManagementModel", m_clientManagementModel);
|
setQmlContextProperty("ClientManagementModel", m_clientManagementModel);
|
||||||
|
|
||||||
@@ -153,8 +145,7 @@ void CoreController::initCoreControllers()
|
|||||||
m_allowedDnsController = new AllowedDnsController(m_appSettingsRepository);
|
m_allowedDnsController = new AllowedDnsController(m_appSettingsRepository);
|
||||||
m_servicesCatalogController = new ServicesCatalogController(m_appSettingsRepository);
|
m_servicesCatalogController = new ServicesCatalogController(m_appSettingsRepository);
|
||||||
m_subscriptionController = new SubscriptionController(m_serversRepository, m_appSettingsRepository);
|
m_subscriptionController = new SubscriptionController(m_serversRepository, m_appSettingsRepository);
|
||||||
m_newsController = new NewsController(m_appSettingsRepository, m_serversRepository);
|
m_newsController = new NewsController(m_appSettingsRepository, m_serversController);
|
||||||
m_updateController = new UpdateController(m_appSettingsRepository, this);
|
|
||||||
|
|
||||||
m_installController = new InstallController(m_serversRepository, m_appSettingsRepository, this);
|
m_installController = new InstallController(m_serversRepository, m_appSettingsRepository, this);
|
||||||
m_exportController = new ExportController(m_serversRepository, m_appSettingsRepository, this);
|
m_exportController = new ExportController(m_serversRepository, m_appSettingsRepository, this);
|
||||||
@@ -178,7 +169,7 @@ void CoreController::initControllers()
|
|||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
m_ikev2ConfigModel,
|
m_ikev2ConfigModel,
|
||||||
#endif
|
#endif
|
||||||
m_sftpConfigModel, m_socks5ConfigModel, m_mtProxyConfigModel, m_telemtConfigModel, this);
|
m_sftpConfigModel, m_socks5ConfigModel, this);
|
||||||
setQmlContextProperty("InstallController", m_installUiController);
|
setQmlContextProperty("InstallController", m_installUiController);
|
||||||
|
|
||||||
m_importController = new ImportUiController(m_importCoreController, this);
|
m_importController = new ImportUiController(m_importCoreController, this);
|
||||||
@@ -211,10 +202,6 @@ void CoreController::initControllers()
|
|||||||
m_systemController = new SystemController(this);
|
m_systemController = new SystemController(this);
|
||||||
setQmlContextProperty("SystemController", m_systemController);
|
setQmlContextProperty("SystemController", m_systemController);
|
||||||
|
|
||||||
m_networkReachabilityController = new NetworkReachabilityController(this);
|
|
||||||
m_engine->rootContext()->setContextProperty("NetworkReachabilityController", m_networkReachabilityController);
|
|
||||||
m_engine->rootContext()->setContextProperty("NetworkReachability", m_networkReachabilityController);
|
|
||||||
|
|
||||||
m_servicesCatalogUiController = new ServicesCatalogUiController(m_servicesCatalogController, m_apiServicesModel, this);
|
m_servicesCatalogUiController = new ServicesCatalogUiController(m_servicesCatalogController, m_apiServicesModel, this);
|
||||||
setQmlContextProperty("ServicesCatalogUiController", m_servicesCatalogUiController);
|
setQmlContextProperty("ServicesCatalogUiController", m_servicesCatalogUiController);
|
||||||
|
|
||||||
@@ -225,9 +212,6 @@ void CoreController::initControllers()
|
|||||||
|
|
||||||
m_apiNewsUiController = new ApiNewsUiController(m_newsModel, m_newsController, this);
|
m_apiNewsUiController = new ApiNewsUiController(m_newsModel, m_newsController, this);
|
||||||
setQmlContextProperty("ApiNewsController", m_apiNewsUiController);
|
setQmlContextProperty("ApiNewsController", m_apiNewsUiController);
|
||||||
|
|
||||||
m_updateUiController = new UpdateUiController(m_updateController, this);
|
|
||||||
setQmlContextProperty("UpdateController", m_updateUiController);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initAndroidController()
|
void CoreController::initAndroidController()
|
||||||
@@ -277,9 +261,6 @@ void CoreController::initSignalHandlers()
|
|||||||
|
|
||||||
// Trigger initial update after handlers are connected
|
// Trigger initial update after handlers are connected
|
||||||
m_serversUiController->updateModel();
|
m_serversUiController->updateModel();
|
||||||
if (m_serversUiController->hasServersFromGatewayApi()) {
|
|
||||||
m_apiNewsUiController->fetchNews(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::updateTranslator(const QLocale &locale)
|
void CoreController::updateTranslator(const QLocale &locale)
|
||||||
@@ -337,16 +318,11 @@ PageController* CoreController::pageController() const
|
|||||||
|
|
||||||
void CoreController::openConnectionByIndex(int serverIndex)
|
void CoreController::openConnectionByIndex(int serverIndex)
|
||||||
{
|
{
|
||||||
const QString serverId =
|
|
||||||
m_serversUiController ? m_serversUiController->getServerId(serverIndex) : QString();
|
|
||||||
if (serverId.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (m_serversModel) {
|
if (m_serversModel) {
|
||||||
m_serversModel->setProcessedServerIndex(serverIndex);
|
m_serversModel->setProcessedServerIndex(serverIndex);
|
||||||
}
|
}
|
||||||
if (m_serversController) {
|
if (m_serversController) {
|
||||||
m_serversController->setDefaultServer(serverId);
|
m_serversController->setDefaultServerIndex(serverIndex);
|
||||||
}
|
}
|
||||||
m_connectionUiController->toggleConnection();
|
m_connectionUiController->toggleConnection();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,7 @@
|
|||||||
#include "ui/controllers/ipSplitTunnelingUiController.h"
|
#include "ui/controllers/ipSplitTunnelingUiController.h"
|
||||||
#include "ui/controllers/systemController.h"
|
#include "ui/controllers/systemController.h"
|
||||||
#include "ui/controllers/languageUiController.h"
|
#include "ui/controllers/languageUiController.h"
|
||||||
#include "ui/controllers/updateUiController.h"
|
|
||||||
#include "ui/controllers/api/servicesCatalogUiController.h"
|
#include "ui/controllers/api/servicesCatalogUiController.h"
|
||||||
#include "ui/controllers/networkReachabilityController.h"
|
|
||||||
|
|
||||||
#include "core/controllers/serversController.h"
|
#include "core/controllers/serversController.h"
|
||||||
#include "core/controllers/selfhosted/usersController.h"
|
#include "core/controllers/selfhosted/usersController.h"
|
||||||
@@ -41,7 +39,6 @@
|
|||||||
#include "core/controllers/selfhosted/installController.h"
|
#include "core/controllers/selfhosted/installController.h"
|
||||||
#include "core/controllers/settingsController.h"
|
#include "core/controllers/settingsController.h"
|
||||||
#include "core/controllers/connectionController.h"
|
#include "core/controllers/connectionController.h"
|
||||||
#include "core/controllers/updateController.h"
|
|
||||||
|
|
||||||
#include "core/repositories/secureServersRepository.h"
|
#include "core/repositories/secureServersRepository.h"
|
||||||
#include "core/repositories/secureAppSettingsRepository.h"
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
@@ -65,15 +62,11 @@
|
|||||||
#include "ui/models/protocols/openvpnConfigModel.h"
|
#include "ui/models/protocols/openvpnConfigModel.h"
|
||||||
#include "ui/models/protocols/wireguardConfigModel.h"
|
#include "ui/models/protocols/wireguardConfigModel.h"
|
||||||
#include "ui/models/protocols/xrayConfigModel.h"
|
#include "ui/models/protocols/xrayConfigModel.h"
|
||||||
#include "ui/models/protocols/xrayConfigSnapshotsModel.h"
|
|
||||||
#include "ui/models/protocolsModel.h"
|
#include "ui/models/protocolsModel.h"
|
||||||
#include "ui/models/services/torConfigModel.h"
|
#include "ui/models/services/torConfigModel.h"
|
||||||
#include "ui/models/serversModel.h"
|
#include "ui/models/serversModel.h"
|
||||||
#include "ui/models/services/sftpConfigModel.h"
|
#include "ui/models/services/sftpConfigModel.h"
|
||||||
#include "ui/models/services/socks5ProxyConfigModel.h"
|
#include "ui/models/services/socks5ProxyConfigModel.h"
|
||||||
#include "ui/models/services/mtProxyConfigModel.h"
|
|
||||||
#include "ui/models/services/telemtConfigModel.h"
|
|
||||||
|
|
||||||
#include "ui/models/ipSplitTunnelingModel.h"
|
#include "ui/models/ipSplitTunnelingModel.h"
|
||||||
#include "ui/models/newsModel.h"
|
#include "ui/models/newsModel.h"
|
||||||
|
|
||||||
@@ -89,6 +82,7 @@ class TestDefaultServerChange;
|
|||||||
class TestServerEdgeCases;
|
class TestServerEdgeCases;
|
||||||
class TestSignalOrder;
|
class TestSignalOrder;
|
||||||
class TestServersModelSync;
|
class TestServersModelSync;
|
||||||
|
class TestGatewayStacks;
|
||||||
class TestComplexOperations;
|
class TestComplexOperations;
|
||||||
class TestSettingsSignals;
|
class TestSettingsSignals;
|
||||||
class TestUiServersModelAndController;
|
class TestUiServersModelAndController;
|
||||||
@@ -105,6 +99,7 @@ class CoreController : public QObject
|
|||||||
friend class TestServerEdgeCases;
|
friend class TestServerEdgeCases;
|
||||||
friend class TestSignalOrder;
|
friend class TestSignalOrder;
|
||||||
friend class TestServersModelSync;
|
friend class TestServersModelSync;
|
||||||
|
friend class TestGatewayStacks;
|
||||||
friend class TestComplexOperations;
|
friend class TestComplexOperations;
|
||||||
friend class TestSettingsSignals;
|
friend class TestSettingsSignals;
|
||||||
friend class TestUiServersModelAndController;
|
friend class TestUiServersModelAndController;
|
||||||
@@ -161,11 +156,9 @@ private:
|
|||||||
ServersUiController* m_serversUiController;
|
ServersUiController* m_serversUiController;
|
||||||
IpSplitTunnelingUiController* m_ipSplitTunnelingUiController;
|
IpSplitTunnelingUiController* m_ipSplitTunnelingUiController;
|
||||||
SystemController* m_systemController;
|
SystemController* m_systemController;
|
||||||
NetworkReachabilityController* m_networkReachabilityController;
|
|
||||||
AppSplitTunnelingUiController* m_appSplitTunnelingUiController;
|
AppSplitTunnelingUiController* m_appSplitTunnelingUiController;
|
||||||
AllowedDnsUiController* m_allowedDnsUiController;
|
AllowedDnsUiController* m_allowedDnsUiController;
|
||||||
LanguageUiController* m_languageUiController;
|
LanguageUiController* m_languageUiController;
|
||||||
UpdateUiController* m_updateUiController;
|
|
||||||
|
|
||||||
SubscriptionUiController* m_subscriptionUiController;
|
SubscriptionUiController* m_subscriptionUiController;
|
||||||
ApiNewsUiController* m_apiNewsUiController;
|
ApiNewsUiController* m_apiNewsUiController;
|
||||||
@@ -180,7 +173,6 @@ private:
|
|||||||
ServicesCatalogController* m_servicesCatalogController;
|
ServicesCatalogController* m_servicesCatalogController;
|
||||||
SubscriptionController* m_subscriptionController;
|
SubscriptionController* m_subscriptionController;
|
||||||
NewsController* m_newsController;
|
NewsController* m_newsController;
|
||||||
UpdateController* m_updateController;
|
|
||||||
InstallController* m_installController;
|
InstallController* m_installController;
|
||||||
ExportController* m_exportController;
|
ExportController* m_exportController;
|
||||||
ConnectionController* m_connectionController;
|
ConnectionController* m_connectionController;
|
||||||
@@ -206,7 +198,6 @@ private:
|
|||||||
|
|
||||||
OpenVpnConfigModel* m_openVpnConfigModel;
|
OpenVpnConfigModel* m_openVpnConfigModel;
|
||||||
XrayConfigModel* m_xrayConfigModel;
|
XrayConfigModel* m_xrayConfigModel;
|
||||||
XrayConfigSnapshotsModel* m_xrayConfigSnapshotsModel;
|
|
||||||
TorConfigModel* m_torConfigModel;
|
TorConfigModel* m_torConfigModel;
|
||||||
WireGuardConfigModel* m_wireGuardConfigModel;
|
WireGuardConfigModel* m_wireGuardConfigModel;
|
||||||
AwgConfigModel* m_awgConfigModel;
|
AwgConfigModel* m_awgConfigModel;
|
||||||
@@ -215,8 +206,6 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
SftpConfigModel* m_sftpConfigModel;
|
SftpConfigModel* m_sftpConfigModel;
|
||||||
Socks5ProxyConfigModel* m_socks5ConfigModel;
|
Socks5ProxyConfigModel* m_socks5ConfigModel;
|
||||||
MtProxyConfigModel* m_mtProxyConfigModel;
|
|
||||||
TelemtConfigModel* m_telemtConfigModel;
|
|
||||||
|
|
||||||
CoreSignalHandlers* m_signalHandlers;
|
CoreSignalHandlers* m_signalHandlers;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include "core/utils/routeModes.h"
|
#include "core/utils/routeModes.h"
|
||||||
#include "core/controllers/coreController.h"
|
#include "core/controllers/coreController.h"
|
||||||
#include "core/repositories/secureServersRepository.h"
|
#include "core/repositories/secureServersRepository.h"
|
||||||
#include "core/utils/serverConfigUtils.h"
|
|
||||||
#include "core/repositories/secureAppSettingsRepository.h"
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
#include "vpnConnection.h"
|
#include "vpnConnection.h"
|
||||||
#include "ui/controllers/qml/pageController.h"
|
#include "ui/controllers/qml/pageController.h"
|
||||||
@@ -21,7 +20,6 @@
|
|||||||
#include "ui/controllers/selfhosted/installUiController.h"
|
#include "ui/controllers/selfhosted/installUiController.h"
|
||||||
#include "ui/controllers/importUiController.h"
|
#include "ui/controllers/importUiController.h"
|
||||||
#include "ui/controllers/api/subscriptionUiController.h"
|
#include "ui/controllers/api/subscriptionUiController.h"
|
||||||
#include "ui/controllers/updateUiController.h"
|
|
||||||
#include "ui/models/serversModel.h"
|
#include "ui/models/serversModel.h"
|
||||||
#include "core/controllers/serversController.h"
|
#include "core/controllers/serversController.h"
|
||||||
#include "core/controllers/ipSplitTunnelingController.h"
|
#include "core/controllers/ipSplitTunnelingController.h"
|
||||||
@@ -66,6 +64,7 @@ void CoreSignalHandlers::initAllHandlers()
|
|||||||
initImportControllerHandler();
|
initImportControllerHandler();
|
||||||
initApiCountryModelUpdateHandler();
|
initApiCountryModelUpdateHandler();
|
||||||
initSubscriptionRefreshHandler();
|
initSubscriptionRefreshHandler();
|
||||||
|
initContainerModelUpdateHandler();
|
||||||
initAdminConfigRevokedHandler();
|
initAdminConfigRevokedHandler();
|
||||||
initPassphraseRequestHandler();
|
initPassphraseRequestHandler();
|
||||||
initTranslationsUpdatedHandler();
|
initTranslationsUpdatedHandler();
|
||||||
@@ -78,14 +77,12 @@ void CoreSignalHandlers::initAllHandlers()
|
|||||||
initAllowedDnsModelUpdateHandler();
|
initAllowedDnsModelUpdateHandler();
|
||||||
initAppSplitTunnelingModelUpdateHandler();
|
initAppSplitTunnelingModelUpdateHandler();
|
||||||
initPrepareConfigHandler();
|
initPrepareConfigHandler();
|
||||||
initUnsupportedConnectDrawerHandler();
|
|
||||||
initStrictKillSwitchHandler();
|
initStrictKillSwitchHandler();
|
||||||
initAndroidSettingsHandler();
|
initAndroidSettingsHandler();
|
||||||
initAndroidConnectionHandler();
|
initAndroidConnectionHandler();
|
||||||
initIosImportHandler();
|
initIosImportHandler();
|
||||||
initIosSettingsHandler();
|
initIosSettingsHandler();
|
||||||
initNotificationHandler();
|
initNotificationHandler();
|
||||||
initUpdateFoundHandler();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreSignalHandlers::initErrorMessagesHandler()
|
void CoreSignalHandlers::initErrorMessagesHandler()
|
||||||
@@ -125,9 +122,11 @@ void CoreSignalHandlers::initInstallControllerHandler()
|
|||||||
{
|
{
|
||||||
connect(m_coreController->m_installController, &InstallController::serverIsBusy, m_coreController->m_installUiController, &InstallUiController::serverIsBusy);
|
connect(m_coreController->m_installController, &InstallController::serverIsBusy, m_coreController->m_installUiController, &InstallUiController::serverIsBusy);
|
||||||
connect(m_coreController->m_installUiController, &InstallUiController::cancelInstallation, m_coreController->m_installController, &InstallController::cancelInstallation);
|
connect(m_coreController->m_installUiController, &InstallUiController::cancelInstallation, m_coreController->m_installController, &InstallController::cancelInstallation);
|
||||||
|
connect(m_coreController->m_installUiController, &InstallUiController::currentContainerUpdated, m_coreController->m_connectionUiController,
|
||||||
|
&ConnectionUiController::onCurrentContainerUpdated);
|
||||||
connect(m_coreController->m_serversUiController, &ServersUiController::processedServerIndexChanged,
|
connect(m_coreController->m_serversUiController, &ServersUiController::processedServerIndexChanged,
|
||||||
m_coreController->m_installUiController, [this](int serverIndex) {
|
m_coreController->m_installUiController, [this](int index) {
|
||||||
if (serverIndex >= 0) {
|
if (index >= 0) {
|
||||||
m_coreController->m_installUiController->clearProcessedServerCredentials();
|
m_coreController->m_installUiController->clearProcessedServerCredentials();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -136,20 +135,20 @@ void CoreSignalHandlers::initInstallControllerHandler()
|
|||||||
void CoreSignalHandlers::initExportControllerHandler()
|
void CoreSignalHandlers::initExportControllerHandler()
|
||||||
{
|
{
|
||||||
connect(m_coreController->m_exportController, &ExportController::appendClientRequested, this,
|
connect(m_coreController->m_exportController, &ExportController::appendClientRequested, this,
|
||||||
[this](const QString &serverId, const QString &clientId, const QString &clientName, DockerContainer container) {
|
[this](int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container) {
|
||||||
m_coreController->m_usersController->appendClient(serverId, clientId, clientName, container);
|
m_coreController->m_usersController->appendClient(serverIndex, clientId, clientName, container);
|
||||||
});
|
});
|
||||||
connect(m_coreController->m_exportController, &ExportController::updateClientsRequested, this,
|
connect(m_coreController->m_exportController, &ExportController::updateClientsRequested, this,
|
||||||
[this](const QString &serverId, DockerContainer container) {
|
[this](int serverIndex, DockerContainer container) {
|
||||||
m_coreController->m_usersController->updateClients(serverId, container);
|
m_coreController->m_usersController->updateClients(serverIndex, container);
|
||||||
});
|
});
|
||||||
connect(m_coreController->m_exportController, &ExportController::revokeClientRequested, this,
|
connect(m_coreController->m_exportController, &ExportController::revokeClientRequested, this,
|
||||||
[this](const QString &serverId, int row, DockerContainer container) {
|
[this](int serverIndex, int row, DockerContainer container) {
|
||||||
m_coreController->m_usersController->revokeClient(serverId, row, container);
|
m_coreController->m_usersController->revokeClient(serverIndex, row, container);
|
||||||
});
|
});
|
||||||
connect(m_coreController->m_exportController, &ExportController::renameClientRequested, this,
|
connect(m_coreController->m_exportController, &ExportController::renameClientRequested, this,
|
||||||
[this](const QString &serverId, int row, const QString &clientName, DockerContainer container) {
|
[this](int serverIndex, int row, const QString &clientName, DockerContainer container) {
|
||||||
m_coreController->m_usersController->renameClient(serverId, row, clientName, container);
|
m_coreController->m_usersController->renameClient(serverIndex, row, clientName, container);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,12 +157,9 @@ void CoreSignalHandlers::initImportControllerHandler()
|
|||||||
connect(m_coreController->m_importCoreController, &ImportController::importFinished, this, [this]() {
|
connect(m_coreController->m_importCoreController, &ImportController::importFinished, this, [this]() {
|
||||||
if (!m_coreController->m_connectionController->isConnected()) {
|
if (!m_coreController->m_connectionController->isConnected()) {
|
||||||
int newServerIndex = m_coreController->m_serversController->getServersCount() - 1;
|
int newServerIndex = m_coreController->m_serversController->getServersCount() - 1;
|
||||||
const QString serverId = m_coreController->m_serversController->getServerId(newServerIndex);
|
m_coreController->m_serversController->setDefaultServerIndex(newServerIndex);
|
||||||
if (!serverId.isEmpty()) {
|
|
||||||
m_coreController->m_serversController->setDefaultServer(serverId);
|
|
||||||
}
|
|
||||||
if (m_coreController->m_serversUiController) {
|
if (m_coreController->m_serversUiController) {
|
||||||
m_coreController->m_serversUiController->setProcessedServerId(serverId);
|
m_coreController->m_serversUiController->setProcessedServerIndex(newServerIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -172,19 +168,22 @@ void CoreSignalHandlers::initImportControllerHandler()
|
|||||||
void CoreSignalHandlers::initApiCountryModelUpdateHandler()
|
void CoreSignalHandlers::initApiCountryModelUpdateHandler()
|
||||||
{
|
{
|
||||||
connect(m_coreController->m_serversUiController, &ServersUiController::updateApiCountryModel, this, [this]() {
|
connect(m_coreController->m_serversUiController, &ServersUiController::updateApiCountryModel, this, [this]() {
|
||||||
const QString processedServerId = m_coreController->m_serversUiController->getProcessedServerId();
|
int processedIndex = m_coreController->m_serversUiController->getProcessedServerIndex();
|
||||||
if (processedServerId.isEmpty()) {
|
if (processedIndex < 0 || processedIndex >= m_coreController->m_serversRepository->serversCount()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ServerConfig server = m_coreController->m_serversRepository->server(processedIndex);
|
||||||
QJsonArray availableCountries;
|
QJsonArray availableCountries;
|
||||||
QString serverCountryCode;
|
QString serverCountryCode;
|
||||||
|
|
||||||
const auto apiV2 = m_coreController->m_serversRepository->apiV2Config(processedServerId);
|
if (server.isApiV2()) {
|
||||||
if (apiV2.has_value()) {
|
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
|
||||||
|
if (apiV2) {
|
||||||
availableCountries = apiV2->apiConfig.availableCountries;
|
availableCountries = apiV2->apiConfig.availableCountries;
|
||||||
serverCountryCode = apiV2->apiConfig.serverCountryCode;
|
serverCountryCode = apiV2->apiConfig.serverCountryCode;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_coreController->m_apiCountryModel->updateModel(availableCountries, serverCountryCode);
|
m_coreController->m_apiCountryModel->updateModel(availableCountries, serverCountryCode);
|
||||||
});
|
});
|
||||||
@@ -193,9 +192,18 @@ void CoreSignalHandlers::initApiCountryModelUpdateHandler()
|
|||||||
void CoreSignalHandlers::initSubscriptionRefreshHandler()
|
void CoreSignalHandlers::initSubscriptionRefreshHandler()
|
||||||
{
|
{
|
||||||
connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::subscriptionRefreshNeeded, this, [this]() {
|
connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::subscriptionRefreshNeeded, this, [this]() {
|
||||||
const QString defaultServerId = m_coreController->m_serversController->getDefaultServerId();
|
const int defaultServerIndex = m_coreController->m_serversController->getDefaultServerIndex();
|
||||||
if (!defaultServerId.isEmpty()) {
|
if (defaultServerIndex >= 0) {
|
||||||
m_coreController->m_subscriptionUiController->getAccountInfo(defaultServerId, false);
|
m_coreController->m_subscriptionUiController->getAccountInfo(defaultServerIndex, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initContainerModelUpdateHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_serversController, &ServersController::gatewayStacksExpanded, this, [this]() {
|
||||||
|
if (m_coreController->m_serversUiController->hasServersFromGatewayApi()) {
|
||||||
|
m_coreController->m_apiNewsUiController->fetchNews(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -203,17 +211,17 @@ void CoreSignalHandlers::initSubscriptionRefreshHandler()
|
|||||||
void CoreSignalHandlers::initAdminConfigRevokedHandler()
|
void CoreSignalHandlers::initAdminConfigRevokedHandler()
|
||||||
{
|
{
|
||||||
connect(m_coreController->m_installController, &InstallController::clientRevocationRequested, this,
|
connect(m_coreController->m_installController, &InstallController::clientRevocationRequested, this,
|
||||||
[this](const QString &serverId, const ContainerConfig &containerConfig, DockerContainer container) {
|
[this](int serverIndex, const ContainerConfig &containerConfig, DockerContainer container) {
|
||||||
m_coreController->m_usersController->revokeClient(serverId, containerConfig, container);
|
m_coreController->m_usersController->revokeClient(serverIndex, containerConfig, container);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(m_coreController->m_installController, &InstallController::clientAppendRequested, this,
|
connect(m_coreController->m_installController, &InstallController::clientAppendRequested, this,
|
||||||
[this](const QString &serverId, const QString &clientId, const QString &clientName, DockerContainer container) {
|
[this](int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container) {
|
||||||
m_coreController->m_usersController->appendClient(serverId, clientId, clientName, container);
|
m_coreController->m_usersController->appendClient(serverIndex, clientId, clientName, container);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(m_coreController->m_usersController, &UsersController::adminConfigRevoked, m_coreController->m_installController,
|
connect(m_coreController->m_usersController, &UsersController::adminConfigRevoked, m_coreController->m_serversController,
|
||||||
&InstallController::clearCachedProfile);
|
&ServersController::clearCachedProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreSignalHandlers::initPassphraseRequestHandler()
|
void CoreSignalHandlers::initPassphraseRequestHandler()
|
||||||
@@ -241,8 +249,7 @@ void CoreSignalHandlers::initLanguageHandler()
|
|||||||
|
|
||||||
void CoreSignalHandlers::initAutoConnectHandler()
|
void CoreSignalHandlers::initAutoConnectHandler()
|
||||||
{
|
{
|
||||||
if (m_coreController->m_settingsUiController->isAutoConnectEnabled()
|
if (m_coreController->m_settingsUiController->isAutoConnectEnabled() && m_coreController->m_serversController->getDefaultServerIndex() >= 0) {
|
||||||
&& !m_coreController->m_serversController->getDefaultServerId().isEmpty()) {
|
|
||||||
QTimer::singleShot(1000, this, [this]() { m_coreController->m_connectionUiController->openConnection(); });
|
QTimer::singleShot(1000, this, [this]() { m_coreController->m_connectionUiController->openConnection(); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,19 +270,15 @@ void CoreSignalHandlers::initServersModelUpdateHandler()
|
|||||||
connect(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged,
|
connect(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged,
|
||||||
m_coreController->m_serversUiController, &ServersUiController::onDefaultServerChanged);
|
m_coreController->m_serversUiController, &ServersUiController::onDefaultServerChanged);
|
||||||
|
|
||||||
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded, this,
|
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded,
|
||||||
[this](const QString &serverId) {
|
m_coreController->m_serversController, &ServersController::recomputeGatewayStacks);
|
||||||
if (m_coreController->m_serversRepository->apiV2Config(serverId).has_value()) {
|
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverEdited,
|
||||||
m_coreController->m_apiNewsUiController->fetchNews(false);
|
m_coreController->m_serversController, &ServersController::recomputeGatewayStacks);
|
||||||
}
|
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved,
|
||||||
});
|
m_coreController->m_serversController, &ServersController::recomputeGatewayStacks);
|
||||||
|
|
||||||
connect(m_coreController->m_settingsUiController, &SettingsUiController::restoreBackupFinished, this, [this]() {
|
connect(m_coreController->m_settingsUiController, &SettingsUiController::restoreBackupFinished,
|
||||||
m_coreController->m_serversUiController->updateModel();
|
m_coreController->m_serversUiController, &ServersUiController::updateModel);
|
||||||
if (m_coreController->m_serversUiController->hasServersFromGatewayApi()) {
|
|
||||||
m_coreController->m_apiNewsUiController->fetchNews(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreSignalHandlers::initClientManagementModelUpdateHandler()
|
void CoreSignalHandlers::initClientManagementModelUpdateHandler()
|
||||||
@@ -310,19 +313,7 @@ void CoreSignalHandlers::initPrepareConfigHandler()
|
|||||||
connect(m_coreController->m_connectionUiController, &ConnectionUiController::prepareConfig, this, [this]() {
|
connect(m_coreController->m_connectionUiController, &ConnectionUiController::prepareConfig, this, [this]() {
|
||||||
m_coreController->m_connectionController->setConnectionState(Vpn::ConnectionState::Preparing);
|
m_coreController->m_connectionController->setConnectionState(Vpn::ConnectionState::Preparing);
|
||||||
|
|
||||||
const QString serverId = m_coreController->m_serversController->getDefaultServerId();
|
|
||||||
if (serverId.isEmpty()) {
|
|
||||||
m_coreController->m_connectionController->setConnectionState(Vpn::ConnectionState::Disconnected);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const serverConfigUtils::ConfigType kind = m_coreController->m_serversRepository->serverKind(serverId);
|
|
||||||
|
|
||||||
if (serverConfigUtils::isApiV2Subscription(kind) || serverConfigUtils::isLegacyApiSubscription(kind)) {
|
|
||||||
m_coreController->m_subscriptionUiController->validateConfig();
|
m_coreController->m_subscriptionUiController->validateConfig();
|
||||||
} else {
|
|
||||||
m_coreController->m_installUiController->validateConfig();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::configValidated, this, [this](bool isValid) {
|
connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::configValidated, this, [this](bool isValid) {
|
||||||
@@ -331,7 +322,7 @@ void CoreSignalHandlers::initPrepareConfigHandler()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_coreController->m_connectionUiController->openConnection();
|
m_coreController->m_installUiController->validateConfig();
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(m_coreController->m_installUiController, &InstallUiController::configValidated, this, [this](bool isValid) {
|
connect(m_coreController->m_installUiController, &InstallUiController::configValidated, this, [this](bool isValid) {
|
||||||
@@ -344,12 +335,6 @@ void CoreSignalHandlers::initPrepareConfigHandler()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreSignalHandlers::initUnsupportedConnectDrawerHandler()
|
|
||||||
{
|
|
||||||
connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::unsupportedConnectDrawerRequested,
|
|
||||||
m_coreController->m_pageController, &PageController::unsupportedConnectDrawerRequested);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreSignalHandlers::initStrictKillSwitchHandler()
|
void CoreSignalHandlers::initStrictKillSwitchHandler()
|
||||||
{
|
{
|
||||||
connect(m_coreController->m_settingsUiController, &SettingsUiController::strictKillSwitchEnabledChanged, m_coreController->m_connectionController,
|
connect(m_coreController->m_settingsUiController, &SettingsUiController::strictKillSwitchEnabledChanged, m_coreController->m_connectionController,
|
||||||
@@ -361,10 +346,7 @@ void CoreSignalHandlers::initAndroidSettingsHandler()
|
|||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::saveLogsChanged, AndroidController::instance(), &AndroidController::setSaveLogs);
|
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::saveLogsChanged, AndroidController::instance(), &AndroidController::setSaveLogs);
|
||||||
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::screenshotsEnabledChanged, AndroidController::instance(), &AndroidController::setScreenshotsEnabled);
|
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::screenshotsEnabledChanged, AndroidController::instance(), &AndroidController::setScreenshotsEnabled);
|
||||||
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved, this,
|
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved, AndroidController::instance(), &AndroidController::resetLastServer);
|
||||||
[](const QString &/*serverId*/, int removedIndex) {
|
|
||||||
AndroidController::instance()->resetLastServer(removedIndex);
|
|
||||||
});
|
|
||||||
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::settingsCleared, []() { AndroidController::instance()->resetLastServer(-1); });
|
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::settingsCleared, []() { AndroidController::instance()->resetLastServer(-1); });
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -428,19 +410,3 @@ void CoreSignalHandlers::initNotificationHandler()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreSignalHandlers::initUpdateFoundHandler()
|
|
||||||
{
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
|
||||||
connect(m_coreController->m_apiNewsUiController, &ApiNewsUiController::fetchNewsFinished, m_coreController->m_updateUiController,
|
|
||||||
&UpdateUiController::checkForUpdates);
|
|
||||||
|
|
||||||
connect(m_coreController->m_updateUiController, &UpdateUiController::updateFound, this, [this]() {
|
|
||||||
const QString version = m_coreController->m_updateUiController->getVersion();
|
|
||||||
const QString updateId = version.isEmpty() ? QStringLiteral("update") : QStringLiteral("update-%1").arg(version);
|
|
||||||
m_coreController->m_newsModel->setUpdateNotification(
|
|
||||||
updateId, m_coreController->m_updateUiController->getHeaderText(), m_coreController->m_updateUiController->getChangelogText());
|
|
||||||
emit m_coreController->m_pageController->showChangelogDrawer();
|
|
||||||
});
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ private:
|
|||||||
void initImportControllerHandler();
|
void initImportControllerHandler();
|
||||||
void initApiCountryModelUpdateHandler();
|
void initApiCountryModelUpdateHandler();
|
||||||
void initSubscriptionRefreshHandler();
|
void initSubscriptionRefreshHandler();
|
||||||
|
void initContainerModelUpdateHandler();
|
||||||
void initAdminConfigRevokedHandler();
|
void initAdminConfigRevokedHandler();
|
||||||
void initPassphraseRequestHandler();
|
void initPassphraseRequestHandler();
|
||||||
void initTranslationsUpdatedHandler();
|
void initTranslationsUpdatedHandler();
|
||||||
@@ -33,14 +34,12 @@ private:
|
|||||||
void initAllowedDnsModelUpdateHandler();
|
void initAllowedDnsModelUpdateHandler();
|
||||||
void initAppSplitTunnelingModelUpdateHandler();
|
void initAppSplitTunnelingModelUpdateHandler();
|
||||||
void initPrepareConfigHandler();
|
void initPrepareConfigHandler();
|
||||||
void initUnsupportedConnectDrawerHandler();
|
|
||||||
void initStrictKillSwitchHandler();
|
void initStrictKillSwitchHandler();
|
||||||
void initAndroidSettingsHandler();
|
void initAndroidSettingsHandler();
|
||||||
void initAndroidConnectionHandler();
|
void initAndroidConnectionHandler();
|
||||||
void initIosImportHandler();
|
void initIosImportHandler();
|
||||||
void initIosSettingsHandler();
|
void initIosSettingsHandler();
|
||||||
void initNotificationHandler();
|
void initNotificationHandler();
|
||||||
void initUpdateFoundHandler();
|
|
||||||
|
|
||||||
CoreController* m_coreController;
|
CoreController* m_coreController;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ QFuture<QPair<ErrorCode, QByteArray>> GatewayController::postAsync(const QString
|
|||||||
|
|
||||||
connect(reply, &QNetworkReply::sslErrors, [sslErrors](const QList<QSslError> &errors) { *sslErrors = errors; });
|
connect(reply, &QNetworkReply::sslErrors, [sslErrors](const QList<QSslError> &errors) { *sslErrors = errors; });
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::finished, this, [promise, sslErrors, encRequestData, endpoint, apiPayload, reply, this]() mutable {
|
connect(reply, &QNetworkReply::finished, reply, [promise, sslErrors, encRequestData, endpoint, apiPayload, reply, this]() mutable {
|
||||||
QByteArray encryptedResponseBody = reply->readAll();
|
QByteArray encryptedResponseBody = reply->readAll();
|
||||||
QString replyErrorString = reply->errorString();
|
QString replyErrorString = reply->errorString();
|
||||||
auto replyError = reply->error();
|
auto replyError = reply->error();
|
||||||
|
|||||||
@@ -5,13 +5,14 @@
|
|||||||
|
|
||||||
#include "core/configurators/configuratorBase.h"
|
#include "core/configurators/configuratorBase.h"
|
||||||
#include "core/utils/selfhosted/sshSession.h"
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
|
#include "core/utils/networkUtilities.h"
|
||||||
#include "core/utils/qrCodeUtils.h"
|
#include "core/utils/qrCodeUtils.h"
|
||||||
#include "core/utils/serialization/serialization.h"
|
#include "core/utils/serialization/serialization.h"
|
||||||
#include "core/utils/protocolEnum.h"
|
#include "core/utils/protocolEnum.h"
|
||||||
#include "core/protocols/protocolUtils.h"
|
#include "core/protocols/protocolUtils.h"
|
||||||
#include "core/utils/constants/configKeys.h"
|
#include "core/utils/constants/configKeys.h"
|
||||||
#include "core/utils/constants/protocolConstants.h"
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
#include "core/models/selfhosted/selfHostedAdminServerConfig.h"
|
#include "core/models/serverConfig.h"
|
||||||
#include "core/models/containerConfig.h"
|
#include "core/models/containerConfig.h"
|
||||||
#include "core/models/protocolConfig.h"
|
#include "core/models/protocolConfig.h"
|
||||||
|
|
||||||
@@ -26,20 +27,18 @@ ExportController::ExportController(SecureServersRepository* serversRepository,
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ExportController::ExportResult ExportController::generateFullAccessConfig(const QString &serverId)
|
ExportController::ExportResult ExportController::generateFullAccessConfig(int serverIndex)
|
||||||
{
|
{
|
||||||
ExportResult result;
|
ExportResult result;
|
||||||
|
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
|
||||||
if (!adminConfig.has_value()) {
|
serverConfig.visit([](auto& arg) {
|
||||||
result.errorCode = ErrorCode::InternalError;
|
for (auto it = arg.containers.begin(); it != arg.containers.end(); ++it) {
|
||||||
return result;
|
|
||||||
}
|
|
||||||
for (auto it = adminConfig->containers.begin(); it != adminConfig->containers.end(); ++it) {
|
|
||||||
it.value().protocolConfig.clearClientConfig();
|
it.value().protocolConfig.clearClientConfig();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
QJsonObject serverJson = adminConfig->toJson();
|
QJsonObject serverJson = serverConfig.toJson();
|
||||||
QByteArray compressedConfig = QJsonDocument(serverJson).toJson();
|
QByteArray compressedConfig = QJsonDocument(serverJson).toJson();
|
||||||
compressedConfig = qCompress(compressedConfig, 8);
|
compressedConfig = qCompress(compressedConfig, 8);
|
||||||
result.config = generateVpnUrl(compressedConfig);
|
result.config = generateVpnUrl(compressedConfig);
|
||||||
@@ -48,22 +47,13 @@ ExportController::ExportResult ExportController::generateFullAccessConfig(const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExportController::ExportResult ExportController::generateConnectionConfig(const QString &serverId, int containerIndex, const QString &clientName)
|
ExportController::ExportResult ExportController::generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName)
|
||||||
{
|
{
|
||||||
ExportResult result;
|
ExportResult result;
|
||||||
|
|
||||||
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
if (!adminConfig.has_value()) {
|
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container);
|
||||||
result.errorCode = ErrorCode::InternalError;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
const ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
result.errorCode = ErrorCode::InternalError;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
ContainerConfig containerConfig = adminConfig->containerConfig(container);
|
|
||||||
|
|
||||||
if (ContainerUtils::containerService(container) != ServiceType::Other) {
|
if (ContainerUtils::containerService(container) != ServiceType::Other) {
|
||||||
SshSession sshSession;
|
SshSession sshSession;
|
||||||
@@ -84,25 +74,35 @@ ExportController::ExportResult ExportController::generateConnectionConfig(const
|
|||||||
|
|
||||||
QString clientId = newProtocolConfig.clientId();
|
QString clientId = newProtocolConfig.clientId();
|
||||||
if (!clientId.isEmpty()) {
|
if (!clientId.isEmpty()) {
|
||||||
emit appendClientRequested(serverId, clientId, clientName, container);
|
emit appendClientRequested(serverIndex, clientId, clientName, container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const QPair<QString, QString> dns = adminConfig->getDnsPair(m_appSettingsRepository->useAmneziaDns(),
|
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
|
||||||
|
serverConfig.visit([container, containerConfig](auto& arg) {
|
||||||
|
arg.containers.clear();
|
||||||
|
arg.containers[container] = containerConfig;
|
||||||
|
arg.defaultContainer = container;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (serverConfig.isSelfHosted()) {
|
||||||
|
SelfHostedServerConfig* selfHosted = serverConfig.as<SelfHostedServerConfig>();
|
||||||
|
if (selfHosted) {
|
||||||
|
selfHosted->userName.reset();
|
||||||
|
selfHosted->password.reset();
|
||||||
|
selfHosted->port.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dns = serverConfig.getDnsPair(m_appSettingsRepository->useAmneziaDns(),
|
||||||
m_appSettingsRepository->primaryDns(),
|
m_appSettingsRepository->primaryDns(),
|
||||||
m_appSettingsRepository->secondaryDns());
|
m_appSettingsRepository->secondaryDns());
|
||||||
|
serverConfig.visit([&dns](auto& arg) {
|
||||||
|
arg.dns1 = dns.first;
|
||||||
|
arg.dns2 = dns.second;
|
||||||
|
});
|
||||||
|
|
||||||
adminConfig->containers.clear();
|
QJsonObject serverJson = serverConfig.toJson();
|
||||||
adminConfig->containers[container] = containerConfig;
|
|
||||||
adminConfig->defaultContainer = container;
|
|
||||||
adminConfig->userName.clear();
|
|
||||||
adminConfig->password.clear();
|
|
||||||
adminConfig->port = 0;
|
|
||||||
|
|
||||||
adminConfig->dns1 = dns.first;
|
|
||||||
adminConfig->dns2 = dns.second;
|
|
||||||
|
|
||||||
QJsonObject serverJson = adminConfig->toJson();
|
|
||||||
QByteArray compressedConfig = QJsonDocument(serverJson).toJson();
|
QByteArray compressedConfig = QJsonDocument(serverJson).toJson();
|
||||||
compressedConfig = qCompress(compressedConfig, 8);
|
compressedConfig = qCompress(compressedConfig, 8);
|
||||||
result.config = generateVpnUrl(compressedConfig);
|
result.config = generateVpnUrl(compressedConfig);
|
||||||
@@ -111,7 +111,7 @@ ExportController::ExportResult ExportController::generateConnectionConfig(const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExportController::NativeConfigResult ExportController::generateNativeConfig(const QString &serverId, DockerContainer container,
|
ExportController::NativeConfigResult ExportController::generateNativeConfig(int serverIndex, DockerContainer container,
|
||||||
const ContainerConfig &containerConfig,
|
const ContainerConfig &containerConfig,
|
||||||
const QString &clientName)
|
const QString &clientName)
|
||||||
{
|
{
|
||||||
@@ -123,17 +123,9 @@ ExportController::NativeConfigResult ExportController::generateNativeConfig(cons
|
|||||||
|
|
||||||
Proto protocol = ContainerUtils::defaultProtocol(container);
|
Proto protocol = ContainerUtils::defaultProtocol(container);
|
||||||
|
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
if (!adminConfig.has_value()) {
|
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
|
||||||
result.errorCode = ErrorCode::InternalError;
|
auto dns = serverConfig.getDnsPair(m_appSettingsRepository->useAmneziaDns(),
|
||||||
return result;
|
|
||||||
}
|
|
||||||
const ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
result.errorCode = ErrorCode::InternalError;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
const QPair<QString, QString> dns = adminConfig->getDnsPair(m_appSettingsRepository->useAmneziaDns(),
|
|
||||||
m_appSettingsRepository->primaryDns(),
|
m_appSettingsRepository->primaryDns(),
|
||||||
m_appSettingsRepository->secondaryDns());
|
m_appSettingsRepository->secondaryDns());
|
||||||
|
|
||||||
@@ -165,25 +157,20 @@ ExportController::NativeConfigResult ExportController::generateNativeConfig(cons
|
|||||||
if (protocol == Proto::OpenVpn || protocol == Proto::WireGuard || protocol == Proto::Awg || protocol == Proto::Xray) {
|
if (protocol == Proto::OpenVpn || protocol == Proto::WireGuard || protocol == Proto::Awg || protocol == Proto::Xray) {
|
||||||
QString clientId = newProtocolConfig.clientId();
|
QString clientId = newProtocolConfig.clientId();
|
||||||
if (!clientId.isEmpty()) {
|
if (!clientId.isEmpty()) {
|
||||||
emit appendClientRequested(serverId, clientId, clientName, container);
|
emit appendClientRequested(serverIndex, clientId, clientName, container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExportController::ExportResult ExportController::generateOpenVpnConfig(const QString &serverId, const QString &clientName)
|
ExportController::ExportResult ExportController::generateOpenVpnConfig(int serverIndex, const QString &clientName)
|
||||||
{
|
{
|
||||||
ExportResult result;
|
ExportResult result;
|
||||||
|
|
||||||
DockerContainer container = DockerContainer::OpenVpn;
|
DockerContainer container = DockerContainer::OpenVpn;
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container);
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
result.errorCode = ErrorCode::InternalError;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
ContainerConfig containerConfig = adminConfig->containerConfig(container);
|
|
||||||
|
|
||||||
auto nativeResult = generateNativeConfig(serverId, container, containerConfig, clientName);
|
auto nativeResult = generateNativeConfig(serverIndex, container, containerConfig, clientName);
|
||||||
if (nativeResult.errorCode != ErrorCode::NoError) {
|
if (nativeResult.errorCode != ErrorCode::NoError) {
|
||||||
result.errorCode = nativeResult.errorCode;
|
result.errorCode = nativeResult.errorCode;
|
||||||
return result;
|
return result;
|
||||||
@@ -198,18 +185,13 @@ ExportController::ExportResult ExportController::generateOpenVpnConfig(const QSt
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExportController::ExportResult ExportController::generateWireGuardConfig(const QString &serverId, const QString &clientName)
|
ExportController::ExportResult ExportController::generateWireGuardConfig(int serverIndex, const QString &clientName)
|
||||||
{
|
{
|
||||||
ExportResult result;
|
ExportResult result;
|
||||||
|
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, DockerContainer::WireGuard);
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
result.errorCode = ErrorCode::InternalError;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
ContainerConfig containerConfig = adminConfig->containerConfig(DockerContainer::WireGuard);
|
|
||||||
|
|
||||||
auto nativeResult = generateNativeConfig(serverId, DockerContainer::WireGuard, containerConfig, clientName);
|
auto nativeResult = generateNativeConfig(serverIndex, DockerContainer::WireGuard, containerConfig, clientName);
|
||||||
if (nativeResult.errorCode != ErrorCode::NoError) {
|
if (nativeResult.errorCode != ErrorCode::NoError) {
|
||||||
result.errorCode = nativeResult.errorCode;
|
result.errorCode = nativeResult.errorCode;
|
||||||
return result;
|
return result;
|
||||||
@@ -224,7 +206,7 @@ ExportController::ExportResult ExportController::generateWireGuardConfig(const Q
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExportController::ExportResult ExportController::generateAwgConfig(const QString &serverId, int containerIndex, const QString &clientName)
|
ExportController::ExportResult ExportController::generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName)
|
||||||
{
|
{
|
||||||
ExportResult result;
|
ExportResult result;
|
||||||
|
|
||||||
@@ -233,14 +215,9 @@ ExportController::ExportResult ExportController::generateAwgConfig(const QString
|
|||||||
result.errorCode = ErrorCode::InternalError;
|
result.errorCode = ErrorCode::InternalError;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container);
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
result.errorCode = ErrorCode::InternalError;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
ContainerConfig containerConfig = adminConfig->containerConfig(container);
|
|
||||||
|
|
||||||
auto nativeResult = generateNativeConfig(serverId, container, containerConfig, clientName);
|
auto nativeResult = generateNativeConfig(serverIndex, container, containerConfig, clientName);
|
||||||
if (nativeResult.errorCode != ErrorCode::NoError) {
|
if (nativeResult.errorCode != ErrorCode::NoError) {
|
||||||
result.errorCode = nativeResult.errorCode;
|
result.errorCode = nativeResult.errorCode;
|
||||||
return result;
|
return result;
|
||||||
@@ -256,18 +233,13 @@ ExportController::ExportResult ExportController::generateAwgConfig(const QString
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ExportController::ExportResult ExportController::generateXrayConfig(const QString &serverId, const QString &clientName)
|
ExportController::ExportResult ExportController::generateXrayConfig(int serverIndex, const QString &clientName)
|
||||||
{
|
{
|
||||||
ExportResult result;
|
ExportResult result;
|
||||||
|
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, DockerContainer::Xray);
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
result.errorCode = ErrorCode::InternalError;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
ContainerConfig containerConfig = adminConfig->containerConfig(DockerContainer::Xray);
|
|
||||||
|
|
||||||
auto nativeResult = generateNativeConfig(serverId, DockerContainer::Xray, containerConfig, clientName);
|
auto nativeResult = generateNativeConfig(serverIndex, DockerContainer::Xray, containerConfig, clientName);
|
||||||
if (nativeResult.errorCode != ErrorCode::NoError) {
|
if (nativeResult.errorCode != ErrorCode::NoError) {
|
||||||
result.errorCode = nativeResult.errorCode;
|
result.errorCode = nativeResult.errorCode;
|
||||||
return result;
|
return result;
|
||||||
@@ -323,18 +295,6 @@ ExportController::ExportResult ExportController::generateXrayConfig(const QStrin
|
|||||||
vlessServer.shortId = realitySettings.value(amnezia::protocols::xray::shortId).toString();
|
vlessServer.shortId = realitySettings.value(amnezia::protocols::xray::shortId).toString();
|
||||||
vlessServer.fingerprint = realitySettings.value(amnezia::protocols::xray::fingerprint).toString("chrome");
|
vlessServer.fingerprint = realitySettings.value(amnezia::protocols::xray::fingerprint).toString("chrome");
|
||||||
vlessServer.spiderX = realitySettings.value(amnezia::protocols::xray::spiderX).toString("");
|
vlessServer.spiderX = realitySettings.value(amnezia::protocols::xray::spiderX).toString("");
|
||||||
} else if (vlessServer.security == "tls") {
|
|
||||||
QJsonObject tlsSettings = streamSettings.value("tlsSettings").toObject();
|
|
||||||
vlessServer.serverName = tlsSettings.value(amnezia::protocols::xray::serverName).toString();
|
|
||||||
vlessServer.fingerprint = tlsSettings.value(amnezia::protocols::xray::fingerprint).toString();
|
|
||||||
// alpn: serialize array back to comma-separated for VLESS URI
|
|
||||||
QJsonArray alpnArr = tlsSettings.value("alpn").toArray();
|
|
||||||
QStringList alpnList;
|
|
||||||
for (const QJsonValue &v : alpnArr) {
|
|
||||||
alpnList << v.toString();
|
|
||||||
}
|
|
||||||
// alpn goes into vless URI query param — handled by Serialize via serverName/alpn fields
|
|
||||||
// VlessServerObject doesn't have alpn field, so we embed in serverName if needed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.nativeConfigString = amnezia::serialization::vless::Serialize(vlessServer, "AmneziaVPN");
|
result.nativeConfigString = amnezia::serialization::vless::Serialize(vlessServer, "AmneziaVPN");
|
||||||
@@ -342,22 +302,22 @@ ExportController::ExportResult ExportController::generateXrayConfig(const QStrin
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportController::updateClientManagementModel(const QString &serverId, int containerIndex)
|
void ExportController::updateClientManagementModel(int serverIndex, int containerIndex)
|
||||||
{
|
{
|
||||||
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
||||||
emit updateClientsRequested(serverId, container);
|
emit updateClientsRequested(serverIndex, container);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportController::revokeConfig(int row, const QString &serverId, int containerIndex)
|
void ExportController::revokeConfig(int row, int serverIndex, int containerIndex)
|
||||||
{
|
{
|
||||||
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
||||||
emit revokeClientRequested(serverId, row, container);
|
emit revokeClientRequested(serverIndex, row, container);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportController::renameClient(int row, const QString &clientName, const QString &serverId, int containerIndex)
|
void ExportController::renameClient(int row, const QString &clientName, int serverIndex, int containerIndex)
|
||||||
{
|
{
|
||||||
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
||||||
emit renameClientRequested(serverId, row, clientName, container);
|
emit renameClientRequested(serverIndex, row, clientName, container);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ExportController::generateVpnUrl(const QByteArray &compressedConfig)
|
QString ExportController::generateVpnUrl(const QByteArray &compressedConfig)
|
||||||
|
|||||||
@@ -37,23 +37,23 @@ public:
|
|||||||
SecureAppSettingsRepository* appSettingsRepository,
|
SecureAppSettingsRepository* appSettingsRepository,
|
||||||
QObject *parent = nullptr);
|
QObject *parent = nullptr);
|
||||||
|
|
||||||
ExportResult generateFullAccessConfig(const QString &serverId);
|
ExportResult generateFullAccessConfig(int serverIndex);
|
||||||
ExportResult generateConnectionConfig(const QString &serverId, int containerIndex, const QString &clientName);
|
ExportResult generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName);
|
||||||
ExportResult generateOpenVpnConfig(const QString &serverId, const QString &clientName);
|
ExportResult generateOpenVpnConfig(int serverIndex, const QString &clientName);
|
||||||
ExportResult generateWireGuardConfig(const QString &serverId, const QString &clientName);
|
ExportResult generateWireGuardConfig(int serverIndex, const QString &clientName);
|
||||||
ExportResult generateAwgConfig(const QString &serverId, int containerIndex, const QString &clientName);
|
ExportResult generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName);
|
||||||
ExportResult generateXrayConfig(const QString &serverId, const QString &clientName);
|
ExportResult generateXrayConfig(int serverIndex, const QString &clientName);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void appendClientRequested(const QString &serverId, const QString &clientId, const QString &clientName, DockerContainer container);
|
void appendClientRequested(int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container);
|
||||||
void updateClientsRequested(const QString &serverId, DockerContainer container);
|
void updateClientsRequested(int serverIndex, DockerContainer container);
|
||||||
void revokeClientRequested(const QString &serverId, int row, DockerContainer container);
|
void revokeClientRequested(int serverIndex, int row, DockerContainer container);
|
||||||
void renameClientRequested(const QString &serverId, int row, const QString &clientName, DockerContainer container);
|
void renameClientRequested(int serverIndex, int row, const QString &clientName, DockerContainer container);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void updateClientManagementModel(const QString &serverId, int containerIndex);
|
void updateClientManagementModel(int serverIndex, int containerIndex);
|
||||||
void revokeConfig(int row, const QString &serverId, int containerIndex);
|
void revokeConfig(int row, int serverIndex, int containerIndex);
|
||||||
void renameClient(int row, const QString &clientName, const QString &serverId, int containerIndex);
|
void renameClient(int row, const QString &clientName, int serverIndex, int containerIndex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct NativeConfigResult
|
struct NativeConfigResult
|
||||||
@@ -62,7 +62,7 @@ private:
|
|||||||
QJsonObject jsonNativeConfig;
|
QJsonObject jsonNativeConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
NativeConfigResult generateNativeConfig(const QString &serverId, DockerContainer container,
|
NativeConfigResult generateNativeConfig(int serverIndex, DockerContainer container,
|
||||||
const ContainerConfig &containerConfig,
|
const ContainerConfig &containerConfig,
|
||||||
const QString &clientName);
|
const QString &clientName);
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
#include "core/utils/containerEnum.h"
|
#include "core/utils/containerEnum.h"
|
||||||
#include "core/utils/containers/containerUtils.h"
|
#include "core/utils/containers/containerUtils.h"
|
||||||
#include "core/utils/protocolEnum.h"
|
#include "core/utils/protocolEnum.h"
|
||||||
#include "core/utils/serverConfigUtils.h"
|
#include "core/utils/api/apiEnums.h"
|
||||||
#include "core/utils/constants/apiKeys.h"
|
#include "core/utils/constants/apiKeys.h"
|
||||||
#include "core/utils/constants/apiConstants.h"
|
#include "core/utils/constants/apiConstants.h"
|
||||||
#include "core/utils/api/apiUtils.h"
|
#include "core/utils/api/apiUtils.h"
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "core/utils/constants/configKeys.h"
|
#include "core/utils/constants/configKeys.h"
|
||||||
#include "core/utils/constants/protocolConstants.h"
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
#include "core/utils/qrCodeUtils.h"
|
#include "core/utils/qrCodeUtils.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
using namespace ProtocolUtils;
|
using namespace ProtocolUtils;
|
||||||
@@ -207,18 +208,12 @@ ImportController::ImportResult ImportController::extractConfigFromData(const QSt
|
|||||||
case ConfigTypes::Amnezia: {
|
case ConfigTypes::Amnezia: {
|
||||||
result.config = QJsonDocument::fromJson(config.toUtf8()).object();
|
result.config = QJsonDocument::fromJson(config.toUtf8()).object();
|
||||||
|
|
||||||
if (serverConfigUtils::isServerFromApi(result.config)) {
|
if (apiUtils::isServerFromApi(result.config)) {
|
||||||
auto apiConfig = result.config.value(apiDefs::key::apiConfig).toObject();
|
auto apiConfig = result.config.value(apiDefs::key::apiConfig).toObject();
|
||||||
apiConfig[apiDefs::key::vpnKey] = data;
|
apiConfig[apiDefs::key::vpnKey] = data;
|
||||||
result.config[apiDefs::key::apiConfig] = apiConfig;
|
result.config[apiDefs::key::apiConfig] = apiConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serverConfigUtils::isLegacyApiSubscription(serverConfigUtils::configTypeFromJson(result.config))) {
|
|
||||||
result.errorCode = ErrorCode::LegacyApiV1NotSupportedError;
|
|
||||||
result.config = {};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
processAmneziaConfig(result.config);
|
processAmneziaConfig(result.config);
|
||||||
if (!result.config.empty()) {
|
if (!result.config.empty()) {
|
||||||
checkForMaliciousStrings(result.config, result.maliciousWarningText);
|
checkForMaliciousStrings(result.config, result.maliciousWarningText);
|
||||||
@@ -386,29 +381,18 @@ void ImportController::importConfig(const QJsonObject &config)
|
|||||||
credentials.secretData = config.value(configKey::password).toString();
|
credentials.secretData = config.value(configKey::password).toString();
|
||||||
|
|
||||||
if (credentials.isValid() || config.contains(configKey::containers)) {
|
if (credentials.isValid() || config.contains(configKey::containers)) {
|
||||||
m_serversRepository->addServer(QString(), config, serverConfigUtils::configTypeFromJson(config));
|
ServerConfig serverConfig = ServerConfig::fromJson(config);
|
||||||
|
m_serversRepository->addServer(serverConfig);
|
||||||
emit importFinished();
|
emit importFinished();
|
||||||
} else if (config.contains(configKey::configVersion)) {
|
} else if (config.contains(configKey::configVersion)) {
|
||||||
quint16 crc = qChecksum(QJsonDocument(config).toJson());
|
quint16 crc = qChecksum(QJsonDocument(config).toJson());
|
||||||
bool hasServerWithCrc = false;
|
if (m_serversRepository->hasServerWithCrc(crc)) {
|
||||||
const QVector<QString> ids = m_serversRepository->orderedServerIds();
|
|
||||||
for (const QString &id : ids) {
|
|
||||||
const auto apiV2 = m_serversRepository->apiV2Config(id);
|
|
||||||
if (!apiV2.has_value()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (static_cast<quint16>(apiV2->crc) == crc) {
|
|
||||||
hasServerWithCrc = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasServerWithCrc) {
|
|
||||||
emit importErrorOccurred(ErrorCode::ApiConfigAlreadyAdded, true);
|
emit importErrorOccurred(ErrorCode::ApiConfigAlreadyAdded, true);
|
||||||
} else {
|
} else {
|
||||||
QJsonObject configWithCrc = config;
|
QJsonObject configWithCrc = config;
|
||||||
configWithCrc.insert(configKey::crc, crc);
|
configWithCrc.insert(configKey::crc, crc);
|
||||||
m_serversRepository->addServer(QString(), configWithCrc, serverConfigUtils::configTypeFromJson(configWithCrc));
|
ServerConfig serverConfig = ServerConfig::fromJson(configWithCrc);
|
||||||
|
m_serversRepository->addServer(serverConfig);
|
||||||
emit importFinished();
|
emit importFinished();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -19,8 +19,6 @@
|
|||||||
#include "core/installers/openvpnInstaller.h"
|
#include "core/installers/openvpnInstaller.h"
|
||||||
#include "core/installers/sftpInstaller.h"
|
#include "core/installers/sftpInstaller.h"
|
||||||
#include "core/installers/socks5Installer.h"
|
#include "core/installers/socks5Installer.h"
|
||||||
#include "core/installers/mtProxyInstaller.h"
|
|
||||||
#include "core/installers/telemtInstaller.h"
|
|
||||||
#include "core/installers/torInstaller.h"
|
#include "core/installers/torInstaller.h"
|
||||||
#include "core/installers/wireguardInstaller.h"
|
#include "core/installers/wireguardInstaller.h"
|
||||||
#include "core/installers/xrayInstaller.h"
|
#include "core/installers/xrayInstaller.h"
|
||||||
@@ -35,8 +33,8 @@
|
|||||||
#include "core/protocols/protocolUtils.h"
|
#include "core/protocols/protocolUtils.h"
|
||||||
#include "core/utils/constants/configKeys.h"
|
#include "core/utils/constants/configKeys.h"
|
||||||
#include "core/utils/constants/protocolConstants.h"
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
#include "core/models/containerConfig.h"
|
#include "core/models/containerConfig.h"
|
||||||
#include "core/models/protocols/mtProxyProtocolConfig.h"
|
|
||||||
#include "core/models/protocols/awgProtocolConfig.h"
|
#include "core/models/protocols/awgProtocolConfig.h"
|
||||||
#include "ui/models/protocols/wireguardConfigModel.h"
|
#include "ui/models/protocols/wireguardConfigModel.h"
|
||||||
#include "core/utils/utilities.h"
|
#include "core/utils/utilities.h"
|
||||||
@@ -56,21 +54,6 @@ using namespace ProtocolUtils;
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
Logger logger("InstallController");
|
Logger logger("InstallController");
|
||||||
|
|
||||||
bool dockerDaemonContainerMissing(const QString &out, const QString &containerDockerName)
|
|
||||||
{
|
|
||||||
if (!out.contains(QLatin1String("Error response from daemon"), Qt::CaseInsensitive)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (out.contains(QLatin1String("No such container"), Qt::CaseInsensitive)
|
|
||||||
&& out.contains(containerDockerName, Qt::CaseInsensitive)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (out.size() < 700 && out.contains(QLatin1String("is not running"), Qt::CaseInsensitive)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InstallController::InstallController(SecureServersRepository *serversRepository,
|
InstallController::InstallController(SecureServersRepository *serversRepository,
|
||||||
@@ -146,36 +129,15 @@ ErrorCode InstallController::setupContainer(const ServerCredentials &credentials
|
|||||||
return startupContainerWorker(credentials, container, config, sshSession);
|
return startupContainerWorker(credentials, container, config, sshSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode InstallController::updateContainer(const QString &serverId, DockerContainer container, const ContainerConfig &oldConfig,
|
ErrorCode InstallController::updateContainer(int serverIndex, DockerContainer container, const ContainerConfig &oldConfig,
|
||||||
ContainerConfig &newConfig)
|
ContainerConfig &newConfig)
|
||||||
{
|
{
|
||||||
if (!isUpdateDockerContainerRequired(container, oldConfig, newConfig)) {
|
if (!isUpdateDockerContainerRequired(container, oldConfig, newConfig)) {
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
m_serversRepository->setContainerConfig(serverIndex, container, newConfig);
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
if (container == DockerContainer::MtProxy) {
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
SshSession sshSession(this);
|
|
||||||
MtProxyInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
|
|
||||||
} else if (container == DockerContainer::Telemt) {
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
SshSession sshSession(this);
|
|
||||||
TelemtInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
|
|
||||||
}
|
|
||||||
adminConfig->updateContainerConfig(container, newConfig);
|
|
||||||
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
|
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
SshSession sshSession(this);
|
SshSession sshSession(this);
|
||||||
|
|
||||||
bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig);
|
bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig);
|
||||||
@@ -192,116 +154,64 @@ ErrorCode InstallController::updateContainer(const QString &serverId, DockerCont
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (errorCode == ErrorCode::NoError) {
|
if (errorCode == ErrorCode::NoError) {
|
||||||
if (container == DockerContainer::MtProxy) {
|
clearCachedProfile(serverIndex, container);
|
||||||
MtProxyInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
|
m_serversRepository->setContainerConfig(serverIndex, container, newConfig);
|
||||||
} else if (container == DockerContainer::Telemt) {
|
|
||||||
TelemtInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
|
|
||||||
}
|
|
||||||
clearCachedProfile(serverId, container);
|
|
||||||
adminConfig->updateContainerConfig(container, newConfig);
|
|
||||||
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstallController::clearCachedProfile(const QString &serverId, DockerContainer container)
|
void InstallController::clearCachedProfile(int serverIndex, DockerContainer container)
|
||||||
{
|
{
|
||||||
if (ContainerUtils::containerService(container) == ServiceType::Other) {
|
if (ContainerUtils::containerService(container) == ServiceType::Other) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
ContainerConfig containerConfigModel = m_serversRepository->containerConfig(serverIndex, container);
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return;
|
m_serversRepository->clearLastConnectionConfig(serverIndex, container);
|
||||||
|
|
||||||
|
emit clientRevocationRequested(serverIndex, containerConfigModel, container);
|
||||||
}
|
}
|
||||||
|
|
||||||
adminConfig->clearCachedClientProfile(container);
|
ErrorCode InstallController::validateAndPrepareConfig(int serverIndex)
|
||||||
const ContainerConfig containerConfigModel = adminConfig->containerConfig(container);
|
|
||||||
|
|
||||||
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
|
|
||||||
|
|
||||||
emit clientRevocationRequested(serverId, containerConfigModel, container);
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode InstallController::validateAndPrepareConfig(const QString &serverId)
|
|
||||||
{
|
{
|
||||||
const auto kind = m_serversRepository->serverKind(serverId);
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
|
|
||||||
DockerContainer container = DockerContainer::None;
|
if (serverConfigModel.isApiConfig()) {
|
||||||
ContainerConfig containerConfig;
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
switch (kind) {
|
DockerContainer container = serverConfigModel.defaultContainer();
|
||||||
case serverConfigUtils::ConfigType::SelfHostedAdmin: {
|
|
||||||
const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId);
|
|
||||||
if (!cfg.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
container = cfg->defaultContainer;
|
|
||||||
containerConfig = cfg->containerConfig(container);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::SelfHostedUser: {
|
|
||||||
const auto cfg = m_serversRepository->selfHostedUserConfig(serverId);
|
|
||||||
if (!cfg.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
container = cfg->defaultContainer;
|
|
||||||
containerConfig = cfg->containerConfig(container);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::Native: {
|
|
||||||
const auto cfg = m_serversRepository->nativeConfig(serverId);
|
|
||||||
if (!cfg.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
container = cfg->defaultContainer;
|
|
||||||
containerConfig = cfg->containerConfig(container);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (container == DockerContainer::None) {
|
if (container == DockerContainer::None) {
|
||||||
return ErrorCode::NoInstalledContainersError;
|
return ErrorCode::NoInstalledContainersError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (containerConfig.protocolConfig.hasClientConfig()) {
|
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container);
|
||||||
return ErrorCode::NoError;
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
}
|
|
||||||
|
|
||||||
if (kind != serverConfigUtils::ConfigType::SelfHostedAdmin) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
SshSession sshSession;
|
SshSession sshSession;
|
||||||
const QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName());
|
|
||||||
const ErrorCode errorCode = processContainerForAdmin(container, containerConfig, credentials, sshSession, serverId, clientName);
|
auto isProtocolConfigExists = [](const ContainerConfig &cfg) {
|
||||||
|
return cfg.protocolConfig.hasClientConfig();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isProtocolConfigExists(containerConfig)) {
|
||||||
|
QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName());
|
||||||
|
ErrorCode errorCode = processContainerForAdmin(container, containerConfig, credentials, sshSession, serverIndex, clientName);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
|
m_serversRepository->setContainerConfig(serverIndex, container, containerConfig);
|
||||||
adminConfig->updateContainerConfig(container, containerConfig);
|
}
|
||||||
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
|
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstallController::validateConfig(const QString &serverId)
|
void InstallController::validateConfig(int serverIndex)
|
||||||
{
|
{
|
||||||
QFuture<ErrorCode> future = QtConcurrent::run([this, serverId]() {
|
QFuture<ErrorCode> future = QtConcurrent::run([this, serverIndex]() {
|
||||||
return validateAndPrepareConfig(serverId);
|
return validateAndPrepareConfig(serverIndex);
|
||||||
});
|
});
|
||||||
|
|
||||||
auto *watcher = new QFutureWatcher<ErrorCode>(this);
|
auto *watcher = new QFutureWatcher<ErrorCode>(this);
|
||||||
@@ -320,21 +230,6 @@ void InstallController::validateConfig(const QString &serverId)
|
|||||||
watcher->setFuture(future);
|
watcher->setFuture(future);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstallController::addEmptyServer(const ServerCredentials &credentials)
|
|
||||||
{
|
|
||||||
SelfHostedAdminServerConfig serverConfig;
|
|
||||||
serverConfig.hostName = credentials.hostName;
|
|
||||||
serverConfig.userName = credentials.userName;
|
|
||||||
serverConfig.password = credentials.secretData;
|
|
||||||
serverConfig.port = credentials.port;
|
|
||||||
serverConfig.description = m_appSettingsRepository->nextAvailableServerName();
|
|
||||||
serverConfig.displayName = serverConfig.description.isEmpty() ? serverConfig.hostName : serverConfig.description;
|
|
||||||
serverConfig.defaultContainer = DockerContainer::None;
|
|
||||||
|
|
||||||
m_serversRepository->addServer(QString(), serverConfig.toJson(),
|
|
||||||
serverConfigUtils::ConfigType::SelfHostedAdmin);
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode InstallController::prepareContainerConfig(DockerContainer container, const ServerCredentials &credentials, ContainerConfig &containerConfig, SshSession &sshSession)
|
ErrorCode InstallController::prepareContainerConfig(DockerContainer container, const ServerCredentials &credentials, ContainerConfig &containerConfig, SshSession &sshSession)
|
||||||
{
|
{
|
||||||
if (!ContainerUtils::isSupportedByCurrentPlatform(container)) {
|
if (!ContainerUtils::isSupportedByCurrentPlatform(container)) {
|
||||||
@@ -362,7 +257,7 @@ ErrorCode InstallController::prepareContainerConfig(DockerContainer container, c
|
|||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstallController::adminAppendRequested(const QString &serverId, DockerContainer container,
|
void InstallController::adminAppendRequested(int serverIndex, DockerContainer container,
|
||||||
const ContainerConfig &containerConfig, const QString &clientName)
|
const ContainerConfig &containerConfig, const QString &clientName)
|
||||||
{
|
{
|
||||||
if (ContainerUtils::containerService(container) == ServiceType::Other
|
if (ContainerUtils::containerService(container) == ServiceType::Other
|
||||||
@@ -371,13 +266,13 @@ void InstallController::adminAppendRequested(const QString &serverId, DockerCont
|
|||||||
}
|
}
|
||||||
QString clientId = containerConfig.protocolConfig.clientId();
|
QString clientId = containerConfig.protocolConfig.clientId();
|
||||||
if (!clientId.isEmpty()) {
|
if (!clientId.isEmpty()) {
|
||||||
emit clientAppendRequested(serverId, clientId, clientName, container);
|
emit clientAppendRequested(serverIndex, clientId, clientName, container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode InstallController::processContainerForAdmin(DockerContainer container, ContainerConfig &containerConfig,
|
ErrorCode InstallController::processContainerForAdmin(DockerContainer container, ContainerConfig &containerConfig,
|
||||||
const ServerCredentials &credentials, SshSession &sshSession,
|
const ServerCredentials &credentials, SshSession &sshSession,
|
||||||
const QString &serverId, const QString &clientName)
|
int serverIndex, const QString &clientName)
|
||||||
{
|
{
|
||||||
if (ContainerUtils::isSupportedByCurrentPlatform(container)) {
|
if (ContainerUtils::isSupportedByCurrentPlatform(container)) {
|
||||||
ErrorCode errorCode = prepareContainerConfig(container, credentials, containerConfig, sshSession);
|
ErrorCode errorCode = prepareContainerConfig(container, credentials, containerConfig, sshSession);
|
||||||
@@ -385,7 +280,7 @@ ErrorCode InstallController::processContainerForAdmin(DockerContainer container,
|
|||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
adminAppendRequested(serverId, container, containerConfig, clientName);
|
adminAppendRequested(serverIndex, container, containerConfig, clientName);
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,24 +372,9 @@ ErrorCode InstallController::configureContainerWorker(const ServerCredentials &c
|
|||||||
sshSession.replaceVars(amnezia::scriptData(ProtocolScriptType::configure_container, container), baseVars),
|
sshSession.replaceVars(amnezia::scriptData(ProtocolScriptType::configure_container, container), baseVars),
|
||||||
cbReadStdOut, cbReadStdErr);
|
cbReadStdOut, cbReadStdErr);
|
||||||
|
|
||||||
if (e != ErrorCode::NoError) {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dockerDaemonContainerMissing(stdOut, ContainerUtils::containerToString(container))) {
|
|
||||||
qDebug() << "configureContainerWorker: Docker daemon reports container missing/stopped, output:" << stdOut;
|
|
||||||
return ErrorCode::ServerContainerMissingError;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateContainerConfigAfterInstallation(container, config, stdOut);
|
updateContainerConfigAfterInstallation(container, config, stdOut);
|
||||||
|
|
||||||
if (container == DockerContainer::MtProxy) {
|
return e;
|
||||||
MtProxyInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, config);
|
|
||||||
} else if (container == DockerContainer::Telemt) {
|
|
||||||
TelemtInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode InstallController::startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config, SshSession &sshSession)
|
ErrorCode InstallController::startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config, SshSession &sshSession)
|
||||||
@@ -647,79 +527,6 @@ bool InstallController::isReinstallContainerRequired(DockerContainer container,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container == DockerContainer::MtProxy) {
|
|
||||||
const auto *oldMt = oldConfig.getMtProxyProtocolConfig();
|
|
||||||
const auto *newMt = newConfig.getMtProxyProtocolConfig();
|
|
||||||
if (oldMt && newMt) {
|
|
||||||
const QString oldPort =
|
|
||||||
oldMt->port.isEmpty() ? QString(protocols::mtProxy::defaultPort) : oldMt->port;
|
|
||||||
const QString newPort =
|
|
||||||
newMt->port.isEmpty() ? QString(protocols::mtProxy::defaultPort) : newMt->port;
|
|
||||||
if (oldPort != newPort) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const QString oldTransport = oldMt->transportMode.isEmpty() ? QString(
|
|
||||||
protocols::mtProxy::transportModeStandard)
|
|
||||||
: oldMt->transportMode;
|
|
||||||
const QString newTransport = newMt->transportMode.isEmpty() ? QString(
|
|
||||||
protocols::mtProxy::transportModeStandard)
|
|
||||||
: newMt->transportMode;
|
|
||||||
if (oldTransport != newTransport) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (oldMt->tlsDomain != newMt->tlsDomain) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (container == DockerContainer::Telemt) {
|
|
||||||
const auto *oldT = oldConfig.getTelemtProtocolConfig();
|
|
||||||
const auto *newT = newConfig.getTelemtProtocolConfig();
|
|
||||||
if (oldT && newT) {
|
|
||||||
const QString oldPort =
|
|
||||||
oldT->port.isEmpty() ? QString(protocols::telemt::defaultPort) : oldT->port;
|
|
||||||
const QString newPort =
|
|
||||||
newT->port.isEmpty() ? QString(protocols::telemt::defaultPort) : newT->port;
|
|
||||||
if (oldPort != newPort) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const QString oldTransport = oldT->transportMode.isEmpty()
|
|
||||||
? QString(protocols::telemt::transportModeStandard)
|
|
||||||
: oldT->transportMode;
|
|
||||||
const QString newTransport = newT->transportMode.isEmpty()
|
|
||||||
? QString(protocols::telemt::transportModeStandard)
|
|
||||||
: newT->transportMode;
|
|
||||||
if (oldTransport != newTransport) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (oldT->tlsDomain != newT->tlsDomain) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (oldT->maskEnabled != newT->maskEnabled) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (oldT->tlsEmulation != newT->tlsEmulation) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (oldT->useMiddleProxy != newT->useMiddleProxy) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (oldT->tag != newT->tag) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const QString oldUser = oldT->userName.isEmpty()
|
|
||||||
? QString::fromUtf8(protocols::telemt::defaultUserName)
|
|
||||||
: oldT->userName;
|
|
||||||
const QString newUser = newT->userName.isEmpty()
|
|
||||||
? QString::fromUtf8(protocols::telemt::defaultUserName)
|
|
||||||
: newT->userName;
|
|
||||||
if (oldUser != newUser) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (container == DockerContainer::Socks5Proxy) {
|
if (container == DockerContainer::Socks5Proxy) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -811,7 +618,7 @@ ErrorCode InstallController::isUserInSudo(const ServerCredentials &credentials,
|
|||||||
return ErrorCode::ServerUserDirectoryNotAccessible;
|
return ErrorCode::ServerUserDirectoryNotAccessible;
|
||||||
if (stdOut.contains("sudoers") || stdOut.contains("is not allowed to run sudo on"))
|
if (stdOut.contains("sudoers") || stdOut.contains("is not allowed to run sudo on"))
|
||||||
return ErrorCode::ServerUserNotAllowedInSudoers;
|
return ErrorCode::ServerUserNotAllowedInSudoers;
|
||||||
if (stdOut.contains("password is required") || stdOut.contains("authentication is required"))
|
if (stdOut.contains("password is required"))
|
||||||
return ErrorCode::ServerUserPasswordRequired;
|
return ErrorCode::ServerUserPasswordRequired;
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
@@ -881,16 +688,9 @@ ErrorCode InstallController::setupServerFirewall(const ServerCredentials &creden
|
|||||||
amnezia::genBaseVars(credentials, DockerContainer::None, QString(), QString())));
|
amnezia::genBaseVars(credentials, DockerContainer::None, QString(), QString())));
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode InstallController::rebootServer(const QString &serverId)
|
ErrorCode InstallController::rebootServer(int serverIndex)
|
||||||
{
|
{
|
||||||
const auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
auto credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
SshSession sshSession(this);
|
SshSession sshSession(this);
|
||||||
|
|
||||||
QString script = QString("sudo reboot");
|
QString script = QString("sudo reboot");
|
||||||
@@ -909,38 +709,27 @@ ErrorCode InstallController::rebootServer(const QString &serverId)
|
|||||||
return sshSession.runScript(credentials, script, cbReadStdOut, cbReadStdErr);
|
return sshSession.runScript(credentials, script, cbReadStdOut, cbReadStdErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode InstallController::removeAllContainers(const QString &serverId)
|
ErrorCode InstallController::removeAllContainers(int serverIndex)
|
||||||
{
|
{
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
auto credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
SshSession sshSession(this);
|
SshSession sshSession(this);
|
||||||
ErrorCode errorCode = sshSession.runScript(credentials, amnezia::scriptData(SharedScriptType::remove_all_containers));
|
ErrorCode errorCode = sshSession.runScript(credentials, amnezia::scriptData(SharedScriptType::remove_all_containers));
|
||||||
|
|
||||||
if (errorCode == ErrorCode::NoError) {
|
if (errorCode == ErrorCode::NoError) {
|
||||||
adminConfig->containers.clear();
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
adminConfig->defaultContainer = DockerContainer::None;
|
serverConfigModel.visit([](auto& arg) {
|
||||||
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
|
arg.containers.clear();
|
||||||
|
arg.defaultContainer = DockerContainer::None;
|
||||||
|
});
|
||||||
|
m_serversRepository->editServer(serverIndex, serverConfigModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode InstallController::removeContainer(const QString &serverId, DockerContainer container)
|
ErrorCode InstallController::removeContainer(int serverIndex, DockerContainer container)
|
||||||
{
|
{
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
auto credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
SshSession sshSession(this);
|
SshSession sshSession(this);
|
||||||
ErrorCode errorCode = sshSession.runScript(
|
ErrorCode errorCode = sshSession.runScript(
|
||||||
credentials,
|
credentials,
|
||||||
@@ -948,10 +737,11 @@ ErrorCode InstallController::removeContainer(const QString &serverId, DockerCont
|
|||||||
amnezia::genBaseVars(credentials, container, QString(), QString())));
|
amnezia::genBaseVars(credentials, container, QString(), QString())));
|
||||||
|
|
||||||
if (errorCode == ErrorCode::NoError) {
|
if (errorCode == ErrorCode::NoError) {
|
||||||
QMap<DockerContainer, ContainerConfig> containers = adminConfig->containers;
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
|
QMap<DockerContainer, ContainerConfig> containers = serverConfigModel.containers();
|
||||||
containers.remove(container);
|
containers.remove(container);
|
||||||
|
|
||||||
DockerContainer defaultContainer = adminConfig->defaultContainer;
|
DockerContainer defaultContainer = serverConfigModel.defaultContainer();
|
||||||
if (defaultContainer == container) {
|
if (defaultContainer == container) {
|
||||||
if (containers.isEmpty()) {
|
if (containers.isEmpty()) {
|
||||||
defaultContainer = DockerContainer::None;
|
defaultContainer = DockerContainer::None;
|
||||||
@@ -960,9 +750,11 @@ ErrorCode InstallController::removeContainer(const QString &serverId, DockerCont
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
adminConfig->containers = containers;
|
serverConfigModel.visit([&containers, defaultContainer](auto& arg) {
|
||||||
adminConfig->defaultContainer = defaultContainer;
|
arg.containers = containers;
|
||||||
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
|
arg.defaultContainer = defaultContainer;
|
||||||
|
});
|
||||||
|
m_serversRepository->editServer(serverIndex, serverConfigModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
return errorCode;
|
return errorCode;
|
||||||
@@ -980,8 +772,6 @@ QScopedPointer<InstallerBase> InstallController::createInstaller(DockerContainer
|
|||||||
case DockerContainer::TorWebSite: return QScopedPointer<InstallerBase>(new TorInstaller(this));
|
case DockerContainer::TorWebSite: return QScopedPointer<InstallerBase>(new TorInstaller(this));
|
||||||
case DockerContainer::Sftp: return QScopedPointer<InstallerBase>(new SftpInstaller(this));
|
case DockerContainer::Sftp: return QScopedPointer<InstallerBase>(new SftpInstaller(this));
|
||||||
case DockerContainer::Socks5Proxy: return QScopedPointer<InstallerBase>(new Socks5Installer(this));
|
case DockerContainer::Socks5Proxy: return QScopedPointer<InstallerBase>(new Socks5Installer(this));
|
||||||
case DockerContainer::MtProxy: return QScopedPointer<InstallerBase>(new MtProxyInstaller(this));
|
|
||||||
case DockerContainer::Telemt: return QScopedPointer<InstallerBase>(new TelemtInstaller(this));
|
|
||||||
default: return QScopedPointer<InstallerBase>(new InstallerBase(this));
|
default: return QScopedPointer<InstallerBase>(new InstallerBase(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1020,35 +810,14 @@ bool InstallController::isUpdateDockerContainerRequired(DockerContainer containe
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (container == DockerContainer::MtProxy) {
|
|
||||||
const auto *oldMt = oldConfig.getMtProxyProtocolConfig();
|
|
||||||
const auto *newMt = newConfig.getMtProxyProtocolConfig();
|
|
||||||
if (!oldMt || !newMt) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return !oldMt->equalsDockerDeploymentSettings(*newMt);
|
|
||||||
} else if (container == DockerContainer::Telemt) {
|
|
||||||
const auto *oldT = oldConfig.getTelemtProtocolConfig();
|
|
||||||
const auto *newT = newConfig.getTelemtProtocolConfig();
|
|
||||||
if (!oldT || !newT) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return !oldT->equalsDockerDeploymentSettings(*newT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode InstallController::scanServerForInstalledContainers(const QString &serverId)
|
ErrorCode InstallController::scanServerForInstalledContainers(int serverIndex)
|
||||||
{
|
{
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
SshSession sshSession(this);
|
SshSession sshSession(this);
|
||||||
|
|
||||||
QMap<DockerContainer, ContainerConfig> installedContainers;
|
QMap<DockerContainer, ContainerConfig> installedContainers;
|
||||||
@@ -1057,7 +826,8 @@ ErrorCode InstallController::scanServerForInstalledContainers(const QString &ser
|
|||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<DockerContainer, ContainerConfig> containers = adminConfig->containers;
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
|
QMap<DockerContainer, ContainerConfig> containers = serverConfigModel.containers();
|
||||||
bool hasNewContainers = false;
|
bool hasNewContainers = false;
|
||||||
|
|
||||||
QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName());
|
QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName());
|
||||||
@@ -1065,25 +835,29 @@ ErrorCode InstallController::scanServerForInstalledContainers(const QString &ser
|
|||||||
if (!containers.contains(iterator.key())) {
|
if (!containers.contains(iterator.key())) {
|
||||||
ContainerConfig containerConfig = iterator.value();
|
ContainerConfig containerConfig = iterator.value();
|
||||||
errorCode = processContainerForAdmin(iterator.key(), containerConfig, credentials, sshSession,
|
errorCode = processContainerForAdmin(iterator.key(), containerConfig, credentials, sshSession,
|
||||||
serverId, clientName);
|
serverIndex, clientName);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
containers.insert(iterator.key(), containerConfig);
|
containers.insert(iterator.key(), containerConfig);
|
||||||
hasNewContainers = true;
|
hasNewContainers = true;
|
||||||
|
|
||||||
DockerContainer defaultContainer = adminConfig->defaultContainer;
|
DockerContainer defaultContainer = serverConfigModel.defaultContainer();
|
||||||
if (defaultContainer == DockerContainer::None
|
if (defaultContainer == DockerContainer::None
|
||||||
&& ContainerUtils::containerService(iterator.key()) != ServiceType::Other
|
&& ContainerUtils::containerService(iterator.key()) != ServiceType::Other
|
||||||
&& ContainerUtils::isSupportedByCurrentPlatform(iterator.key())) {
|
&& ContainerUtils::isSupportedByCurrentPlatform(iterator.key())) {
|
||||||
adminConfig->defaultContainer = iterator.key();
|
serverConfigModel.visit([iterator](auto& arg) {
|
||||||
|
arg.defaultContainer = iterator.key();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasNewContainers) {
|
if (hasNewContainers) {
|
||||||
adminConfig->containers = containers;
|
serverConfigModel.visit([&containers](auto& arg) {
|
||||||
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
|
arg.containers = containers;
|
||||||
|
});
|
||||||
|
m_serversRepository->editServer(serverIndex, serverConfigModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
@@ -1125,7 +899,7 @@ ErrorCode InstallController::installServer(const ServerCredentials &credentials,
|
|||||||
preparedContainers.insert(container, containerConfig);
|
preparedContainers.insert(container, containerConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
SelfHostedAdminServerConfig serverConfig;
|
SelfHostedServerConfig serverConfig;
|
||||||
serverConfig.hostName = credentials.hostName;
|
serverConfig.hostName = credentials.hostName;
|
||||||
serverConfig.userName = credentials.userName;
|
serverConfig.userName = credentials.userName;
|
||||||
serverConfig.password = credentials.secretData;
|
serverConfig.password = credentials.secretData;
|
||||||
@@ -1138,29 +912,21 @@ ErrorCode InstallController::installServer(const ServerCredentials &credentials,
|
|||||||
|
|
||||||
serverConfig.defaultContainer = container;
|
serverConfig.defaultContainer = container;
|
||||||
|
|
||||||
serverConfig.displayName = serverConfig.description.isEmpty() ? serverConfig.hostName : serverConfig.description;
|
m_serversRepository->addServer(ServerConfig(serverConfig));
|
||||||
|
|
||||||
const QString newServerId = m_serversRepository->addServer(QString(), serverConfig.toJson(),
|
int serverIndex = m_serversRepository->serversCount() - 1;
|
||||||
serverConfigUtils::ConfigType::SelfHostedAdmin);
|
|
||||||
QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName());
|
QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName());
|
||||||
for (auto iterator = preparedContainers.begin(); iterator != preparedContainers.end(); iterator++) {
|
for (auto iterator = preparedContainers.begin(); iterator != preparedContainers.end(); iterator++) {
|
||||||
adminAppendRequested(newServerId, iterator.key(), iterator.value(), clientName);
|
adminAppendRequested(serverIndex, iterator.key(), iterator.value(), clientName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode InstallController::installContainer(const QString &serverId, DockerContainer container, int port,
|
ErrorCode InstallController::installContainer(int serverIndex, DockerContainer container, int port,
|
||||||
TransportProto transportProto, bool &wasContainerInstalled)
|
TransportProto transportProto, bool &wasContainerInstalled)
|
||||||
{
|
{
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
SshSession sshSession(this);
|
SshSession sshSession(this);
|
||||||
|
|
||||||
QMap<DockerContainer, ContainerConfig> installedContainers;
|
QMap<DockerContainer, ContainerConfig> installedContainers;
|
||||||
@@ -1183,17 +949,15 @@ ErrorCode InstallController::installContainer(const QString &serverId, DockerCon
|
|||||||
|
|
||||||
QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName());
|
QString clientName = QString("Admin [%1]").arg(QSysInfo::prettyProductName());
|
||||||
for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) {
|
for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) {
|
||||||
ContainerConfig existingConfigModel = adminConfig->containerConfig(iterator.key());
|
ContainerConfig existingConfigModel = m_serversRepository->containerConfig(serverIndex, iterator.key());
|
||||||
if (existingConfigModel.container == DockerContainer::None) {
|
if (existingConfigModel.container == DockerContainer::None) {
|
||||||
ContainerConfig containerConfig = iterator.value();
|
ContainerConfig containerConfig = iterator.value();
|
||||||
errorCode = processContainerForAdmin(iterator.key(), containerConfig, credentials, sshSession,
|
errorCode = processContainerForAdmin(iterator.key(), containerConfig, credentials, sshSession,
|
||||||
serverId, clientName);
|
serverIndex, clientName);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
adminConfig->updateContainerConfig(iterator.key(), containerConfig);
|
m_serversRepository->setContainerConfig(serverIndex, iterator.key(), containerConfig);
|
||||||
m_serversRepository->editServer(serverId, adminConfig->toJson(),
|
|
||||||
serverConfigUtils::ConfigType::SelfHostedAdmin);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1229,15 +993,7 @@ bool InstallController::isServerAlreadyExists(const ServerCredentials &credentia
|
|||||||
{
|
{
|
||||||
int serversCount = m_serversRepository->serversCount();
|
int serversCount = m_serversRepository->serversCount();
|
||||||
for (int i = 0; i < serversCount; i++) {
|
for (int i = 0; i < serversCount; i++) {
|
||||||
const QString existingServerId = m_serversRepository->serverIdAt(i);
|
const ServerCredentials existingCredentials = m_serversRepository->serverCredentials(i);
|
||||||
const auto adminConfig = m_serversRepository->selfHostedAdminConfig(existingServerId);
|
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const ServerCredentials existingCredentials = adminConfig->credentials();
|
|
||||||
if (!existingCredentials.isValid()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (credentials.hostName == existingCredentials.hostName && credentials.port == existingCredentials.port) {
|
if (credentials.hostName == existingCredentials.hostName && credentials.port == existingCredentials.port) {
|
||||||
existingServerIndex = i;
|
existingServerIndex = i;
|
||||||
return true;
|
return true;
|
||||||
@@ -1337,56 +1093,6 @@ void InstallController::updateContainerConfigAfterInstallation(DockerContainer c
|
|||||||
onion.replace("\n", "");
|
onion.replace("\n", "");
|
||||||
torProtocolConfig->serverConfig.site = onion;
|
torProtocolConfig->serverConfig.site = onion;
|
||||||
}
|
}
|
||||||
} else if (container == DockerContainer::MtProxy) {
|
|
||||||
if (auto* mtProxyConfig = containerConfig.getMtProxyProtocolConfig()) {
|
|
||||||
qDebug() << "amnezia mtproxy" << stdOut;
|
|
||||||
|
|
||||||
static const QRegularExpression reSecret(
|
|
||||||
QStringLiteral(R"(\[\*\]\s+Secret:\s+([0-9a-fA-F]{32}))"),
|
|
||||||
QRegularExpression::CaseInsensitiveOption);
|
|
||||||
static const QRegularExpression reTgLink(QStringLiteral(R"(\[\*\]\s+tg://\s+link:\s+(tg://proxy\?[^\s]+))"));
|
|
||||||
static const QRegularExpression reTmeLink(
|
|
||||||
QStringLiteral(R"(\[\*\]\s+t\.me\s+link:\s+(https://t\.me/proxy\?[^\s]+))"));
|
|
||||||
|
|
||||||
const QRegularExpressionMatch mSecret = reSecret.match(stdOut);
|
|
||||||
const QRegularExpressionMatch mTgLink = reTgLink.match(stdOut);
|
|
||||||
const QRegularExpressionMatch mTmeLink = reTmeLink.match(stdOut);
|
|
||||||
|
|
||||||
if (mSecret.hasMatch()) {
|
|
||||||
mtProxyConfig->secret = mSecret.captured(1);
|
|
||||||
}
|
|
||||||
if (mTgLink.hasMatch()) {
|
|
||||||
mtProxyConfig->tgLink = mTgLink.captured(1);
|
|
||||||
}
|
|
||||||
if (mTmeLink.hasMatch()) {
|
|
||||||
mtProxyConfig->tmeLink = mTmeLink.captured(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (container == DockerContainer::Telemt) {
|
|
||||||
if (auto *telemtConfig = containerConfig.getTelemtProtocolConfig()) {
|
|
||||||
qDebug() << "amnezia-telemt configure stdout" << stdOut;
|
|
||||||
|
|
||||||
static const QRegularExpression reSecret(
|
|
||||||
QStringLiteral(R"(\[\*\]\s+Secret:\s+([0-9a-fA-F]{32}))"),
|
|
||||||
QRegularExpression::CaseInsensitiveOption);
|
|
||||||
static const QRegularExpression reTgLink(QStringLiteral(R"(\[\*\]\s+tg://\s+link:\s+(tg://proxy\?[^\s]+))"));
|
|
||||||
static const QRegularExpression reTmeLink(
|
|
||||||
QStringLiteral(R"(\[\*\]\s+t\.me\s+link:\s+(https://t\.me/proxy\?[^\s]+))"));
|
|
||||||
|
|
||||||
const QRegularExpressionMatch mSecret = reSecret.match(stdOut);
|
|
||||||
const QRegularExpressionMatch mTgLink = reTgLink.match(stdOut);
|
|
||||||
const QRegularExpressionMatch mTmeLink = reTmeLink.match(stdOut);
|
|
||||||
|
|
||||||
if (mSecret.hasMatch()) {
|
|
||||||
telemtConfig->secret = mSecret.captured(1);
|
|
||||||
}
|
|
||||||
if (mTgLink.hasMatch()) {
|
|
||||||
telemtConfig->tgLink = mTgLink.captured(1);
|
|
||||||
}
|
|
||||||
if (mTmeLink.hasMatch()) {
|
|
||||||
telemtConfig->tmeLink = mTmeLink.captured(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1471,126 +1177,3 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
|
|||||||
|
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode InstallController::setDockerContainerEnabledState(const QString &serverId, DockerContainer container, bool enabled)
|
|
||||||
{
|
|
||||||
if (container != DockerContainer::MtProxy && container != DockerContainer::Telemt) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
const QString containerName = ContainerUtils::containerToString(container);
|
|
||||||
SshSession sshSession(this);
|
|
||||||
const QString script = enabled ? QStringLiteral("sudo docker start %1").arg(containerName)
|
|
||||||
: QStringLiteral("sudo docker stop %1").arg(containerName);
|
|
||||||
const ErrorCode runError = sshSession.runScript(credentials, script);
|
|
||||||
if (runError != ErrorCode::NoError) {
|
|
||||||
return runError;
|
|
||||||
}
|
|
||||||
ContainerConfig currentConfig = adminConfig->containerConfig(container);
|
|
||||||
bool persist = false;
|
|
||||||
if (auto *mtConfig = currentConfig.getMtProxyProtocolConfig()) {
|
|
||||||
mtConfig->isEnabled = enabled;
|
|
||||||
persist = true;
|
|
||||||
} else if (auto *telemtConfig = currentConfig.getTelemtProtocolConfig()) {
|
|
||||||
telemtConfig->isEnabled = enabled;
|
|
||||||
persist = true;
|
|
||||||
}
|
|
||||||
if (persist) {
|
|
||||||
adminConfig->updateContainerConfig(container, currentConfig);
|
|
||||||
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
|
|
||||||
}
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode InstallController::queryDockerContainerStatus(const QString &serverId, DockerContainer container, int &statusOut)
|
|
||||||
{
|
|
||||||
statusOut = 3;
|
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
const QString containerName = ContainerUtils::containerToString(container);
|
|
||||||
QString stdOut;
|
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data;
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
SshSession sshSession(this);
|
|
||||||
const QString script = QStringLiteral(
|
|
||||||
"sudo docker inspect --format '{{.State.Status}}' %1 2>/dev/null || echo 'not_found'")
|
|
||||||
.arg(containerName);
|
|
||||||
const ErrorCode errorCode = sshSession.runScript(credentials, script, cbReadStdOut);
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
const QString status = stdOut.trimmed();
|
|
||||||
if (status == QLatin1String("running")) {
|
|
||||||
statusOut = 1;
|
|
||||||
} else if (status == QLatin1String("not_found") || status.isEmpty()) {
|
|
||||||
statusOut = 0;
|
|
||||||
} else if (status == QLatin1String("exited") || status == QLatin1String("created")
|
|
||||||
|| status == QLatin1String("paused")) {
|
|
||||||
statusOut = 2;
|
|
||||||
} else {
|
|
||||||
statusOut = 3;
|
|
||||||
}
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode InstallController::queryMtProxyDiagnostics(const QString &serverId, DockerContainer container, int listenPort,
|
|
||||||
MtProxyContainerDiagnostics &out)
|
|
||||||
{
|
|
||||||
out = {};
|
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
SshSession sshSession(this);
|
|
||||||
return MtProxyInstaller::queryDiagnostics(sshSession, credentials, container, listenPort, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString InstallController::fetchDockerContainerSecret(const QString &serverId, DockerContainer container)
|
|
||||||
{
|
|
||||||
if (container != DockerContainer::MtProxy && container != DockerContainer::Telemt) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
const QString containerName = ContainerUtils::containerToString(container);
|
|
||||||
QString stdOut;
|
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data;
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
SshSession sshSession(this);
|
|
||||||
const QString path = QStringLiteral("/data/secret");
|
|
||||||
const QString cmd = QStringLiteral("sudo docker exec %1 cat %2").arg(containerName, path);
|
|
||||||
const ErrorCode errorCode = sshSession.runScript(credentials, cmd, cbReadStdOut);
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
const QString secret = stdOut.trimmed();
|
|
||||||
static const QRegularExpression hex32(QStringLiteral("^[0-9a-fA-F]{32}$"));
|
|
||||||
return hex32.match(secret).hasMatch() ? secret : QString();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
#include "core/models/containerConfig.h"
|
#include "core/models/containerConfig.h"
|
||||||
#include "core/repositories/secureServersRepository.h"
|
#include "core/repositories/secureServersRepository.h"
|
||||||
#include "core/repositories/secureAppSettingsRepository.h"
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
#include "core/installers/mtProxyInstaller.h"
|
|
||||||
|
|
||||||
class SshSession;
|
class SshSession;
|
||||||
class InstallerBase;
|
class InstallerBase;
|
||||||
@@ -34,32 +33,22 @@ public:
|
|||||||
~InstallController();
|
~InstallController();
|
||||||
|
|
||||||
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, bool isUpdate = false);
|
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, bool isUpdate = false);
|
||||||
ErrorCode updateContainer(const QString &serverId, DockerContainer container, const ContainerConfig &oldConfig, ContainerConfig &newConfig);
|
ErrorCode updateContainer(int serverIndex, DockerContainer container, const ContainerConfig &oldConfig, ContainerConfig &newConfig);
|
||||||
|
|
||||||
ErrorCode rebootServer(const QString &serverId);
|
ErrorCode rebootServer(int serverIndex);
|
||||||
ErrorCode removeAllContainers(const QString &serverId);
|
ErrorCode removeAllContainers(int serverIndex);
|
||||||
ErrorCode removeContainer(const QString &serverId, DockerContainer container);
|
ErrorCode removeContainer(int serverIndex, DockerContainer container);
|
||||||
|
|
||||||
ErrorCode setDockerContainerEnabledState(const QString &serverId, DockerContainer container, bool enabled);
|
|
||||||
|
|
||||||
/// statusOut: 0 = not deployed, 1 = running, 2 = stopped, 3 = error
|
|
||||||
ErrorCode queryDockerContainerStatus(const QString &serverId, DockerContainer container, int &statusOut);
|
|
||||||
|
|
||||||
ErrorCode queryMtProxyDiagnostics(const QString &serverId, DockerContainer container, int listenPort,
|
|
||||||
MtProxyContainerDiagnostics &out);
|
|
||||||
|
|
||||||
QString fetchDockerContainerSecret(const QString &serverId, DockerContainer container);
|
|
||||||
|
|
||||||
ContainerConfig generateConfig(DockerContainer container, int port, TransportProto transportProto);
|
ContainerConfig generateConfig(DockerContainer container, int port, TransportProto transportProto);
|
||||||
ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials, QMap<DockerContainer, ContainerConfig> &installedContainers, SshSession &sshSession);
|
ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials, QMap<DockerContainer, ContainerConfig> &installedContainers, SshSession &sshSession);
|
||||||
|
|
||||||
ErrorCode scanServerForInstalledContainers(const QString &serverId);
|
ErrorCode scanServerForInstalledContainers(int serverIndex);
|
||||||
|
|
||||||
ErrorCode installContainer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto, ContainerConfig &config);
|
ErrorCode installContainer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto, ContainerConfig &config);
|
||||||
|
|
||||||
ErrorCode installServer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto,
|
ErrorCode installServer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto,
|
||||||
bool &wasContainerInstalled);
|
bool &wasContainerInstalled);
|
||||||
ErrorCode installContainer(const QString &serverId, DockerContainer container, int port, TransportProto transportProto,
|
ErrorCode installContainer(int serverIndex, DockerContainer container, int port, TransportProto transportProto,
|
||||||
bool &wasContainerInstalled);
|
bool &wasContainerInstalled);
|
||||||
|
|
||||||
bool isUpdateDockerContainerRequired(DockerContainer container, const ContainerConfig &oldConfig, const ContainerConfig &newConfig);
|
bool isUpdateDockerContainerRequired(DockerContainer container, const ContainerConfig &oldConfig, const ContainerConfig &newConfig);
|
||||||
@@ -73,13 +62,11 @@ public:
|
|||||||
|
|
||||||
void cancelInstallation();
|
void cancelInstallation();
|
||||||
|
|
||||||
void clearCachedProfile(const QString &serverId, DockerContainer container);
|
void clearCachedProfile(int serverIndex, DockerContainer container);
|
||||||
|
|
||||||
ErrorCode validateAndPrepareConfig(const QString &serverId);
|
ErrorCode validateAndPrepareConfig(int serverIndex);
|
||||||
|
|
||||||
void validateConfig(const QString &serverId);
|
void validateConfig(int serverIndex);
|
||||||
|
|
||||||
void addEmptyServer(const ServerCredentials &credentials);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void configValidated(bool isValid);
|
void configValidated(bool isValid);
|
||||||
@@ -87,8 +74,8 @@ signals:
|
|||||||
|
|
||||||
void serverIsBusy(const bool isBusy);
|
void serverIsBusy(const bool isBusy);
|
||||||
void cancelInstallationRequested();
|
void cancelInstallationRequested();
|
||||||
void clientRevocationRequested(const QString &serverId, const ContainerConfig &containerConfig, DockerContainer container);
|
void clientRevocationRequested(int serverIndex, const ContainerConfig &containerConfig, DockerContainer container);
|
||||||
void clientAppendRequested(const QString &serverId, const QString &clientId, const QString &clientName, DockerContainer container);
|
void clientAppendRequested(int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container, SshSession &sshSession);
|
ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container, SshSession &sshSession);
|
||||||
@@ -108,9 +95,9 @@ private:
|
|||||||
|
|
||||||
ErrorCode processContainerForAdmin(DockerContainer container, ContainerConfig &containerConfig,
|
ErrorCode processContainerForAdmin(DockerContainer container, ContainerConfig &containerConfig,
|
||||||
const ServerCredentials &credentials, SshSession &sshSession,
|
const ServerCredentials &credentials, SshSession &sshSession,
|
||||||
const QString &serverId, const QString &clientName);
|
int serverIndex, const QString &clientName);
|
||||||
|
|
||||||
void adminAppendRequested(const QString &serverId, DockerContainer container,
|
void adminAppendRequested(int serverIndex, DockerContainer container,
|
||||||
const ContainerConfig &containerConfig, const QString &clientName);
|
const ContainerConfig &containerConfig, const QString &clientName);
|
||||||
|
|
||||||
static void updateContainerConfigAfterInstallation(DockerContainer container, ContainerConfig &containerConfig, const QString &stdOut);
|
static void updateContainerConfigAfterInstallation(DockerContainer container, ContainerConfig &containerConfig, const QString &stdOut);
|
||||||
@@ -127,3 +114,4 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif // INSTALLCONTROLLER_H
|
#endif // INSTALLCONTROLLER_H
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "core/protocols/protocolUtils.h"
|
#include "core/protocols/protocolUtils.h"
|
||||||
#include "core/utils/constants/configKeys.h"
|
#include "core/utils/constants/configKeys.h"
|
||||||
#include "core/utils/constants/protocolConstants.h"
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
#include "core/models/containerConfig.h"
|
#include "core/models/containerConfig.h"
|
||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
@@ -291,18 +292,11 @@ ErrorCode UsersController::getXrayClients(const DockerContainer container, const
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode UsersController::updateClients(const QString &serverId, const DockerContainer container)
|
ErrorCode UsersController::updateClients(int serverIndex, const DockerContainer container)
|
||||||
{
|
{
|
||||||
ErrorCode error = ErrorCode::NoError;
|
ErrorCode error = ErrorCode::NoError;
|
||||||
SshSession sshSession;
|
SshSession sshSession;
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
|
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
|
||||||
if (container == DockerContainer::OpenVpn) {
|
if (container == DockerContainer::OpenVpn) {
|
||||||
@@ -387,27 +381,20 @@ ErrorCode UsersController::updateClients(const QString &serverId, const DockerCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ErrorCode UsersController::appendClient(const QString &serverId, const QString &clientId, const QString &clientName, const DockerContainer container)
|
ErrorCode UsersController::appendClient(int serverIndex, const QString &clientId, const QString &clientName, const DockerContainer container)
|
||||||
{
|
{
|
||||||
ErrorCode error = ErrorCode::NoError;
|
ErrorCode error = ErrorCode::NoError;
|
||||||
SshSession sshSession;
|
SshSession sshSession;
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = updateClients(serverId, container);
|
error = updateClients(serverIndex, container);
|
||||||
if (error != ErrorCode::NoError) {
|
if (error != ErrorCode::NoError) {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
int existingIndex = clientIndexById(clientId, m_clientsTable);
|
int existingIndex = clientIndexById(clientId, m_clientsTable);
|
||||||
if (existingIndex >= 0) {
|
if (existingIndex >= 0) {
|
||||||
return renameClient(serverId, existingIndex, clientName, container, true);
|
return renameClient(serverIndex, existingIndex, clientName, container, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject client;
|
QJsonObject client;
|
||||||
@@ -439,7 +426,7 @@ ErrorCode UsersController::appendClient(const QString &serverId, const QString &
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode UsersController::renameClient(const QString &serverId, const int row, const QString &clientName,
|
ErrorCode UsersController::renameClient(int serverIndex, const int row, const QString &clientName,
|
||||||
const DockerContainer container, bool addTimeStamp)
|
const DockerContainer container, bool addTimeStamp)
|
||||||
{
|
{
|
||||||
if (row < 0 || row >= m_clientsTable.size()) {
|
if (row < 0 || row >= m_clientsTable.size()) {
|
||||||
@@ -447,14 +434,7 @@ ErrorCode UsersController::renameClient(const QString &serverId, const int row,
|
|||||||
}
|
}
|
||||||
|
|
||||||
SshSession sshSession;
|
SshSession sshSession;
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto client = m_clientsTable.at(row).toObject();
|
auto client = m_clientsTable.at(row).toObject();
|
||||||
auto userData = client[configKey::userData].toObject();
|
auto userData = client[configKey::userData].toObject();
|
||||||
@@ -490,7 +470,7 @@ ErrorCode UsersController::renameClient(const QString &serverId, const int row,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode UsersController::revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
ErrorCode UsersController::revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||||
SshSession* sshSession, QJsonArray &clientsTable)
|
const int serverIndex, SshSession* sshSession, QJsonArray &clientsTable)
|
||||||
{
|
{
|
||||||
if (row < 0 || row >= clientsTable.size()) {
|
if (row < 0 || row >= clientsTable.size()) {
|
||||||
return ErrorCode::InternalError;
|
return ErrorCode::InternalError;
|
||||||
@@ -709,21 +689,14 @@ ErrorCode UsersController::revokeXray(const int row,
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode UsersController::revokeClient(const QString &serverId, const int index, const DockerContainer container)
|
ErrorCode UsersController::revokeClient(int serverIndex, const int index, const DockerContainer container)
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= m_clientsTable.size()) {
|
if (index < 0 || index >= m_clientsTable.size()) {
|
||||||
return ErrorCode::InternalError;
|
return ErrorCode::InternalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
SshSession sshSession;
|
SshSession sshSession;
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString clientId = m_clientsTable.at(index).toObject().value(configKey::clientId).toString();
|
QString clientId = m_clientsTable.at(index).toObject().value(configKey::clientId).toString();
|
||||||
ErrorCode errorCode = ErrorCode::NoError;
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
@@ -731,7 +704,7 @@ ErrorCode UsersController::revokeClient(const QString &serverId, const int index
|
|||||||
switch(container)
|
switch(container)
|
||||||
{
|
{
|
||||||
case DockerContainer::OpenVpn: {
|
case DockerContainer::OpenVpn: {
|
||||||
errorCode = revokeOpenVpn(index, container, credentials, &sshSession, m_clientsTable);
|
errorCode = revokeOpenVpn(index, container, credentials, serverIndex, &sshSession, m_clientsTable);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DockerContainer::WireGuard:
|
case DockerContainer::WireGuard:
|
||||||
@@ -751,15 +724,12 @@ ErrorCode UsersController::revokeClient(const QString &serverId, const int index
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (errorCode == ErrorCode::NoError) {
|
if (errorCode == ErrorCode::NoError) {
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
|
||||||
if (!adminConfig.has_value()) {
|
ContainerConfig containerCfg = m_serversRepository->containerConfig(serverIndex, container);
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
ContainerConfig containerCfg = adminConfig->containerConfig(container);
|
|
||||||
QString containerClientId = containerCfg.protocolConfig.clientId();
|
QString containerClientId = containerCfg.protocolConfig.clientId();
|
||||||
|
|
||||||
if (!clientId.isEmpty() && !containerClientId.isEmpty() && containerClientId.contains(clientId)) {
|
if (!clientId.isEmpty() && !containerClientId.isEmpty() && containerClientId.contains(clientId)) {
|
||||||
emit adminConfigRevoked(serverId, container);
|
emit adminConfigRevoked(serverIndex, container);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit clientRevoked(index);
|
emit clientRevoked(index);
|
||||||
@@ -769,20 +739,13 @@ ErrorCode UsersController::revokeClient(const QString &serverId, const int index
|
|||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode UsersController::revokeClient(const QString &serverId, const ContainerConfig &containerConfig, const DockerContainer container)
|
ErrorCode UsersController::revokeClient(int serverIndex, const ContainerConfig &containerConfig, const DockerContainer container)
|
||||||
{
|
{
|
||||||
SshSession sshSession;
|
SshSession sshSession;
|
||||||
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
if (!adminConfig.has_value()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
ServerCredentials credentials = adminConfig->credentials();
|
|
||||||
if (!credentials.isValid()) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode errorCode = ErrorCode::NoError;
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
errorCode = updateClients(serverId, container);
|
errorCode = updateClients(serverIndex, container);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
@@ -815,7 +778,7 @@ ErrorCode UsersController::revokeClient(const QString &serverId, const Container
|
|||||||
switch (container)
|
switch (container)
|
||||||
{
|
{
|
||||||
case DockerContainer::OpenVpn: {
|
case DockerContainer::OpenVpn: {
|
||||||
errorCode = revokeOpenVpn(row, container, credentials, &sshSession, m_clientsTable);
|
errorCode = revokeOpenVpn(row, container, credentials, serverIndex, &sshSession, m_clientsTable);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DockerContainer::WireGuard:
|
case DockerContainer::WireGuard:
|
||||||
@@ -834,7 +797,7 @@ ErrorCode UsersController::revokeClient(const QString &serverId, const Container
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (errorCode == ErrorCode::NoError) {
|
if (errorCode == ErrorCode::NoError) {
|
||||||
emit adminConfigRevoked(serverId, container);
|
emit adminConfigRevoked(serverIndex, container);
|
||||||
emit clientRevoked(row);
|
emit clientRevoked(row);
|
||||||
emit clientsUpdated(m_clientsTable);
|
emit clientsUpdated(m_clientsTable);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,21 +37,21 @@ signals:
|
|||||||
void clientAdded(const QJsonObject &client);
|
void clientAdded(const QJsonObject &client);
|
||||||
void clientRenamed(int row, const QString &newName);
|
void clientRenamed(int row, const QString &newName);
|
||||||
void clientRevoked(int row);
|
void clientRevoked(int row);
|
||||||
void adminConfigRevoked(const QString &serverId, DockerContainer container);
|
void adminConfigRevoked(int serverIndex, DockerContainer container);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
ErrorCode updateClients(const QString &serverId, const DockerContainer container);
|
ErrorCode updateClients(int serverIndex, const DockerContainer container);
|
||||||
ErrorCode appendClient(const QString &serverId, const QString &clientId, const QString &clientName, const DockerContainer container);
|
ErrorCode appendClient(int serverIndex, const QString &clientId, const QString &clientName, const DockerContainer container);
|
||||||
ErrorCode renameClient(const QString &serverId, const int row, const QString &userName, const DockerContainer container, bool addTimeStamp = false);
|
ErrorCode renameClient(int serverIndex, const int row, const QString &userName, const DockerContainer container, bool addTimeStamp = false);
|
||||||
ErrorCode revokeClient(const QString &serverId, const int index, const DockerContainer container);
|
ErrorCode revokeClient(int serverIndex, const int index, const DockerContainer container);
|
||||||
ErrorCode revokeClient(const QString &serverId, const ContainerConfig &containerConfig, const DockerContainer container);
|
ErrorCode revokeClient(int serverIndex, const ContainerConfig &containerConfig, const DockerContainer container);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isClientExists(const QString &clientId, const QJsonArray &clientsTable);
|
bool isClientExists(const QString &clientId, const QJsonArray &clientsTable);
|
||||||
int clientIndexById(const QString &clientId, const QJsonArray &clientsTable);
|
int clientIndexById(const QString &clientId, const QJsonArray &clientsTable);
|
||||||
void migration(const QByteArray &clientsTableString, QJsonArray &clientsTable);
|
void migration(const QByteArray &clientsTableString, QJsonArray &clientsTable);
|
||||||
|
|
||||||
ErrorCode revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
ErrorCode revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials, const int serverIndex,
|
||||||
SshSession* sshSession, QJsonArray &clientsTable);
|
SshSession* sshSession, QJsonArray &clientsTable);
|
||||||
ErrorCode revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
ErrorCode revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||||
SshSession* sshSession, QJsonArray &clientsTable);
|
SshSession* sshSession, QJsonArray &clientsTable);
|
||||||
@@ -73,3 +73,4 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif // USERSCONTROLLER_H
|
#endif // USERSCONTROLLER_H
|
||||||
|
|
||||||
|
|||||||
@@ -1,268 +1,81 @@
|
|||||||
#include "serversController.h"
|
#include "serversController.h"
|
||||||
#include "core/utils/serverConfigUtils.h"
|
#include "core/utils/networkUtilities.h"
|
||||||
|
#include "core/utils/api/apiEnums.h"
|
||||||
|
#include "core/utils/constants/apiKeys.h"
|
||||||
|
#include "core/utils/constants/apiConstants.h"
|
||||||
#include "core/utils/protocolEnum.h"
|
#include "core/utils/protocolEnum.h"
|
||||||
#include "core/protocols/protocolUtils.h"
|
#include "core/protocols/protocolUtils.h"
|
||||||
#include "core/utils/constants/configKeys.h"
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
#include "core/models/containerConfig.h"
|
#include "core/models/containerConfig.h"
|
||||||
|
|
||||||
#include "core/models/serverDescription.h"
|
|
||||||
|
|
||||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <AmneziaVPN-Swift.h>
|
#include <AmneziaVPN-Swift.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
ServersController::ServersController(SecureServersRepository* serversRepository,
|
ServersController::ServersController(SecureServersRepository* serversRepository,
|
||||||
SecureAppSettingsRepository *appSettingsRepository, QObject *parent)
|
SecureAppSettingsRepository* appSettingsRepository,
|
||||||
|
QObject *parent)
|
||||||
: QObject(parent), m_serversRepository(serversRepository), m_appSettingsRepository(appSettingsRepository)
|
: QObject(parent), m_serversRepository(serversRepository), m_appSettingsRepository(appSettingsRepository)
|
||||||
{
|
{
|
||||||
ensureDefaultServerValid();
|
recomputeGatewayStacks();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServersController::ensureDefaultServerValid()
|
void ServersController::addServer(const ServerConfig &server)
|
||||||
{
|
{
|
||||||
if (!getServersCount()) {
|
m_serversRepository->addServer(server);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString defaultId = getDefaultServerId();
|
void ServersController::editServer(int index, const ServerConfig &server)
|
||||||
if (!defaultId.isEmpty() && indexOfServerId(defaultId) >= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString firstId = getServerId(0);
|
|
||||||
if (!firstId.isEmpty()) {
|
|
||||||
setDefaultServer(firstId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ServersController::renameServer(const QString &serverId, const QString &name)
|
|
||||||
{
|
{
|
||||||
const serverConfigUtils::ConfigType kind = m_serversRepository->serverKind(serverId);
|
m_serversRepository->editServer(index, server);
|
||||||
switch (kind) {
|
|
||||||
case serverConfigUtils::ConfigType::SelfHostedAdmin: {
|
|
||||||
auto cfg = m_serversRepository->selfHostedAdminConfig(serverId);
|
|
||||||
if (!cfg.has_value()) return false;
|
|
||||||
cfg->description = name;
|
|
||||||
m_serversRepository->editServer(serverId, cfg->toJson(), kind);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::SelfHostedUser: {
|
|
||||||
auto cfg = m_serversRepository->selfHostedUserConfig(serverId);
|
|
||||||
if (!cfg.has_value()) return false;
|
|
||||||
cfg->description = name;
|
|
||||||
m_serversRepository->editServer(serverId, cfg->toJson(), kind);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::Native: {
|
|
||||||
auto cfg = m_serversRepository->nativeConfig(serverId);
|
|
||||||
if (!cfg.has_value()) return false;
|
|
||||||
cfg->description = name;
|
|
||||||
m_serversRepository->editServer(serverId, cfg->toJson(), kind);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaPremiumV2:
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaFreeV3:
|
|
||||||
case serverConfigUtils::ConfigType::ExternalPremium: {
|
|
||||||
auto cfg = m_serversRepository->apiV2Config(serverId);
|
|
||||||
if (!cfg.has_value()) return false;
|
|
||||||
cfg->name = name;
|
|
||||||
cfg->nameOverriddenByUser = true;
|
|
||||||
m_serversRepository->editServer(serverId, cfg->toJson(), kind);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaPremiumV1:
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaFreeV2:
|
|
||||||
case serverConfigUtils::ConfigType::Invalid:
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServersController::removeServer(const QString &serverId)
|
void ServersController::removeServer(int index)
|
||||||
{
|
{
|
||||||
m_serversRepository->removeServer(serverId);
|
m_serversRepository->removeServer(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServersController::setDefaultServer(const QString &serverId)
|
void ServersController::setDefaultServerIndex(int index)
|
||||||
{
|
{
|
||||||
m_serversRepository->setDefaultServer(serverId);
|
m_serversRepository->setDefaultServer(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServersController::setDefaultContainer(const QString &serverId, DockerContainer container)
|
void ServersController::setDefaultContainer(int serverIndex, DockerContainer container)
|
||||||
{
|
{
|
||||||
const serverConfigUtils::ConfigType kind = m_serversRepository->serverKind(serverId);
|
m_serversRepository->setDefaultContainer(serverIndex, container);
|
||||||
switch (kind) {
|
|
||||||
case serverConfigUtils::ConfigType::SelfHostedAdmin: {
|
|
||||||
auto cfg = m_serversRepository->selfHostedAdminConfig(serverId);
|
|
||||||
if (!cfg.has_value()) return;
|
|
||||||
cfg->defaultContainer = container;
|
|
||||||
m_serversRepository->editServer(serverId, cfg->toJson(), kind);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::SelfHostedUser: {
|
|
||||||
auto cfg = m_serversRepository->selfHostedUserConfig(serverId);
|
|
||||||
if (!cfg.has_value()) return;
|
|
||||||
cfg->defaultContainer = container;
|
|
||||||
m_serversRepository->editServer(serverId, cfg->toJson(), kind);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::Native: {
|
|
||||||
auto cfg = m_serversRepository->nativeConfig(serverId);
|
|
||||||
if (!cfg.has_value()) return;
|
|
||||||
cfg->defaultContainer = container;
|
|
||||||
m_serversRepository->editServer(serverId, cfg->toJson(), kind);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaPremiumV2:
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaFreeV3:
|
|
||||||
case serverConfigUtils::ConfigType::ExternalPremium: {
|
|
||||||
auto cfg = m_serversRepository->apiV2Config(serverId);
|
|
||||||
if (!cfg.has_value()) return;
|
|
||||||
cfg->defaultContainer = container;
|
|
||||||
m_serversRepository->editServer(serverId, cfg->toJson(), kind);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaPremiumV1:
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaFreeV2:
|
|
||||||
case serverConfigUtils::ConfigType::Invalid:
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<ServerDescription> ServersController::buildServerDescriptions(bool isAmneziaDnsEnabled) const
|
void ServersController::updateContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config)
|
||||||
{
|
{
|
||||||
QVector<ServerDescription> out;
|
m_serversRepository->setContainerConfig(serverIndex, container, config);
|
||||||
const QVector<QString> ids = m_serversRepository->orderedServerIds();
|
|
||||||
out.reserve(ids.size());
|
|
||||||
|
|
||||||
for (const QString &id : ids) {
|
|
||||||
ServerDescription d;
|
|
||||||
using Kind = serverConfigUtils::ConfigType;
|
|
||||||
const Kind kind = m_serversRepository->serverKind(id);
|
|
||||||
switch (kind) {
|
|
||||||
case Kind::SelfHostedAdmin: {
|
|
||||||
const auto cfg = m_serversRepository->selfHostedAdminConfig(id);
|
|
||||||
if (!cfg) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
d = buildServerDescription(*cfg, isAmneziaDnsEnabled);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Kind::SelfHostedUser: {
|
|
||||||
const auto cfg = m_serversRepository->selfHostedUserConfig(id);
|
|
||||||
if (!cfg) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
d = buildServerDescription(*cfg, isAmneziaDnsEnabled);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Kind::Native: {
|
|
||||||
const auto cfg = m_serversRepository->nativeConfig(id);
|
|
||||||
if (!cfg) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
d = buildServerDescription(*cfg, isAmneziaDnsEnabled);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Kind::AmneziaPremiumV2:
|
|
||||||
case Kind::AmneziaFreeV3:
|
|
||||||
case Kind::ExternalPremium: {
|
|
||||||
const auto cfg = m_serversRepository->apiV2Config(id);
|
|
||||||
if (!cfg) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
d = buildServerDescription(*cfg, isAmneziaDnsEnabled);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Kind::AmneziaPremiumV1:
|
|
||||||
case Kind::AmneziaFreeV2: {
|
|
||||||
const auto cfg = m_serversRepository->legacyApiConfig(id);
|
|
||||||
if (!cfg) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
d = buildServerDescription(*cfg, isAmneziaDnsEnabled);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Kind::Invalid:
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
d.serverId = id;
|
void ServersController::clearCachedProfile(int serverIndex, DockerContainer container)
|
||||||
out.append(d);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
QMap<DockerContainer, ContainerConfig> ServersController::getServerContainersMap(const QString &serverId) const
|
|
||||||
{
|
{
|
||||||
switch (m_serversRepository->serverKind(serverId)) {
|
m_serversRepository->clearLastConnectionConfig(serverIndex, container);
|
||||||
case serverConfigUtils::ConfigType::SelfHostedAdmin: {
|
|
||||||
const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId);
|
|
||||||
return cfg.has_value() ? cfg->containers : QMap<DockerContainer, ContainerConfig>{};
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::SelfHostedUser: {
|
|
||||||
const auto cfg = m_serversRepository->selfHostedUserConfig(serverId);
|
|
||||||
return cfg.has_value() ? cfg->containers : QMap<DockerContainer, ContainerConfig>{};
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::Native: {
|
|
||||||
const auto cfg = m_serversRepository->nativeConfig(serverId);
|
|
||||||
return cfg.has_value() ? cfg->containers : QMap<DockerContainer, ContainerConfig>{};
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaPremiumV2:
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaFreeV3:
|
|
||||||
case serverConfigUtils::ConfigType::ExternalPremium: {
|
|
||||||
const auto cfg = m_serversRepository->apiV2Config(serverId);
|
|
||||||
return cfg.has_value() ? cfg->containers : QMap<DockerContainer, ContainerConfig>{};
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaPremiumV1:
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaFreeV2: {
|
|
||||||
const auto cfg = m_serversRepository->legacyApiConfig(serverId);
|
|
||||||
return cfg.has_value() ? cfg->containers : QMap<DockerContainer, ContainerConfig>{};
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::Invalid:
|
|
||||||
default:
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DockerContainer ServersController::getDefaultContainer(const QString &serverId) const
|
QJsonArray ServersController::getServersArray() const
|
||||||
{
|
{
|
||||||
switch (m_serversRepository->serverKind(serverId)) {
|
QJsonArray result;
|
||||||
case serverConfigUtils::ConfigType::SelfHostedAdmin: {
|
QVector<ServerConfig> servers = m_serversRepository->servers();
|
||||||
const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId);
|
for (const ServerConfig& server : servers) {
|
||||||
return cfg.has_value() ? cfg->defaultContainer : DockerContainer::None;
|
result.append(server.toJson());
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::SelfHostedUser: {
|
|
||||||
const auto cfg = m_serversRepository->selfHostedUserConfig(serverId);
|
|
||||||
return cfg.has_value() ? cfg->defaultContainer : DockerContainer::None;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::Native: {
|
|
||||||
const auto cfg = m_serversRepository->nativeConfig(serverId);
|
|
||||||
return cfg.has_value() ? cfg->defaultContainer : DockerContainer::None;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaPremiumV2:
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaFreeV3:
|
|
||||||
case serverConfigUtils::ConfigType::ExternalPremium: {
|
|
||||||
const auto cfg = m_serversRepository->apiV2Config(serverId);
|
|
||||||
return cfg.has_value() ? cfg->defaultContainer : DockerContainer::None;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaPremiumV1:
|
|
||||||
case serverConfigUtils::ConfigType::AmneziaFreeV2: {
|
|
||||||
const auto cfg = m_serversRepository->legacyApiConfig(serverId);
|
|
||||||
return cfg.has_value() ? cfg->defaultContainer : DockerContainer::None;
|
|
||||||
}
|
|
||||||
case serverConfigUtils::ConfigType::Invalid:
|
|
||||||
default:
|
|
||||||
return DockerContainer::None;
|
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContainerConfig ServersController::getContainerConfig(const QString &serverId, DockerContainer container) const
|
QVector<ServerConfig> ServersController::getServers() const
|
||||||
{
|
{
|
||||||
return getServerContainersMap(serverId).value(container);
|
return m_serversRepository->servers();
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainerConfig ServersController::getContainerConfig(int serverIndex, DockerContainer container) const
|
||||||
|
{
|
||||||
|
return m_serversRepository->containerConfig(serverIndex, container);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ServersController::getDefaultServerIndex() const
|
int ServersController::getDefaultServerIndex() const
|
||||||
@@ -270,131 +83,114 @@ int ServersController::getDefaultServerIndex() const
|
|||||||
return m_serversRepository->defaultServerIndex();
|
return m_serversRepository->defaultServerIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ServersController::getDefaultServerId() const
|
|
||||||
{
|
|
||||||
return m_serversRepository->defaultServerId();
|
|
||||||
}
|
|
||||||
|
|
||||||
int ServersController::getServersCount() const
|
int ServersController::getServersCount() const
|
||||||
{
|
{
|
||||||
return m_serversRepository->serversCount();
|
return m_serversRepository->serversCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ServersController::getServerId(int serverIndex) const
|
ServerConfig ServersController::getServerConfig(int serverIndex) const
|
||||||
{
|
{
|
||||||
return m_serversRepository->serverIdAt(serverIndex);
|
return m_serversRepository->server(serverIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ServersController::indexOfServerId(const QString &serverId) const
|
ServerCredentials ServersController::getServerCredentials(int serverIndex) const
|
||||||
{
|
{
|
||||||
return m_serversRepository->indexOfServerId(serverId);
|
return m_serversRepository->serverCredentials(serverIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ServersController::notificationDisplayName(const QString &serverId) const
|
QPair<QString, QString> ServersController::getDnsPair(int serverIndex, bool isAmneziaDnsEnabled) const
|
||||||
{
|
{
|
||||||
if (serverId.isEmpty()) {
|
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
|
||||||
return {};
|
return serverConfig.getDnsPair(isAmneziaDnsEnabled,
|
||||||
|
m_appSettingsRepository->primaryDns(),
|
||||||
|
m_appSettingsRepository->secondaryDns());
|
||||||
}
|
}
|
||||||
|
|
||||||
using Kind = serverConfigUtils::ConfigType;
|
ServersController::GatewayStacksData ServersController::gatewayStacks() const
|
||||||
switch (m_serversRepository->serverKind(serverId)) {
|
|
||||||
case Kind::SelfHostedAdmin: {
|
|
||||||
if (const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId)) {
|
|
||||||
if (!cfg->displayName.isEmpty()) {
|
|
||||||
return cfg->displayName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Kind::SelfHostedUser: {
|
|
||||||
if (const auto cfg = m_serversRepository->selfHostedUserConfig(serverId)) {
|
|
||||||
if (!cfg->displayName.isEmpty()) {
|
|
||||||
return cfg->displayName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Kind::Native: {
|
|
||||||
if (const auto cfg = m_serversRepository->nativeConfig(serverId)) {
|
|
||||||
if (!cfg->displayName.isEmpty()) {
|
|
||||||
return cfg->displayName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Kind::AmneziaPremiumV2:
|
|
||||||
case Kind::AmneziaFreeV3:
|
|
||||||
case Kind::ExternalPremium: {
|
|
||||||
if (const auto cfg = m_serversRepository->apiV2Config(serverId)) {
|
|
||||||
if (!cfg->displayName.isEmpty()) {
|
|
||||||
return cfg->displayName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Kind::AmneziaPremiumV1:
|
|
||||||
case Kind::AmneziaFreeV2: {
|
|
||||||
if (const auto cfg = m_serversRepository->legacyApiConfig(serverId)) {
|
|
||||||
if (!cfg->displayName.isEmpty()) {
|
|
||||||
return cfg->displayName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int idx = indexOfServerId(serverId);
|
|
||||||
if (idx >= 0) {
|
|
||||||
return QString::number(idx + 1);
|
|
||||||
}
|
|
||||||
return serverId;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<ApiV2ServerConfig> ServersController::apiV2Config(const QString &serverId) const
|
|
||||||
{
|
{
|
||||||
return m_serversRepository->apiV2Config(serverId);
|
return m_gatewayStacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<SelfHostedAdminServerConfig> ServersController::selfHostedAdminConfig(const QString &serverId) const
|
void ServersController::recomputeGatewayStacks()
|
||||||
{
|
{
|
||||||
return m_serversRepository->selfHostedAdminConfig(serverId);
|
GatewayStacksData computed;
|
||||||
|
bool hasNewTags = false;
|
||||||
|
QVector<ServerConfig> servers = m_serversRepository->servers();
|
||||||
|
|
||||||
|
for (const ServerConfig& serverConfig : servers) {
|
||||||
|
if (serverConfig.isApiV2()) {
|
||||||
|
const ApiV2ServerConfig* apiV2 = serverConfig.as<ApiV2ServerConfig>();
|
||||||
|
if (!apiV2) continue;
|
||||||
|
const QString userCountryCode = apiV2->apiConfig.userCountryCode;
|
||||||
|
const QString serviceType = apiV2->serviceType();
|
||||||
|
|
||||||
|
if (!userCountryCode.isEmpty()) {
|
||||||
|
if (!m_gatewayStacks.userCountryCodes.contains(userCountryCode)) {
|
||||||
|
hasNewTags = true;
|
||||||
|
}
|
||||||
|
computed.userCountryCodes.insert(userCountryCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerCredentials ServersController::getServerCredentials(const QString &serverId) const
|
if (!serviceType.isEmpty()) {
|
||||||
{
|
if (!m_gatewayStacks.serviceTypes.contains(serviceType)) {
|
||||||
const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId);
|
hasNewTags = true;
|
||||||
if (cfg.has_value()) {
|
}
|
||||||
const ServerCredentials creds = cfg->credentials();
|
computed.serviceTypes.insert(serviceType);
|
||||||
if (creds.isValid()) {
|
|
||||||
return creds;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ServerCredentials {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ServersController::isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType,
|
m_gatewayStacks = std::move(computed);
|
||||||
const QString &serviceProtocol) const
|
if (hasNewTags) {
|
||||||
{
|
emit gatewayStacksExpanded();
|
||||||
const QVector<QString> ids = m_serversRepository->orderedServerIds();
|
|
||||||
for (const QString &id : ids) {
|
|
||||||
const auto apiV2 = m_serversRepository->apiV2Config(id);
|
|
||||||
if (!apiV2.has_value()) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if (apiV2->apiConfig.userCountryCode == userCountryCode && apiV2->serviceType() == serviceType
|
}
|
||||||
|
|
||||||
|
bool ServersController::GatewayStacksData::operator==(const GatewayStacksData &other) const
|
||||||
|
{
|
||||||
|
return userCountryCodes == other.userCountryCodes && serviceTypes == other.serviceTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ServersController::GatewayStacksData::toJson() const
|
||||||
|
{
|
||||||
|
QJsonObject json;
|
||||||
|
|
||||||
|
QJsonArray userCountryCodesArray;
|
||||||
|
for (const QString &code : userCountryCodes) {
|
||||||
|
userCountryCodesArray.append(code);
|
||||||
|
}
|
||||||
|
json[apiDefs::key::userCountryCode] = userCountryCodesArray;
|
||||||
|
|
||||||
|
QJsonArray serviceTypesArray;
|
||||||
|
for (const QString &type : serviceTypes) {
|
||||||
|
serviceTypesArray.append(type);
|
||||||
|
}
|
||||||
|
json[apiDefs::key::serviceType] = serviceTypesArray;
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ServersController::isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol) const
|
||||||
|
{
|
||||||
|
QVector<ServerConfig> servers = m_serversRepository->servers();
|
||||||
|
for (const ServerConfig& serverConfig : servers) {
|
||||||
|
if (serverConfig.isApiV2()) {
|
||||||
|
const ApiV2ServerConfig* apiV2 = serverConfig.as<ApiV2ServerConfig>();
|
||||||
|
if (!apiV2) return false;
|
||||||
|
if (apiV2->apiConfig.userCountryCode == userCountryCode
|
||||||
|
&& apiV2->serviceType() == serviceType
|
||||||
&& apiV2->serviceProtocol() == serviceProtocol) {
|
&& apiV2->serviceProtocol() == serviceProtocol) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ServersController::hasInstalledContainers(const QString &serverId) const
|
bool ServersController::hasInstalledContainers(int serverIndex) const
|
||||||
{
|
{
|
||||||
const QMap<DockerContainer, ContainerConfig> containers = getServerContainersMap(serverId);
|
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
|
||||||
|
QMap<DockerContainer, ContainerConfig> containers = serverConfig.containers();
|
||||||
for (auto it = containers.begin(); it != containers.end(); ++it) {
|
for (auto it = containers.begin(); it != containers.end(); ++it) {
|
||||||
DockerContainer container = it.key();
|
DockerContainer container = it.key();
|
||||||
if (ContainerUtils::containerService(container) == ServiceType::Vpn) {
|
if (ContainerUtils::containerService(container) == ServiceType::Vpn) {
|
||||||
@@ -407,8 +203,3 @@ bool ServersController::hasInstalledContainers(const QString &serverId) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ServersController::isLegacyApiV1Server(const QString &serverId) const
|
|
||||||
{
|
|
||||||
return !serverId.isEmpty()
|
|
||||||
&& serverConfigUtils::isLegacyApiSubscription(m_serversRepository->serverKind(serverId));
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
#ifndef SERVERSCONTROLLER_H
|
#ifndef SERVERSCONTROLLER_H
|
||||||
#define SERVERSCONTROLLER_H
|
#define SERVERSCONTROLLER_H
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QSet>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QMap>
|
|
||||||
|
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
|
|
||||||
@@ -17,18 +17,34 @@
|
|||||||
#include "core/utils/commonStructs.h"
|
#include "core/utils/commonStructs.h"
|
||||||
#include "core/repositories/secureServersRepository.h"
|
#include "core/repositories/secureServersRepository.h"
|
||||||
#include "core/repositories/secureAppSettingsRepository.h"
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
#include "core/models/containerConfig.h"
|
#include "core/models/containerConfig.h"
|
||||||
#include "core/models/serverDescription.h"
|
|
||||||
|
|
||||||
class SshSession;
|
class SshSession;
|
||||||
class InstallController;
|
class InstallController;
|
||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Core business logic controller for server operations
|
||||||
|
*
|
||||||
|
* This controller contains pure business logic for managing servers.
|
||||||
|
*/
|
||||||
class ServersController : public QObject
|
class ServersController : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct GatewayStacksData
|
||||||
|
{
|
||||||
|
QSet<QString> userCountryCodes;
|
||||||
|
QSet<QString> serviceTypes;
|
||||||
|
|
||||||
|
bool isEmpty() const { return userCountryCodes.isEmpty() && serviceTypes.isEmpty(); }
|
||||||
|
bool operator==(const GatewayStacksData &other) const;
|
||||||
|
QJsonObject toJson() const;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ServersController(SecureServersRepository* serversRepository,
|
explicit ServersController(SecureServersRepository* serversRepository,
|
||||||
SecureAppSettingsRepository* appSettingsRepository = nullptr,
|
SecureAppSettingsRepository* appSettingsRepository = nullptr,
|
||||||
@@ -36,38 +52,44 @@ public:
|
|||||||
~ServersController() = default;
|
~ServersController() = default;
|
||||||
|
|
||||||
// Server management
|
// Server management
|
||||||
bool renameServer(const QString &serverId, const QString &name);
|
void addServer(const ServerConfig &server);
|
||||||
void removeServer(const QString &serverId);
|
void editServer(int index, const ServerConfig &server);
|
||||||
void setDefaultServer(const QString &serverId);
|
void removeServer(int index);
|
||||||
|
void setDefaultServerIndex(int index);
|
||||||
|
|
||||||
// Container management
|
// Container management
|
||||||
void setDefaultContainer(const QString &serverId, DockerContainer container);
|
void setDefaultContainer(int serverIndex, DockerContainer container);
|
||||||
|
void updateContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config);
|
||||||
|
|
||||||
|
// Cache management
|
||||||
|
void clearCachedProfile(int serverIndex, DockerContainer container);
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
QVector<ServerDescription> buildServerDescriptions(bool isAmneziaDnsEnabled) const;
|
QJsonArray getServersArray() const;
|
||||||
|
QVector<ServerConfig> getServers() const;
|
||||||
int getDefaultServerIndex() const;
|
int getDefaultServerIndex() const;
|
||||||
QString getDefaultServerId() const;
|
|
||||||
int getServersCount() const;
|
int getServersCount() const;
|
||||||
QString getServerId(int serverIndex) const;
|
ServerConfig getServerConfig(int serverIndex) const;
|
||||||
int indexOfServerId(const QString &serverId) const;
|
ServerCredentials getServerCredentials(int serverIndex) const;
|
||||||
QString notificationDisplayName(const QString &serverId) const;
|
ContainerConfig getContainerConfig(int serverIndex, DockerContainer container) const;
|
||||||
std::optional<ApiV2ServerConfig> apiV2Config(const QString &serverId) const;
|
QPair<QString, QString> getDnsPair(int serverIndex, bool isAmneziaDnsEnabled) const;
|
||||||
std::optional<SelfHostedAdminServerConfig> selfHostedAdminConfig(const QString &serverId) const;
|
|
||||||
ServerCredentials getServerCredentials(const QString &serverId) const;
|
GatewayStacksData gatewayStacks() const;
|
||||||
QMap<DockerContainer, ContainerConfig> getServerContainersMap(const QString &serverId) const;
|
|
||||||
DockerContainer getDefaultContainer(const QString &serverId) const;
|
|
||||||
ContainerConfig getContainerConfig(const QString &serverId, DockerContainer container) const;
|
|
||||||
|
|
||||||
// Validation
|
// Validation
|
||||||
bool isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol) const;
|
bool isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol) const;
|
||||||
bool hasInstalledContainers(const QString &serverId) const;
|
bool hasInstalledContainers(int serverIndex) const;
|
||||||
bool isLegacyApiV1Server(const QString &serverId) const;
|
|
||||||
|
signals:
|
||||||
|
void gatewayStacksExpanded();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void recomputeGatewayStacks();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ensureDefaultServerValid();
|
|
||||||
|
|
||||||
SecureServersRepository* m_serversRepository;
|
SecureServersRepository* m_serversRepository;
|
||||||
SecureAppSettingsRepository* m_appSettingsRepository;
|
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||||
|
GatewayStacksData m_gatewayStacks;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SERVERSCONTROLLER_H
|
#endif // SERVERSCONTROLLER_H
|
||||||
|
|||||||
@@ -179,9 +179,12 @@ QString SettingsController::getAppVersion() const
|
|||||||
|
|
||||||
void SettingsController::clearSettings()
|
void SettingsController::clearSettings()
|
||||||
{
|
{
|
||||||
|
int serverCount = m_serversRepository->serversCount();
|
||||||
|
|
||||||
m_appSettingsRepository->clearSettings();
|
m_appSettingsRepository->clearSettings();
|
||||||
|
|
||||||
m_serversRepository->clearServers();
|
m_serversRepository->setServersArray(QJsonArray());
|
||||||
|
m_serversRepository->setDefaultServer(0);
|
||||||
|
|
||||||
emit siteSplitTunnelingRouteModeChanged(RouteMode::VpnOnlyForwardSites);
|
emit siteSplitTunnelingRouteModeChanged(RouteMode::VpnOnlyForwardSites);
|
||||||
emit siteSplitTunnelingToggled(false);
|
emit siteSplitTunnelingToggled(false);
|
||||||
|
|||||||
@@ -1,363 +0,0 @@
|
|||||||
#include "updateController.h"
|
|
||||||
|
|
||||||
#include <QNetworkReply>
|
|
||||||
#include <QVersionNumber>
|
|
||||||
#include <QUrl>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QSysInfo>
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
#include "amneziaApplication.h"
|
|
||||||
#include "logger.h"
|
|
||||||
#include "version.h"
|
|
||||||
#include "core/controllers/gatewayController.h"
|
|
||||||
#include "core/utils/constants/apiKeys.h"
|
|
||||||
#include "core/utils/errorStrings.h"
|
|
||||||
#include "core/utils/selfhosted/scriptsRegistry.h"
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
Logger logger("UpdateController");
|
|
||||||
|
|
||||||
#if defined(Q_OS_WINDOWS)
|
|
||||||
const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN_%1_windows_x64.exe");
|
|
||||||
const QString kInstallerLocalPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/AmneziaVPN_installer.exe";
|
|
||||||
#elif defined(Q_OS_MACOS)
|
|
||||||
const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN_%1_macos_x64.pkg");
|
|
||||||
const QString kInstallerLocalPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/AmneziaVPN.pkg";
|
|
||||||
#elif defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
|
||||||
const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN_%1_linux_x64.run");
|
|
||||||
const QString kInstallerLocalPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/AmneziaVPN.run";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateController::UpdateController(SecureAppSettingsRepository* appSettingsRepository, QObject *parent)
|
|
||||||
: QObject(parent), m_appSettingsRepository(appSettingsRepository)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString UpdateController::getRawChangelogText() const
|
|
||||||
{
|
|
||||||
return m_changelogText;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString UpdateController::getReleaseDate() const
|
|
||||||
{
|
|
||||||
return m_releaseDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString UpdateController::getVersion() const
|
|
||||||
{
|
|
||||||
return m_version;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateController::checkForUpdates()
|
|
||||||
{
|
|
||||||
if (m_updateCheckRunning || !m_appSettingsRepository) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_updateCheckRunning = true;
|
|
||||||
|
|
||||||
fetchGatewayUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateController::finishUpdateCheck()
|
|
||||||
{
|
|
||||||
m_updateCheckRunning = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateController::doGetAsync(const QString &endpoint, std::function<void(bool, QByteArray)> onDone)
|
|
||||||
{
|
|
||||||
QString fullUrl = m_baseUrl + endpoint;
|
|
||||||
|
|
||||||
QNetworkRequest req;
|
|
||||||
req.setTransferTimeout(7000);
|
|
||||||
req.setUrl(QUrl(fullUrl));
|
|
||||||
|
|
||||||
QNetworkReply *reply = amnApp->networkManager()->get(req);
|
|
||||||
setupNetworkErrorHandling(reply, endpoint);
|
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, endpoint, onDone]() {
|
|
||||||
const bool ok = (reply->error() == QNetworkReply::NoError);
|
|
||||||
QByteArray data;
|
|
||||||
if (ok) {
|
|
||||||
data = reply->readAll();
|
|
||||||
} else {
|
|
||||||
handleNetworkError(reply, endpoint);
|
|
||||||
}
|
|
||||||
reply->deleteLater();
|
|
||||||
onDone(ok, data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateController::fetchGatewayUrl()
|
|
||||||
{
|
|
||||||
auto gatewayController = QSharedPointer<GatewayController>::create(m_appSettingsRepository->getGatewayEndpoint(),
|
|
||||||
m_appSettingsRepository->isDevGatewayEnv(),
|
|
||||||
7000,
|
|
||||||
m_appSettingsRepository->isStrictKillSwitchEnabled());
|
|
||||||
|
|
||||||
QJsonObject apiPayload;
|
|
||||||
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
|
||||||
apiPayload[apiDefs::key::osVersion] = QSysInfo::productType();
|
|
||||||
apiPayload[apiDefs::key::installationUuid] = m_appSettingsRepository->getInstallationUuid(true);
|
|
||||||
|
|
||||||
// Workaround: wait before contacting gateway to avoid rate limit triggered by other requests (news etc.)
|
|
||||||
QTimer::singleShot(1000, this, [this, gatewayController, apiPayload]() {
|
|
||||||
gatewayController->postAsync(QStringLiteral("%1v1/updater_endpoint"), apiPayload)
|
|
||||||
.then(this, [this, gatewayController](QPair<ErrorCode, QByteArray> result) {
|
|
||||||
auto [err, gatewayResponse] = result;
|
|
||||||
if (err != ErrorCode::NoError) {
|
|
||||||
logger.error() << errorString(err);
|
|
||||||
finishUpdateCheck();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject gatewayData = QJsonDocument::fromJson(gatewayResponse).object();
|
|
||||||
|
|
||||||
QString baseUrl = gatewayData.value("url").toString();
|
|
||||||
if (baseUrl.endsWith('/')) {
|
|
||||||
baseUrl.chop(1);
|
|
||||||
}
|
|
||||||
m_baseUrl = baseUrl;
|
|
||||||
|
|
||||||
fetchVersionInfo();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateController::fetchVersionInfo()
|
|
||||||
{
|
|
||||||
doGetAsync("/VERSION", [this](bool ok, QByteArray data) {
|
|
||||||
if (!ok) {
|
|
||||||
finishUpdateCheck();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_version = QString::fromUtf8(data).trimmed();
|
|
||||||
|
|
||||||
if (!isNewVersionAvailable()) {
|
|
||||||
finishUpdateCheck();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fetchChangelog();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateController::fetchChangelog()
|
|
||||||
{
|
|
||||||
doGetAsync("/CHANGELOG", [this](bool ok, QByteArray data) {
|
|
||||||
if (!ok) {
|
|
||||||
m_changelogText.clear();
|
|
||||||
} else {
|
|
||||||
m_changelogText = QString::fromUtf8(data);
|
|
||||||
}
|
|
||||||
fetchReleaseDate();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateController::fetchReleaseDate()
|
|
||||||
{
|
|
||||||
doGetAsync("/RELEASE_DATE", [this](bool ok, QByteArray data) {
|
|
||||||
if (ok) {
|
|
||||||
m_releaseDate = QString::fromUtf8(data).trimmed();
|
|
||||||
} else {
|
|
||||||
m_releaseDate = QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_downloadUrl = composeDownloadUrl();
|
|
||||||
emit updateFound();
|
|
||||||
finishUpdateCheck();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UpdateController::isNewVersionAvailable() const
|
|
||||||
{
|
|
||||||
auto currentVersion = QVersionNumber::fromString(QString(APP_VERSION));
|
|
||||||
auto newVersion = QVersionNumber::fromString(m_version);
|
|
||||||
return newVersion > currentVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateController::setupNetworkErrorHandling(QNetworkReply* reply, const QString& operation)
|
|
||||||
{
|
|
||||||
QObject::connect(reply, &QNetworkReply::errorOccurred, [reply, operation](QNetworkReply::NetworkError error) {
|
|
||||||
logger.error() << QString("Network error occurred while fetching %1: %2 %3")
|
|
||||||
.arg(operation, reply->errorString(), QString::number(error));
|
|
||||||
});
|
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::sslErrors, [operation](const QList<QSslError> &errors) {
|
|
||||||
QStringList errorStrings;
|
|
||||||
for (const QSslError &err : errors) {
|
|
||||||
errorStrings << err.errorString();
|
|
||||||
}
|
|
||||||
logger.error() << QString("SSL errors while fetching %1: %2").arg(operation, errorStrings.join("; "));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateController::handleNetworkError(QNetworkReply* reply, const QString& operation)
|
|
||||||
{
|
|
||||||
if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
|
|
||||||
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
|
|
||||||
logger.error() << errorString(ErrorCode::ApiConfigTimeoutError);
|
|
||||||
} else {
|
|
||||||
QString err = reply->errorString();
|
|
||||||
logger.error() << "Network error code:" << QString::number(static_cast<int>(reply->error()));
|
|
||||||
logger.error() << "Error message:" << err;
|
|
||||||
logger.error() << "HTTP status:" << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
||||||
logger.error() << errorString(ErrorCode::ApiConfigDownloadError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString UpdateController::composeDownloadUrl() const
|
|
||||||
{
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
|
||||||
const QString fileName = QString(kInstallerRemoteFileNamePattern).arg(m_version);
|
|
||||||
return m_baseUrl + "/" + fileName;
|
|
||||||
#else
|
|
||||||
return QString();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateController::runInstaller()
|
|
||||||
{
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
|
||||||
if (m_downloadUrl.isEmpty()) {
|
|
||||||
logger.error() << "Download URL is empty";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QNetworkRequest request;
|
|
||||||
request.setTransferTimeout(30000);
|
|
||||||
request.setUrl(m_downloadUrl);
|
|
||||||
|
|
||||||
QNetworkReply *reply = amnApp->networkManager()->get(request);
|
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, [this, reply]() {
|
|
||||||
if (reply->error() == QNetworkReply::NoError) {
|
|
||||||
QFile file(kInstallerLocalPath);
|
|
||||||
if (!file.open(QIODevice::WriteOnly)) {
|
|
||||||
logger.error() << "Failed to open installer file for writing:" << kInstallerLocalPath << "Error:" << file.errorString();
|
|
||||||
reply->deleteLater();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file.write(reply->readAll()) == -1) {
|
|
||||||
logger.error() << "Failed to write installer data to file:" << kInstallerLocalPath << "Error:" << file.errorString();
|
|
||||||
file.close();
|
|
||||||
reply->deleteLater();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
#if defined(Q_OS_WINDOWS)
|
|
||||||
runWindowsInstaller(kInstallerLocalPath);
|
|
||||||
#elif defined(Q_OS_MACOS)
|
|
||||||
runMacInstaller(kInstallerLocalPath);
|
|
||||||
#elif defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
|
||||||
runLinuxInstaller(kInstallerLocalPath);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
|
|
||||||
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
|
|
||||||
logger.error() << errorString(ErrorCode::ApiConfigTimeoutError);
|
|
||||||
} else {
|
|
||||||
QString err = reply->errorString();
|
|
||||||
logger.error() << QString::fromUtf8(reply->readAll());
|
|
||||||
logger.error() << "Network error code:" << QString::number(static_cast<int>(reply->error()));
|
|
||||||
logger.error() << "Error message:" << err;
|
|
||||||
logger.error() << "HTTP status:" << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
||||||
logger.error() << errorString(ErrorCode::ApiConfigDownloadError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reply->deleteLater();
|
|
||||||
});
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(Q_OS_WINDOWS)
|
|
||||||
int UpdateController::runWindowsInstaller(const QString &installerPath)
|
|
||||||
{
|
|
||||||
qint64 pid;
|
|
||||||
bool success = QProcess::startDetached(installerPath, QStringList(), QString(), &pid);
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
logger.info() << "Installation process started with PID:" << pid;
|
|
||||||
} else {
|
|
||||||
logger.error() << "Failed to start installation process";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(Q_OS_MACOS)
|
|
||||||
int UpdateController::runMacInstaller(const QString &installerPath)
|
|
||||||
{
|
|
||||||
// Create temporary directory for extraction
|
|
||||||
QTemporaryDir extractDir;
|
|
||||||
extractDir.setAutoRemove(false);
|
|
||||||
if (!extractDir.isValid()) {
|
|
||||||
logger.error() << "Failed to create temporary directory";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
logger.info() << "Temporary directory created:" << extractDir.path();
|
|
||||||
|
|
||||||
// Create script file in the temporary directory
|
|
||||||
QString scriptPath = extractDir.path() + "/mac_installer.sh";
|
|
||||||
QFile scriptFile(scriptPath);
|
|
||||||
if (!scriptFile.open(QIODevice::WriteOnly)) {
|
|
||||||
logger.error() << "Failed to create script file";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get script content from registry
|
|
||||||
QString scriptContent = amnezia::scriptData(amnezia::ClientScriptType::mac_installer);
|
|
||||||
if (scriptContent.isEmpty()) {
|
|
||||||
logger.error() << "macOS installer script content is empty";
|
|
||||||
scriptFile.close();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
scriptFile.write(scriptContent.toUtf8());
|
|
||||||
scriptFile.close();
|
|
||||||
logger.info() << "Script file created:" << scriptPath;
|
|
||||||
|
|
||||||
// Make script executable
|
|
||||||
QFile::setPermissions(scriptPath, QFile::permissions(scriptPath) | QFile::ExeUser);
|
|
||||||
|
|
||||||
// Start detached process
|
|
||||||
qint64 pid;
|
|
||||||
bool success =
|
|
||||||
QProcess::startDetached("/bin/bash", QStringList() << scriptPath << extractDir.path() << installerPath, extractDir.path(), &pid);
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
logger.info() << "Installation process started with PID:" << pid;
|
|
||||||
} else {
|
|
||||||
logger.error() << "Failed to start installation process";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
|
||||||
int UpdateController::runLinuxInstaller(const QString &installerPath)
|
|
||||||
{
|
|
||||||
QFile::setPermissions(installerPath, QFile::permissions(installerPath) | QFile::ExeUser);
|
|
||||||
|
|
||||||
qint64 pid;
|
|
||||||
bool success = QProcess::startDetached(installerPath, QStringList(), QString(), &pid);
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
logger.info() << "Installation process started with PID:" << pid;
|
|
||||||
} else {
|
|
||||||
logger.error() << "Failed to start installation process";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
#ifndef UPDATECONTROLLER_H
|
|
||||||
#define UPDATECONTROLLER_H
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <QObject>
|
|
||||||
#include <QNetworkReply>
|
|
||||||
|
|
||||||
#include "core/repositories/secureAppSettingsRepository.h"
|
|
||||||
|
|
||||||
class UpdateController : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit UpdateController(SecureAppSettingsRepository* appSettingsRepository, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
QString getRawChangelogText() const;
|
|
||||||
QString getReleaseDate() const;
|
|
||||||
QString getVersion() const;
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void checkForUpdates();
|
|
||||||
void runInstaller();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void updateFound();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void finishUpdateCheck();
|
|
||||||
void fetchGatewayUrl();
|
|
||||||
void fetchVersionInfo();
|
|
||||||
void fetchChangelog();
|
|
||||||
void fetchReleaseDate();
|
|
||||||
void doGetAsync(const QString &endpoint, std::function<void(bool, QByteArray)> onDone);
|
|
||||||
bool isNewVersionAvailable() const;
|
|
||||||
void setupNetworkErrorHandling(QNetworkReply* reply, const QString& operation);
|
|
||||||
void handleNetworkError(QNetworkReply* reply, const QString& operation);
|
|
||||||
QString composeDownloadUrl() const;
|
|
||||||
|
|
||||||
SecureAppSettingsRepository* m_appSettingsRepository;
|
|
||||||
|
|
||||||
QString m_baseUrl;
|
|
||||||
QString m_changelogText;
|
|
||||||
QString m_version;
|
|
||||||
QString m_releaseDate;
|
|
||||||
QString m_downloadUrl;
|
|
||||||
bool m_updateCheckRunning = false;
|
|
||||||
|
|
||||||
#if defined(Q_OS_WINDOWS)
|
|
||||||
int runWindowsInstaller(const QString &installerPath);
|
|
||||||
#elif defined(Q_OS_MACOS)
|
|
||||||
int runMacInstaller(const QString &installerPath);
|
|
||||||
#elif defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
|
||||||
int runLinuxInstaller(const QString &installerPath);
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // UPDATECONTROLLER_H
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#ifndef CONTAINERDIAGNOSTICS_H
|
|
||||||
#define CONTAINERDIAGNOSTICS_H
|
|
||||||
|
|
||||||
namespace amnezia
|
|
||||||
{
|
|
||||||
struct ContainerDiagnostics
|
|
||||||
{
|
|
||||||
bool available = false;
|
|
||||||
bool portReachable = false;
|
|
||||||
|
|
||||||
virtual ~ContainerDiagnostics() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace amnezia
|
|
||||||
|
|
||||||
#endif // CONTAINERDIAGNOSTICS_H
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#ifndef MTPROXYDIAGNOSTICS_H
|
|
||||||
#define MTPROXYDIAGNOSTICS_H
|
|
||||||
|
|
||||||
#include "containerDiagnostics.h"
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
namespace amnezia {
|
|
||||||
struct MtProxyDiagnostics : ContainerDiagnostics {
|
|
||||||
bool upstreamReachable = false;
|
|
||||||
int clientsConnected = -1;
|
|
||||||
QString lastConfigRefresh;
|
|
||||||
QString statsEndpoint;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace amnezia
|
|
||||||
|
|
||||||
#endif // MTPROXYDIAGNOSTICS_H
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#ifndef TELEMTDIAGNOSTICS_H
|
|
||||||
#define TELEMTDIAGNOSTICS_H
|
|
||||||
|
|
||||||
#include "containerDiagnostics.h"
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
namespace amnezia
|
|
||||||
{
|
|
||||||
struct TelemtDiagnostics : ContainerDiagnostics
|
|
||||||
{
|
|
||||||
bool upstreamReachable = false;
|
|
||||||
int clientsConnected = -1;
|
|
||||||
QString lastConfigRefresh;
|
|
||||||
QString statsEndpoint;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace amnezia
|
|
||||||
|
|
||||||
#endif // TELEMTDIAGNOSTICS_H
|
|
||||||
@@ -14,8 +14,6 @@
|
|||||||
#include "core/models/protocols/xrayProtocolConfig.h"
|
#include "core/models/protocols/xrayProtocolConfig.h"
|
||||||
#include "core/models/protocols/sftpProtocolConfig.h"
|
#include "core/models/protocols/sftpProtocolConfig.h"
|
||||||
#include "core/models/protocols/socks5ProxyProtocolConfig.h"
|
#include "core/models/protocols/socks5ProxyProtocolConfig.h"
|
||||||
#include "core/models/protocols/mtProxyProtocolConfig.h"
|
|
||||||
#include "core/models/protocols/telemtProtocolConfig.h"
|
|
||||||
#include "core/models/protocols/ikev2ProtocolConfig.h"
|
#include "core/models/protocols/ikev2ProtocolConfig.h"
|
||||||
#include "core/models/protocols/torProtocolConfig.h"
|
#include "core/models/protocols/torProtocolConfig.h"
|
||||||
|
|
||||||
@@ -93,18 +91,6 @@ ContainerConfig InstallerBase::createBaseConfig(DockerContainer container, int p
|
|||||||
config.protocolConfig = socks5Config;
|
config.protocolConfig = socks5Config;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Proto::MtProxy: {
|
|
||||||
MtProxyProtocolConfig mtConfig;
|
|
||||||
mtConfig.port = portStr;
|
|
||||||
config.protocolConfig = mtConfig;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Proto::Telemt: {
|
|
||||||
TelemtProtocolConfig telemtConfig;
|
|
||||||
telemtConfig.port = portStr;
|
|
||||||
config.protocolConfig = telemtConfig;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Proto::Ikev2: {
|
case Proto::Ikev2: {
|
||||||
Ikev2ProtocolConfig ikev2Config;
|
Ikev2ProtocolConfig ikev2Config;
|
||||||
config.protocolConfig = ikev2Config;
|
config.protocolConfig = ikev2Config;
|
||||||
|
|||||||
@@ -1,130 +0,0 @@
|
|||||||
#include "mtProxyInstaller.h"
|
|
||||||
|
|
||||||
#include "core/utils/containerEnum.h"
|
|
||||||
#include "core/utils/containers/containerUtils.h"
|
|
||||||
#include "core/utils/protocolEnum.h"
|
|
||||||
#include "core/utils/selfhosted/sshSession.h"
|
|
||||||
#include "core/models/containerConfig.h"
|
|
||||||
#include "core/models/protocols/mtProxyProtocolConfig.h"
|
|
||||||
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QJsonParseError>
|
|
||||||
#include <QRegularExpression>
|
|
||||||
|
|
||||||
#include <QtGlobal>
|
|
||||||
|
|
||||||
using namespace amnezia;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
constexpr QLatin1String kMtProxyClientJsonPath("/data/amnezia-mtproxy-client.json");
|
|
||||||
constexpr QLatin1String kMtProxyClientJsonUploadPath("data/amnezia-mtproxy-client.json");
|
|
||||||
constexpr QLatin1String kMtProxySecretPath("/data/secret");
|
|
||||||
}
|
|
||||||
|
|
||||||
MtProxyInstaller::MtProxyInstaller(QObject *parent)
|
|
||||||
: InstallerBase(parent) {
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode MtProxyInstaller::extractConfigFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
|
||||||
SshSession *sshSession, ContainerConfig &config) {
|
|
||||||
if (container != DockerContainer::MtProxy || !sshSession) {
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
MtProxyProtocolConfig *mt = config.getMtProxyProtocolConfig();
|
|
||||||
if (!mt) {
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode jsonErr = ErrorCode::NoError;
|
|
||||||
const QByteArray jsonRaw =
|
|
||||||
sshSession->getTextFileFromContainer(container, credentials, QString(kMtProxyClientJsonPath), jsonErr);
|
|
||||||
if (jsonErr == ErrorCode::NoError && !jsonRaw.trimmed().isEmpty()) {
|
|
||||||
QJsonParseError parseError;
|
|
||||||
const QJsonDocument doc = QJsonDocument::fromJson(jsonRaw.trimmed(), &parseError);
|
|
||||||
if (parseError.error == QJsonParseError::NoError && doc.isObject()) {
|
|
||||||
QJsonObject merged = mt->toJson();
|
|
||||||
const QJsonObject snap = doc.object();
|
|
||||||
for (auto it = snap.constBegin(); it != snap.constEnd(); ++it) {
|
|
||||||
merged.insert(it.key(), it.value());
|
|
||||||
}
|
|
||||||
*mt = MtProxyProtocolConfig::fromJson(merged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode secretErr = ErrorCode::NoError;
|
|
||||||
const QByteArray secretRaw =
|
|
||||||
sshSession->getTextFileFromContainer(container, credentials, QString(kMtProxySecretPath), secretErr);
|
|
||||||
const QString sec = QString::fromUtf8(secretRaw).trimmed();
|
|
||||||
if (sec.length() == 32) {
|
|
||||||
static const QRegularExpression hex32(QStringLiteral("^[0-9a-fA-F]{32}$"));
|
|
||||||
if (hex32.match(sec).hasMatch()) {
|
|
||||||
mt->secret = sec;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode MtProxyInstaller::queryDiagnostics(SshSession &sshSession, const ServerCredentials &credentials,
|
|
||||||
DockerContainer container, int listenPort,
|
|
||||||
MtProxyContainerDiagnostics &out)
|
|
||||||
{
|
|
||||||
out = {};
|
|
||||||
if (container != DockerContainer::MtProxy && container != DockerContainer::Telemt) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
const QString containerName = ContainerUtils::containerToString(container);
|
|
||||||
const QString script =
|
|
||||||
QStringLiteral(
|
|
||||||
"PORT_OK=$(sudo docker exec %1 sh -c 'ss -tlnp 2>/dev/null | grep -q :%2 && echo yes || echo no' 2>/dev/null || echo no); "
|
|
||||||
"TG_OK=$(curl -s --max-time 5 -o /dev/null -w '%%{http_code}' https://core.telegram.org/getProxySecret 2>/dev/null | grep -q '200' && echo yes || echo no); "
|
|
||||||
"CLIENTS=$(sudo docker exec amnezia-mtproxy sh -c 'curl -s --max-time 3 http://localhost:2398/stats 2>/dev/null | grep -o \"total_special_connections:[0-9]*\" | cut -d: -f2' 2>/dev/null); "
|
|
||||||
"CONF_TIME=$(sudo docker exec amnezia-mtproxy sh -c 'stat -c \"%%y\" /data/proxy-multi.conf 2>/dev/null | cut -d. -f1' 2>/dev/null || echo unknown); "
|
|
||||||
"echo \"PORT_OK=${PORT_OK}\"; "
|
|
||||||
"echo \"TG_OK=${TG_OK}\"; "
|
|
||||||
"echo \"CLIENTS=${CLIENTS:-0}\"; "
|
|
||||||
"echo \"CONF_TIME=${CONF_TIME}\"; "
|
|
||||||
"echo \"STATS=http://localhost:2398/stats\";")
|
|
||||||
.arg(containerName)
|
|
||||||
.arg(listenPort);
|
|
||||||
|
|
||||||
QString stdOut;
|
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data;
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
const ErrorCode errorCode = sshSession.runScript(credentials, script, cbReadStdOut);
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
for (const QString &line : stdOut.split('\n', Qt::SkipEmptyParts)) {
|
|
||||||
if (line.startsWith(QLatin1String("PORT_OK="))) {
|
|
||||||
out.portReachable = line.mid(8).trimmed() == QLatin1String("yes");
|
|
||||||
} else if (line.startsWith(QLatin1String("TG_OK="))) {
|
|
||||||
out.upstreamReachable = line.mid(6).trimmed() == QLatin1String("yes");
|
|
||||||
} else if (line.startsWith(QLatin1String("CLIENTS="))) {
|
|
||||||
out.clientsConnected = line.mid(8).trimmed().toInt();
|
|
||||||
} else if (line.startsWith(QLatin1String("CONF_TIME="))) {
|
|
||||||
out.lastConfigRefresh = line.mid(10).trimmed();
|
|
||||||
} else if (line.startsWith(QLatin1String("STATS="))) {
|
|
||||||
out.statsEndpoint = line.mid(6).trimmed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MtProxyInstaller::uploadClientSettingsSnapshot(SshSession &sshSession, const ServerCredentials &credentials,
|
|
||||||
DockerContainer container, const ContainerConfig &config) {
|
|
||||||
const MtProxyProtocolConfig *mt = config.getMtProxyProtocolConfig();
|
|
||||||
if (!mt) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const QByteArray payload = QJsonDocument(mt->toJson()).toJson(QJsonDocument::Compact);
|
|
||||||
const ErrorCode err = sshSession.uploadTextFileToContainer(container, credentials, QString::fromUtf8(payload),
|
|
||||||
QString(kMtProxyClientJsonUploadPath));
|
|
||||||
if (err != ErrorCode::NoError) {
|
|
||||||
qWarning() << "MtProxyInstaller::uploadClientSettingsSnapshot failed" << err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#ifndef MTPROXYINSTALLER_H
|
|
||||||
#define MTPROXYINSTALLER_H
|
|
||||||
|
|
||||||
#include "installerBase.h"
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
struct MtProxyContainerDiagnostics {
|
|
||||||
bool portReachable = false;
|
|
||||||
bool upstreamReachable = false;
|
|
||||||
int clientsConnected = -1;
|
|
||||||
QString lastConfigRefresh;
|
|
||||||
QString statsEndpoint;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MtProxyInstaller : public InstallerBase {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit MtProxyInstaller(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
amnezia::ErrorCode
|
|
||||||
extractConfigFromContainer(amnezia::DockerContainer container, const amnezia::ServerCredentials &credentials,
|
|
||||||
SshSession *sshSession, amnezia::ContainerConfig &config) override;
|
|
||||||
|
|
||||||
static void uploadClientSettingsSnapshot(SshSession &sshSession, const amnezia::ServerCredentials &credentials,
|
|
||||||
amnezia::DockerContainer container,
|
|
||||||
const amnezia::ContainerConfig &config);
|
|
||||||
|
|
||||||
static amnezia::ErrorCode queryDiagnostics(SshSession &sshSession, const amnezia::ServerCredentials &credentials,
|
|
||||||
amnezia::DockerContainer container, int listenPort,
|
|
||||||
MtProxyContainerDiagnostics &out);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // MTPROXYINSTALLER_H
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
#include "telemtInstaller.h"
|
|
||||||
|
|
||||||
#include "core/utils/containerEnum.h"
|
|
||||||
#include "core/utils/containers/containerUtils.h"
|
|
||||||
#include "core/utils/selfhosted/sshSession.h"
|
|
||||||
#include "core/models/containerConfig.h"
|
|
||||||
#include "core/models/protocols/telemtProtocolConfig.h"
|
|
||||||
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QJsonParseError>
|
|
||||||
#include <QRegularExpression>
|
|
||||||
|
|
||||||
#include <QtGlobal>
|
|
||||||
|
|
||||||
using namespace amnezia;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
constexpr QLatin1String kTelemtClientJsonPath("/data/amnezia-telemt-client.json");
|
|
||||||
constexpr QLatin1String kTelemtClientJsonUploadPath("data/amnezia-telemt-client.json");
|
|
||||||
constexpr QLatin1String kTelemtSecretPath("/data/secret");
|
|
||||||
}
|
|
||||||
|
|
||||||
TelemtInstaller::TelemtInstaller(QObject *parent) : InstallerBase(parent) {}
|
|
||||||
|
|
||||||
ErrorCode TelemtInstaller::extractConfigFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
|
||||||
SshSession *sshSession, ContainerConfig &config) {
|
|
||||||
if (container != DockerContainer::Telemt || !sshSession) {
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
TelemtProtocolConfig *tc = config.getTelemtProtocolConfig();
|
|
||||||
if (!tc) {
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode jsonErr = ErrorCode::NoError;
|
|
||||||
const QByteArray jsonRaw =
|
|
||||||
sshSession->getTextFileFromContainer(container, credentials, QString(kTelemtClientJsonPath), jsonErr);
|
|
||||||
if (jsonErr == ErrorCode::NoError && !jsonRaw.trimmed().isEmpty()) {
|
|
||||||
QJsonParseError parseError;
|
|
||||||
const QJsonDocument doc = QJsonDocument::fromJson(jsonRaw.trimmed(), &parseError);
|
|
||||||
if (parseError.error == QJsonParseError::NoError && doc.isObject()) {
|
|
||||||
QJsonObject merged = tc->toJson();
|
|
||||||
const QJsonObject snap = doc.object();
|
|
||||||
for (auto it = snap.constBegin(); it != snap.constEnd(); ++it) {
|
|
||||||
merged.insert(it.key(), it.value());
|
|
||||||
}
|
|
||||||
*tc = TelemtProtocolConfig::fromJson(merged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode secretErr = ErrorCode::NoError;
|
|
||||||
const QByteArray secretRaw =
|
|
||||||
sshSession->getTextFileFromContainer(container, credentials, QString(kTelemtSecretPath), secretErr);
|
|
||||||
const QString sec = QString::fromUtf8(secretRaw).trimmed();
|
|
||||||
if (sec.length() == 32) {
|
|
||||||
static const QRegularExpression hex32(QStringLiteral("^[0-9a-fA-F]{32}$"));
|
|
||||||
if (hex32.match(sec).hasMatch()) {
|
|
||||||
tc->secret = sec;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TelemtInstaller::uploadClientSettingsSnapshot(SshSession &sshSession, const ServerCredentials &credentials,
|
|
||||||
DockerContainer container, const ContainerConfig &config) {
|
|
||||||
const TelemtProtocolConfig *tc = config.getTelemtProtocolConfig();
|
|
||||||
if (!tc) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const QByteArray payload = QJsonDocument(tc->toJson()).toJson(QJsonDocument::Compact);
|
|
||||||
const ErrorCode err = sshSession.uploadTextFileToContainer(container, credentials, QString::fromUtf8(payload),
|
|
||||||
QString(kTelemtClientJsonUploadPath));
|
|
||||||
if (err != ErrorCode::NoError) {
|
|
||||||
qWarning() << "TelemtInstaller::uploadClientSettingsSnapshot failed" << err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#ifndef TELEMTINSTALLER_H
|
|
||||||
#define TELEMTINSTALLER_H
|
|
||||||
|
|
||||||
#include "installerBase.h"
|
|
||||||
|
|
||||||
class TelemtInstaller : public InstallerBase {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit TelemtInstaller(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
amnezia::ErrorCode
|
|
||||||
extractConfigFromContainer(amnezia::DockerContainer container, const amnezia::ServerCredentials &credentials,
|
|
||||||
SshSession *sshSession, amnezia::ContainerConfig &config) override;
|
|
||||||
|
|
||||||
static void uploadClientSettingsSnapshot(SshSession &sshSession, const amnezia::ServerCredentials &credentials,
|
|
||||||
amnezia::DockerContainer container,
|
|
||||||
const amnezia::ContainerConfig &config);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // TELEMTINSTALLER_H
|
|
||||||
@@ -14,18 +14,8 @@
|
|||||||
#include "core/models/protocols/xrayProtocolConfig.h"
|
#include "core/models/protocols/xrayProtocolConfig.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
|
||||||
namespace
|
namespace {
|
||||||
{
|
|
||||||
Logger logger("XrayInstaller");
|
Logger logger("XrayInstaller");
|
||||||
|
|
||||||
// Xray expects uTLS preset names (chrome, firefox, …). Old Amnezia/server templates used "Mozilla/5.0".
|
|
||||||
QString normalizeXrayFingerprint(const QString &fp)
|
|
||||||
{
|
|
||||||
if (fp.isEmpty() || fp.contains(QLatin1String("Mozilla/5.0"), Qt::CaseInsensitive)) {
|
|
||||||
return QString::fromLatin1(protocols::xray::defaultFingerprint);
|
|
||||||
}
|
|
||||||
return fp;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
@@ -73,249 +63,16 @@ ErrorCode XrayInstaller::extractConfigFromContainer(DockerContainer container, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject streamSettings = inbound[protocols::xray::streamSettings].toObject();
|
QJsonObject streamSettings = inbound[protocols::xray::streamSettings].toObject();
|
||||||
auto *xrayConfig = config.getXrayProtocolConfig();
|
QJsonObject realitySettings = streamSettings[protocols::xray::realitySettings].toObject();
|
||||||
if (!xrayConfig) {
|
if (!realitySettings.contains(protocols::xray::serverNames)) {
|
||||||
logger.error() << "No XrayProtocolConfig in ContainerConfig";
|
logger.error() << "Settings missing 'serverNames' field";
|
||||||
return ErrorCode::InternalError;
|
return ErrorCode::InternalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
XrayServerConfig &srv = xrayConfig->serverConfig;
|
QString siteName = realitySettings[protocols::xray::serverNames][0].toString();
|
||||||
|
|
||||||
// ── Port ─────────────────────────────────────────────────────────
|
if (auto* xrayConfig = config.getXrayProtocolConfig()) {
|
||||||
if (inbound.contains(protocols::xray::port)) {
|
xrayConfig->serverConfig.site = siteName;
|
||||||
srv.port = QString::number(inbound[protocols::xray::port].toInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Network (transport) ───────────────────────────────────────────
|
|
||||||
QString networkVal = streamSettings.value(protocols::xray::network).toString("tcp");
|
|
||||||
if (networkVal == "xhttp") {
|
|
||||||
srv.transport = "xhttp";
|
|
||||||
} else if (networkVal == "kcp") {
|
|
||||||
srv.transport = "mkcp";
|
|
||||||
} else {
|
|
||||||
srv.transport = "raw";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Security ──────────────────────────────────────────────────────
|
|
||||||
srv.security = streamSettings.value(protocols::xray::security).toString("reality");
|
|
||||||
|
|
||||||
// ── Reality settings ──────────────────────────────────────────────
|
|
||||||
if (srv.security == "reality") {
|
|
||||||
QJsonObject rs = streamSettings.value(protocols::xray::realitySettings).toObject();
|
|
||||||
|
|
||||||
// serverNames array → site + sni
|
|
||||||
if (rs.contains(protocols::xray::serverNames)) {
|
|
||||||
QString sniVal = rs[protocols::xray::serverNames].toArray().first().toString();
|
|
||||||
srv.sni = sniVal;
|
|
||||||
srv.site = sniVal;
|
|
||||||
} else if (rs.contains(protocols::xray::serverName)) {
|
|
||||||
srv.sni = rs[protocols::xray::serverName].toString();
|
|
||||||
srv.site = srv.sni;
|
|
||||||
}
|
|
||||||
|
|
||||||
srv.fingerprint = normalizeXrayFingerprint(rs.value(protocols::xray::fingerprint).toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── TLS settings ──────────────────────────────────────────────────
|
|
||||||
if (srv.security == "tls") {
|
|
||||||
QJsonObject tls = streamSettings.value("tlsSettings").toObject();
|
|
||||||
srv.sni = tls.value(protocols::xray::serverName).toString();
|
|
||||||
srv.fingerprint = normalizeXrayFingerprint(tls.value(protocols::xray::fingerprint).toString());
|
|
||||||
|
|
||||||
QJsonArray alpnArr = tls.value("alpn").toArray();
|
|
||||||
QStringList alpnList;
|
|
||||||
for (const QJsonValue &v : alpnArr) {
|
|
||||||
alpnList << v.toString();
|
|
||||||
}
|
|
||||||
srv.alpn = alpnList.join(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Flow (from users array) ───────────────────────────────────────
|
|
||||||
if (inbound.contains(protocols::xray::settings)) {
|
|
||||||
QJsonObject s = inbound[protocols::xray::settings].toObject();
|
|
||||||
QJsonArray clientsArr = s.value(protocols::xray::clients).toArray();
|
|
||||||
if (!clientsArr.isEmpty()) {
|
|
||||||
srv.flow = clientsArr[0].toObject().value(protocols::xray::flow).toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── XHTTP settings (Xray-core SplitHTTPConfig + legacy Amnezia keys) ──
|
|
||||||
if (srv.transport == "xhttp") {
|
|
||||||
QJsonObject xhttpObj = streamSettings.value("xhttpSettings").toObject();
|
|
||||||
{
|
|
||||||
const QString m = xhttpObj.value("mode").toString();
|
|
||||||
if (m.isEmpty() || m == QLatin1String("auto"))
|
|
||||||
srv.xhttp.mode = QStringLiteral("Auto");
|
|
||||||
else if (m == QLatin1String("packet-up"))
|
|
||||||
srv.xhttp.mode = QStringLiteral("Packet-up");
|
|
||||||
else if (m == QLatin1String("stream-up"))
|
|
||||||
srv.xhttp.mode = QStringLiteral("Stream-up");
|
|
||||||
else if (m == QLatin1String("stream-one"))
|
|
||||||
srv.xhttp.mode = QStringLiteral("Stream-one");
|
|
||||||
else
|
|
||||||
srv.xhttp.mode = m;
|
|
||||||
}
|
|
||||||
|
|
||||||
srv.xhttp.host = xhttpObj.value("host").toString();
|
|
||||||
srv.xhttp.path = xhttpObj.value("path").toString();
|
|
||||||
|
|
||||||
{
|
|
||||||
const QJsonObject hdrs = xhttpObj.value("headers").toObject();
|
|
||||||
if (hdrs.contains(QLatin1String("Host")) || !hdrs.isEmpty())
|
|
||||||
srv.xhttp.headersTemplate = QStringLiteral("HTTP");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xhttpObj.contains(QLatin1String("uplinkHTTPMethod")))
|
|
||||||
srv.xhttp.uplinkMethod = xhttpObj.value("uplinkHTTPMethod").toString();
|
|
||||||
else
|
|
||||||
srv.xhttp.uplinkMethod = xhttpObj.value("method").toString();
|
|
||||||
|
|
||||||
srv.xhttp.disableGrpc = xhttpObj.value("noGRPCHeader").toBool(true);
|
|
||||||
srv.xhttp.disableSse = xhttpObj.value("noSSEHeader").toBool(true);
|
|
||||||
|
|
||||||
auto sessionSeqUi = [](const QString &core) -> QString {
|
|
||||||
if (core.isEmpty() || core == QLatin1String("path"))
|
|
||||||
return QStringLiteral("Path");
|
|
||||||
if (core == QLatin1String("cookie"))
|
|
||||||
return QStringLiteral("Cookie");
|
|
||||||
if (core == QLatin1String("header"))
|
|
||||||
return QStringLiteral("Header");
|
|
||||||
if (core == QLatin1String("query"))
|
|
||||||
return QStringLiteral("Query");
|
|
||||||
return core;
|
|
||||||
};
|
|
||||||
QString sess = xhttpObj.value("sessionPlacement").toString();
|
|
||||||
if (sess.isEmpty())
|
|
||||||
sess = xhttpObj.value("scSessionPlacement").toString();
|
|
||||||
srv.xhttp.sessionPlacement = sessionSeqUi(sess);
|
|
||||||
|
|
||||||
QString seq = xhttpObj.value("seqPlacement").toString();
|
|
||||||
if (seq.isEmpty())
|
|
||||||
seq = xhttpObj.value("scSeqPlacement").toString();
|
|
||||||
srv.xhttp.seqPlacement = sessionSeqUi(seq);
|
|
||||||
|
|
||||||
auto uplinkDataUi = [](const QString &core) -> QString {
|
|
||||||
if (core.isEmpty() || core == QLatin1String("body"))
|
|
||||||
return QStringLiteral("Body");
|
|
||||||
if (core == QLatin1String("auto"))
|
|
||||||
return QStringLiteral("Auto");
|
|
||||||
if (core == QLatin1String("header"))
|
|
||||||
return QStringLiteral("Header");
|
|
||||||
if (core == QLatin1String("cookie"))
|
|
||||||
return QStringLiteral("Cookie");
|
|
||||||
return core;
|
|
||||||
};
|
|
||||||
QString udata = xhttpObj.value("uplinkDataPlacement").toString();
|
|
||||||
if (udata.isEmpty())
|
|
||||||
udata = xhttpObj.value("scUplinkDataPlacement").toString();
|
|
||||||
srv.xhttp.uplinkDataPlacement = uplinkDataUi(udata);
|
|
||||||
|
|
||||||
srv.xhttp.sessionKey = xhttpObj.value("sessionKey").toString();
|
|
||||||
srv.xhttp.seqKey = xhttpObj.value("seqKey").toString();
|
|
||||||
srv.xhttp.uplinkDataKey = xhttpObj.value("uplinkDataKey").toString();
|
|
||||||
|
|
||||||
if (xhttpObj.contains(QLatin1String("uplinkChunkSize"))) {
|
|
||||||
QJsonObject uc = xhttpObj.value("uplinkChunkSize").toObject();
|
|
||||||
if (!uc.isEmpty())
|
|
||||||
srv.xhttp.uplinkChunkSize = QString::number(uc.value("from").toInt());
|
|
||||||
} else if (xhttpObj.contains(QLatin1String("xhttpUplinkChunkSize"))) {
|
|
||||||
srv.xhttp.uplinkChunkSize = QString::number(xhttpObj.value("xhttpUplinkChunkSize").toInt());
|
|
||||||
}
|
|
||||||
if (xhttpObj.contains(QLatin1String("scMaxBufferedPosts"))) {
|
|
||||||
srv.xhttp.scMaxBufferedPosts = QString::number(xhttpObj.value("scMaxBufferedPosts").toVariant().toLongLong());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto readRange = [&](const char *key, QString &minOut, QString &maxOut) {
|
|
||||||
QJsonObject r = xhttpObj.value(QLatin1String(key)).toObject();
|
|
||||||
if (!r.isEmpty()) {
|
|
||||||
minOut = QString::number(r.value("from").toInt());
|
|
||||||
maxOut = QString::number(r.value("to").toInt());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
readRange("scMaxEachPostBytes", srv.xhttp.scMaxEachPostBytesMin, srv.xhttp.scMaxEachPostBytesMax);
|
|
||||||
readRange("scMinPostsIntervalMs", srv.xhttp.scMinPostsIntervalMsMin, srv.xhttp.scMinPostsIntervalMsMax);
|
|
||||||
readRange("scStreamUpServerSecs", srv.xhttp.scStreamUpServerSecsMin, srv.xhttp.scStreamUpServerSecsMax);
|
|
||||||
|
|
||||||
auto loadPaddingFromObject = [&](const QJsonObject &pad) {
|
|
||||||
if (pad.contains(QLatin1String("xPaddingObfsMode")))
|
|
||||||
srv.xhttp.xPadding.obfsMode = pad.value("xPaddingObfsMode").toBool(true);
|
|
||||||
srv.xhttp.xPadding.key = pad.value("xPaddingKey").toString();
|
|
||||||
srv.xhttp.xPadding.header = pad.value("xPaddingHeader").toString();
|
|
||||||
srv.xhttp.xPadding.placement = pad.value("xPaddingPlacement").toString();
|
|
||||||
srv.xhttp.xPadding.method = pad.value("xPaddingMethod").toString();
|
|
||||||
QJsonObject bytesRange = pad.value("xPaddingBytes").toObject();
|
|
||||||
if (!bytesRange.isEmpty()) {
|
|
||||||
srv.xhttp.xPadding.bytesMin = QString::number(bytesRange.value("from").toInt());
|
|
||||||
srv.xhttp.xPadding.bytesMax = QString::number(bytesRange.value("to").toInt());
|
|
||||||
}
|
|
||||||
QString pl = srv.xhttp.xPadding.placement.toLower();
|
|
||||||
if (pl == QLatin1String("cookie"))
|
|
||||||
srv.xhttp.xPadding.placement = QStringLiteral("Cookie");
|
|
||||||
else if (pl == QLatin1String("header"))
|
|
||||||
srv.xhttp.xPadding.placement = QStringLiteral("Header");
|
|
||||||
else if (pl == QLatin1String("query"))
|
|
||||||
srv.xhttp.xPadding.placement = QStringLiteral("Query");
|
|
||||||
else if (pl == QLatin1String("queryinheader"))
|
|
||||||
srv.xhttp.xPadding.placement = QStringLiteral("Query in header");
|
|
||||||
QString met = srv.xhttp.xPadding.method.toLower();
|
|
||||||
if (met == QLatin1String("repeat-x"))
|
|
||||||
srv.xhttp.xPadding.method = QStringLiteral("Repeat-x");
|
|
||||||
else if (met == QLatin1String("tokenish"))
|
|
||||||
srv.xhttp.xPadding.method = QStringLiteral("Tokenish");
|
|
||||||
};
|
|
||||||
if (xhttpObj.contains(QLatin1String("xPaddingObfsMode")) || xhttpObj.contains(QLatin1String("xPaddingKey"))
|
|
||||||
|| !xhttpObj.value("xPaddingBytes").toObject().isEmpty()) {
|
|
||||||
loadPaddingFromObject(xhttpObj);
|
|
||||||
} else if (xhttpObj.contains(QLatin1String("xPadding")) && xhttpObj.value("xPadding").isObject()) {
|
|
||||||
const QJsonObject nested = xhttpObj.value("xPadding").toObject();
|
|
||||||
if (!nested.isEmpty()) {
|
|
||||||
loadPaddingFromObject(nested);
|
|
||||||
if (!nested.contains(QLatin1String("xPaddingObfsMode")))
|
|
||||||
srv.xhttp.xPadding.obfsMode = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xhttpObj.contains(QLatin1String("xmux"))) {
|
|
||||||
QJsonObject mux = xhttpObj.value("xmux").toObject();
|
|
||||||
srv.xhttp.xmux.enabled = true;
|
|
||||||
|
|
||||||
auto readMuxRange = [&](const char *key, QString &minOut, QString &maxOut) {
|
|
||||||
QJsonObject r = mux.value(QLatin1String(key)).toObject();
|
|
||||||
if (!r.isEmpty()) {
|
|
||||||
minOut = QString::number(r.value("from").toInt());
|
|
||||||
maxOut = QString::number(r.value("to").toInt());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
readMuxRange("maxConcurrency", srv.xhttp.xmux.maxConcurrencyMin, srv.xhttp.xmux.maxConcurrencyMax);
|
|
||||||
readMuxRange("maxConnections", srv.xhttp.xmux.maxConnectionsMin, srv.xhttp.xmux.maxConnectionsMax);
|
|
||||||
readMuxRange("cMaxReuseTimes", srv.xhttp.xmux.cMaxReuseTimesMin, srv.xhttp.xmux.cMaxReuseTimesMax);
|
|
||||||
readMuxRange("hMaxRequestTimes", srv.xhttp.xmux.hMaxRequestTimesMin, srv.xhttp.xmux.hMaxRequestTimesMax);
|
|
||||||
readMuxRange("hMaxReusableSecs", srv.xhttp.xmux.hMaxReusableSecsMin, srv.xhttp.xmux.hMaxReusableSecsMax);
|
|
||||||
|
|
||||||
if (mux.contains(QLatin1String("hKeepAlivePeriod")))
|
|
||||||
srv.xhttp.xmux.hKeepAlivePeriod = QString::number(mux.value("hKeepAlivePeriod").toVariant().toLongLong());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── mKCP settings ─────────────────────────────────────────────────
|
|
||||||
if (srv.transport == "mkcp") {
|
|
||||||
QJsonObject kcp = streamSettings.value("kcpSettings").toObject();
|
|
||||||
if (kcp.contains("tti")) {
|
|
||||||
srv.mkcp.tti = QString::number(kcp["tti"].toInt());
|
|
||||||
}
|
|
||||||
if (kcp.contains("uplinkCapacity")) {
|
|
||||||
srv.mkcp.uplinkCapacity = QString::number(kcp["uplinkCapacity"].toInt());
|
|
||||||
}
|
|
||||||
if (kcp.contains("downlinkCapacity")) {
|
|
||||||
srv.mkcp.downlinkCapacity = QString::number(kcp["downlinkCapacity"].toInt());
|
|
||||||
}
|
|
||||||
if (kcp.contains("readBufferSize")) {
|
|
||||||
srv.mkcp.readBufferSize = QString::number(kcp["readBufferSize"].toInt());
|
|
||||||
}
|
|
||||||
if (kcp.contains("writeBufferSize")) {
|
|
||||||
srv.mkcp.writeBufferSize = QString::number(kcp["writeBufferSize"].toInt());
|
|
||||||
}
|
|
||||||
srv.mkcp.congestion = kcp.value("congestion").toBool(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
||||||
#include "core/utils/serverConfigUtils.h"
|
#include "core/utils/api/apiEnums.h"
|
||||||
#include "core/utils/constants/apiKeys.h"
|
#include "core/utils/constants/apiKeys.h"
|
||||||
#include "core/utils/constants/apiConstants.h"
|
#include "core/utils/constants/apiConstants.h"
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ struct ApiConfig
|
|||||||
|
|
||||||
QString stackType;
|
QString stackType;
|
||||||
QString cliVersion;
|
QString cliVersion;
|
||||||
bool isTestPurchase = false;
|
bool isTestPurchase;
|
||||||
bool isInAppPurchase = false;
|
bool isInAppPurchase = false;
|
||||||
bool subscriptionExpiredByServer = false;
|
bool subscriptionExpiredByServer = false;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
#include "apiV1ServerConfig.h"
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/apiKeys.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/utils/api/apiUtils.h"
|
||||||
|
|
||||||
|
namespace amnezia
|
||||||
|
{
|
||||||
|
|
||||||
|
using namespace ContainerEnumNS;
|
||||||
|
|
||||||
|
bool ApiV1ServerConfig::isPremium() const
|
||||||
|
{
|
||||||
|
constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT);
|
||||||
|
return apiEndpoint.contains(premiumV1Endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiV1ServerConfig::isFree() const
|
||||||
|
{
|
||||||
|
constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT);
|
||||||
|
return apiEndpoint.contains(freeV2Endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ApiV1ServerConfig::vpnKey() const
|
||||||
|
{
|
||||||
|
QJsonObject json = toJson();
|
||||||
|
return apiUtils::getPremiumV1VpnKey(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiV1ServerConfig::hasContainers() const
|
||||||
|
{
|
||||||
|
return !containers.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainerConfig ApiV1ServerConfig::containerConfig(DockerContainer container) const
|
||||||
|
{
|
||||||
|
if (!containers.contains(container)) {
|
||||||
|
return ContainerConfig{};
|
||||||
|
}
|
||||||
|
return containers.value(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ApiV1ServerConfig::toJson() const
|
||||||
|
{
|
||||||
|
QJsonObject obj;
|
||||||
|
|
||||||
|
if (!name.isEmpty()) {
|
||||||
|
obj[configKey::name] = name;
|
||||||
|
}
|
||||||
|
if (!description.isEmpty()) {
|
||||||
|
obj[configKey::description] = description;
|
||||||
|
}
|
||||||
|
if (!protocol.isEmpty()) {
|
||||||
|
obj[apiDefs::key::protocol] = protocol;
|
||||||
|
}
|
||||||
|
if (!apiEndpoint.isEmpty()) {
|
||||||
|
obj[apiDefs::key::apiEndpoint] = apiEndpoint;
|
||||||
|
}
|
||||||
|
if (!apiKey.isEmpty()) {
|
||||||
|
obj[apiDefs::key::apiKey] = apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj[configKey::configVersion] = configVersion;
|
||||||
|
|
||||||
|
if (!hostName.isEmpty()) {
|
||||||
|
obj[configKey::hostName] = hostName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray containersArray;
|
||||||
|
for (auto it = containers.begin(); it != containers.end(); ++it) {
|
||||||
|
QJsonObject containerObj = it.value().toJson();
|
||||||
|
containersArray.append(containerObj);
|
||||||
|
}
|
||||||
|
if (!containersArray.isEmpty()) {
|
||||||
|
obj[configKey::containers] = containersArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultContainer != DockerContainer::None) {
|
||||||
|
obj[configKey::defaultContainer] = ContainerUtils::containerToString(defaultContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dns1.isEmpty()) {
|
||||||
|
obj[configKey::dns1] = dns1;
|
||||||
|
}
|
||||||
|
if (!dns2.isEmpty()) {
|
||||||
|
obj[configKey::dns2] = dns2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crc > 0) {
|
||||||
|
obj[configKey::crc] = crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiV1ServerConfig ApiV1ServerConfig::fromJson(const QJsonObject& json)
|
||||||
|
{
|
||||||
|
ApiV1ServerConfig config;
|
||||||
|
|
||||||
|
config.name = json.value(configKey::name).toString();
|
||||||
|
config.description = json.value(configKey::description).toString();
|
||||||
|
config.protocol = json.value(apiDefs::key::protocol).toString();
|
||||||
|
config.apiEndpoint = json.value(apiDefs::key::apiEndpoint).toString();
|
||||||
|
config.apiKey = json.value(apiDefs::key::apiKey).toString();
|
||||||
|
config.configVersion = json.value(configKey::configVersion).toInt(1);
|
||||||
|
config.hostName = json.value(configKey::hostName).toString();
|
||||||
|
|
||||||
|
QJsonArray containersArray = json.value(configKey::containers).toArray();
|
||||||
|
for (const QJsonValue& val : containersArray) {
|
||||||
|
QJsonObject containerObj = val.toObject();
|
||||||
|
ContainerConfig containerConfig = ContainerConfig::fromJson(containerObj);
|
||||||
|
|
||||||
|
QString containerStr = containerObj.value(configKey::container).toString();
|
||||||
|
DockerContainer container = ContainerUtils::containerFromString(containerStr);
|
||||||
|
|
||||||
|
config.containers.insert(container, containerConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString defaultContainerStr = json.value(configKey::defaultContainer).toString();
|
||||||
|
config.defaultContainer = ContainerUtils::containerFromString(defaultContainerStr);
|
||||||
|
|
||||||
|
config.dns1 = json.value(configKey::dns1).toString();
|
||||||
|
config.dns2 = json.value(configKey::dns2).toString();
|
||||||
|
|
||||||
|
config.crc = json.value(configKey::crc).toInt(0);
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace amnezia
|
||||||
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#ifndef APIV1SERVERCONFIG_H
|
||||||
|
#define APIV1SERVERCONFIG_H
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/models/containerConfig.h"
|
||||||
|
#include "core/utils/api/apiEnums.h"
|
||||||
|
#include "core/utils/constants/apiKeys.h"
|
||||||
|
#include "core/utils/constants/apiConstants.h"
|
||||||
|
|
||||||
|
namespace amnezia
|
||||||
|
{
|
||||||
|
|
||||||
|
using namespace ContainerEnumNS;
|
||||||
|
|
||||||
|
struct ApiV1ServerConfig {
|
||||||
|
QString description;
|
||||||
|
QString hostName;
|
||||||
|
QMap<DockerContainer, ContainerConfig> containers;
|
||||||
|
DockerContainer defaultContainer;
|
||||||
|
QString dns1;
|
||||||
|
QString dns2;
|
||||||
|
|
||||||
|
QString name;
|
||||||
|
QString protocol;
|
||||||
|
QString apiEndpoint;
|
||||||
|
QString apiKey;
|
||||||
|
int crc;
|
||||||
|
int configVersion;
|
||||||
|
|
||||||
|
bool isPremium() const;
|
||||||
|
bool isFree() const;
|
||||||
|
QString vpnKey() const;
|
||||||
|
bool hasContainers() const;
|
||||||
|
ContainerConfig containerConfig(DockerContainer container) const;
|
||||||
|
QJsonObject toJson() const;
|
||||||
|
static ApiV1ServerConfig fromJson(const QJsonObject& json);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace amnezia
|
||||||
|
|
||||||
|
#endif // APIV1SERVERCONFIG_H
|
||||||
|
|
||||||
@@ -80,9 +80,6 @@ QJsonObject ApiV2ServerConfig::toJson() const
|
|||||||
if (!description.isEmpty()) {
|
if (!description.isEmpty()) {
|
||||||
obj[configKey::description] = description;
|
obj[configKey::description] = description;
|
||||||
}
|
}
|
||||||
if (!displayName.isEmpty()) {
|
|
||||||
obj[configKey::displayName] = displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
obj[configKey::configVersion] = configVersion;
|
obj[configKey::configVersion] = configVersion;
|
||||||
|
|
||||||
@@ -134,7 +131,6 @@ ApiV2ServerConfig ApiV2ServerConfig::fromJson(const QJsonObject& json)
|
|||||||
config.name = json.value(configKey::name).toString();
|
config.name = json.value(configKey::name).toString();
|
||||||
config.nameOverriddenByUser = json.value(configKey::nameOverriddenByUser).toBool(false);
|
config.nameOverriddenByUser = json.value(configKey::nameOverriddenByUser).toBool(false);
|
||||||
config.description = json.value(configKey::description).toString();
|
config.description = json.value(configKey::description).toString();
|
||||||
config.displayName = json.value(configKey::displayName).toString();
|
|
||||||
config.configVersion = json.value(configKey::configVersion).toInt(2);
|
config.configVersion = json.value(configKey::configVersion).toInt(2);
|
||||||
config.hostName = json.value(configKey::hostName).toString();
|
config.hostName = json.value(configKey::hostName).toString();
|
||||||
|
|
||||||
@@ -167,10 +163,6 @@ ApiV2ServerConfig ApiV2ServerConfig::fromJson(const QJsonObject& json)
|
|||||||
config.authData = AuthData::fromJson(authDataObj);
|
config.authData = AuthData::fromJson(authDataObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.displayName.isEmpty()) {
|
|
||||||
config.displayName = config.name.isEmpty() ? config.description : config.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#include "core/models/containerConfig.h"
|
#include "core/models/containerConfig.h"
|
||||||
#include "core/models/api/apiConfig.h"
|
#include "core/models/api/apiConfig.h"
|
||||||
#include "core/models/api/authData.h"
|
#include "core/models/api/authData.h"
|
||||||
#include "core/utils/serverConfigUtils.h"
|
#include "core/utils/api/apiEnums.h"
|
||||||
#include "core/utils/constants/apiKeys.h"
|
#include "core/utils/constants/apiKeys.h"
|
||||||
#include "core/utils/constants/apiConstants.h"
|
#include "core/utils/constants/apiConstants.h"
|
||||||
|
|
||||||
@@ -21,7 +21,6 @@ using namespace ContainerEnumNS;
|
|||||||
|
|
||||||
struct ApiV2ServerConfig {
|
struct ApiV2ServerConfig {
|
||||||
QString description;
|
QString description;
|
||||||
QString displayName;
|
|
||||||
QString hostName;
|
QString hostName;
|
||||||
QMap<DockerContainer, ContainerConfig> containers;
|
QMap<DockerContainer, ContainerConfig> containers;
|
||||||
DockerContainer defaultContainer;
|
DockerContainer defaultContainer;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "core/utils/serverConfigUtils.h"
|
#include "core/utils/api/apiEnums.h"
|
||||||
#include "core/utils/constants/apiKeys.h"
|
#include "core/utils/constants/apiKeys.h"
|
||||||
#include "core/utils/constants/apiConstants.h"
|
#include "core/utils/constants/apiConstants.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
#include "legacyApiServerConfig.h"
|
|
||||||
|
|
||||||
#include "core/utils/constants/apiKeys.h"
|
|
||||||
#include "core/utils/constants/configKeys.h"
|
|
||||||
|
|
||||||
namespace amnezia
|
|
||||||
{
|
|
||||||
|
|
||||||
bool LegacyApiServerConfig::hasContainers() const
|
|
||||||
{
|
|
||||||
return !containers.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
ContainerConfig LegacyApiServerConfig::containerConfig(DockerContainer container) const
|
|
||||||
{
|
|
||||||
if (!containers.contains(container)) {
|
|
||||||
return ContainerConfig{};
|
|
||||||
}
|
|
||||||
return containers.value(container);
|
|
||||||
}
|
|
||||||
|
|
||||||
LegacyApiServerConfig LegacyApiServerConfig::fromJson(const QJsonObject &json)
|
|
||||||
{
|
|
||||||
LegacyApiServerConfig config;
|
|
||||||
|
|
||||||
config.name = json.value(configKey::name).toString();
|
|
||||||
config.description = json.value(configKey::description).toString();
|
|
||||||
config.displayName = json.value(configKey::displayName).toString();
|
|
||||||
config.hostName = json.value(configKey::hostName).toString();
|
|
||||||
|
|
||||||
config.crc = json.value(configKey::crc).toInt(0);
|
|
||||||
|
|
||||||
config.configVersion = json.value(configKey::configVersion).toInt(1);
|
|
||||||
config.apiEndpoint = json.value(apiDefs::key::apiEndpoint).toString();
|
|
||||||
|
|
||||||
if (config.displayName.isEmpty()) {
|
|
||||||
config.displayName = config.name.isEmpty() ? config.description : config.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace amnezia
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
#ifndef LEGACYAPISERVERCONFIG_H
|
|
||||||
#define LEGACYAPISERVERCONFIG_H
|
|
||||||
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QMap>
|
|
||||||
|
|
||||||
#include "core/utils/containerEnum.h"
|
|
||||||
#include "core/utils/protocolEnum.h"
|
|
||||||
#include "core/models/containerConfig.h"
|
|
||||||
|
|
||||||
namespace amnezia
|
|
||||||
{
|
|
||||||
|
|
||||||
using namespace ContainerEnumNS;
|
|
||||||
|
|
||||||
struct LegacyApiServerConfig {
|
|
||||||
QString description;
|
|
||||||
QString displayName;
|
|
||||||
QString hostName;
|
|
||||||
QMap<DockerContainer, ContainerConfig> containers;
|
|
||||||
DockerContainer defaultContainer = DockerContainer::None;
|
|
||||||
QString dns1;
|
|
||||||
QString dns2;
|
|
||||||
|
|
||||||
QString name;
|
|
||||||
int crc = 0;
|
|
||||||
|
|
||||||
int configVersion = 0;
|
|
||||||
QString apiEndpoint;
|
|
||||||
|
|
||||||
bool hasContainers() const;
|
|
||||||
ContainerConfig containerConfig(DockerContainer container) const;
|
|
||||||
static LegacyApiServerConfig fromJson(const QJsonObject &json);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace amnezia
|
|
||||||
|
|
||||||
#endif // LEGACYAPISERVERCONFIG_H
|
|
||||||
@@ -113,26 +113,6 @@ const Socks5ProxyProtocolConfig* ContainerConfig::getSocks5ProxyProtocolConfig()
|
|||||||
return protocolConfig.as<Socks5ProxyProtocolConfig>();
|
return protocolConfig.as<Socks5ProxyProtocolConfig>();
|
||||||
}
|
}
|
||||||
|
|
||||||
MtProxyProtocolConfig* ContainerConfig::getMtProxyProtocolConfig()
|
|
||||||
{
|
|
||||||
return protocolConfig.as<MtProxyProtocolConfig>();
|
|
||||||
}
|
|
||||||
|
|
||||||
const MtProxyProtocolConfig* ContainerConfig::getMtProxyProtocolConfig() const
|
|
||||||
{
|
|
||||||
return protocolConfig.as<MtProxyProtocolConfig>();
|
|
||||||
}
|
|
||||||
|
|
||||||
TelemtProtocolConfig* ContainerConfig::getTelemtProtocolConfig()
|
|
||||||
{
|
|
||||||
return protocolConfig.as<TelemtProtocolConfig>();
|
|
||||||
}
|
|
||||||
|
|
||||||
const TelemtProtocolConfig* ContainerConfig::getTelemtProtocolConfig() const
|
|
||||||
{
|
|
||||||
return protocolConfig.as<TelemtProtocolConfig>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ikev2ProtocolConfig* ContainerConfig::getIkev2ProtocolConfig()
|
Ikev2ProtocolConfig* ContainerConfig::getIkev2ProtocolConfig()
|
||||||
{
|
{
|
||||||
return protocolConfig.as<Ikev2ProtocolConfig>();
|
return protocolConfig.as<Ikev2ProtocolConfig>();
|
||||||
|
|||||||
@@ -57,12 +57,6 @@ struct ContainerConfig {
|
|||||||
Socks5ProxyProtocolConfig* getSocks5ProxyProtocolConfig();
|
Socks5ProxyProtocolConfig* getSocks5ProxyProtocolConfig();
|
||||||
const Socks5ProxyProtocolConfig* getSocks5ProxyProtocolConfig() const;
|
const Socks5ProxyProtocolConfig* getSocks5ProxyProtocolConfig() const;
|
||||||
|
|
||||||
MtProxyProtocolConfig* getMtProxyProtocolConfig();
|
|
||||||
const MtProxyProtocolConfig* getMtProxyProtocolConfig() const;
|
|
||||||
|
|
||||||
TelemtProtocolConfig* getTelemtProtocolConfig();
|
|
||||||
const TelemtProtocolConfig* getTelemtProtocolConfig() const;
|
|
||||||
|
|
||||||
Ikev2ProtocolConfig* getIkev2ProtocolConfig();
|
Ikev2ProtocolConfig* getIkev2ProtocolConfig();
|
||||||
const Ikev2ProtocolConfig* getIkev2ProtocolConfig() const;
|
const Ikev2ProtocolConfig* getIkev2ProtocolConfig() const;
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,6 @@
|
|||||||
#include "core/utils/protocolEnum.h"
|
#include "core/utils/protocolEnum.h"
|
||||||
#include "core/models/protocols/ikev2ProtocolConfig.h"
|
#include "core/models/protocols/ikev2ProtocolConfig.h"
|
||||||
#include "core/models/protocols/dnsProtocolConfig.h"
|
#include "core/models/protocols/dnsProtocolConfig.h"
|
||||||
#include "core/models/protocols/mtProxyProtocolConfig.h"
|
|
||||||
#include "core/models/protocols/telemtProtocolConfig.h"
|
|
||||||
|
|
||||||
namespace amnezia
|
namespace amnezia
|
||||||
{
|
{
|
||||||
@@ -40,10 +38,6 @@ Proto ProtocolConfig::type() const
|
|||||||
return Proto::TorWebSite;
|
return Proto::TorWebSite;
|
||||||
} else if constexpr (std::is_same_v<T, DnsProtocolConfig>) {
|
} else if constexpr (std::is_same_v<T, DnsProtocolConfig>) {
|
||||||
return Proto::Dns;
|
return Proto::Dns;
|
||||||
} else if constexpr (std::is_same_v<T, MtProxyProtocolConfig>) {
|
|
||||||
return Proto::MtProxy;
|
|
||||||
} else if constexpr (std::is_same_v<T, TelemtProtocolConfig>) {
|
|
||||||
return Proto::Telemt;
|
|
||||||
}
|
}
|
||||||
return Proto::Unknown;
|
return Proto::Unknown;
|
||||||
}, data);
|
}, data);
|
||||||
@@ -71,10 +65,6 @@ QString ProtocolConfig::port() const
|
|||||||
return QString();
|
return QString();
|
||||||
} else if constexpr (std::is_same_v<T, DnsProtocolConfig>) {
|
} else if constexpr (std::is_same_v<T, DnsProtocolConfig>) {
|
||||||
return QString();
|
return QString();
|
||||||
} else if constexpr (std::is_same_v<T, MtProxyProtocolConfig>) {
|
|
||||||
return arg.port.isEmpty() ? QString(protocols::mtProxy::defaultPort) : arg.port;
|
|
||||||
} else if constexpr (std::is_same_v<T, TelemtProtocolConfig>) {
|
|
||||||
return arg.port.isEmpty() ? QString(protocols::telemt::defaultPort) : arg.port;
|
|
||||||
}
|
}
|
||||||
return QString();
|
return QString();
|
||||||
}, data);
|
}, data);
|
||||||
@@ -98,10 +88,6 @@ QString ProtocolConfig::transportProto() const
|
|||||||
return QString();
|
return QString();
|
||||||
} else if constexpr (std::is_same_v<T, DnsProtocolConfig>) {
|
} else if constexpr (std::is_same_v<T, DnsProtocolConfig>) {
|
||||||
return QString();
|
return QString();
|
||||||
} else if constexpr (std::is_same_v<T, MtProxyProtocolConfig>) {
|
|
||||||
return QStringLiteral("tcp");
|
|
||||||
} else if constexpr (std::is_same_v<T, TelemtProtocolConfig>) {
|
|
||||||
return QStringLiteral("tcp");
|
|
||||||
}
|
}
|
||||||
return QString();
|
return QString();
|
||||||
}, data);
|
}, data);
|
||||||
@@ -313,10 +299,6 @@ ProtocolConfig ProtocolConfig::fromJson(const QJsonObject& json, Proto type)
|
|||||||
return ProtocolConfig{TorProtocolConfig::fromJson(json)};
|
return ProtocolConfig{TorProtocolConfig::fromJson(json)};
|
||||||
case Proto::Dns:
|
case Proto::Dns:
|
||||||
return ProtocolConfig{DnsProtocolConfig::fromJson(json)};
|
return ProtocolConfig{DnsProtocolConfig::fromJson(json)};
|
||||||
case Proto::MtProxy:
|
|
||||||
return ProtocolConfig{MtProxyProtocolConfig::fromJson(json)};
|
|
||||||
case Proto::Telemt:
|
|
||||||
return ProtocolConfig{TelemtProtocolConfig::fromJson(json)};
|
|
||||||
default:
|
default:
|
||||||
return ProtocolConfig{AwgProtocolConfig{}};
|
return ProtocolConfig{AwgProtocolConfig{}};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,6 @@
|
|||||||
#include "core/models/protocols/ikev2ProtocolConfig.h"
|
#include "core/models/protocols/ikev2ProtocolConfig.h"
|
||||||
#include "core/models/protocols/torProtocolConfig.h"
|
#include "core/models/protocols/torProtocolConfig.h"
|
||||||
#include "core/models/protocols/dnsProtocolConfig.h"
|
#include "core/models/protocols/dnsProtocolConfig.h"
|
||||||
#include "core/models/protocols/mtProxyProtocolConfig.h"
|
|
||||||
#include "core/models/protocols/telemtProtocolConfig.h"
|
|
||||||
|
|
||||||
namespace amnezia
|
namespace amnezia
|
||||||
{
|
{
|
||||||
@@ -38,8 +36,6 @@ struct ProtocolConfig {
|
|||||||
XrayProtocolConfig,
|
XrayProtocolConfig,
|
||||||
SftpProtocolConfig,
|
SftpProtocolConfig,
|
||||||
Socks5ProxyProtocolConfig,
|
Socks5ProxyProtocolConfig,
|
||||||
MtProxyProtocolConfig,
|
|
||||||
TelemtProtocolConfig,
|
|
||||||
Ikev2ProtocolConfig,
|
Ikev2ProtocolConfig,
|
||||||
TorProtocolConfig,
|
TorProtocolConfig,
|
||||||
DnsProtocolConfig
|
DnsProtocolConfig
|
||||||
|
|||||||
@@ -1,147 +0,0 @@
|
|||||||
#include "mtProxyProtocolConfig.h"
|
|
||||||
|
|
||||||
#include "../../../core/utils/protocolEnum.h"
|
|
||||||
#include "../../../core/protocols/protocolUtils.h"
|
|
||||||
#include "../../../core/utils/constants/configKeys.h"
|
|
||||||
#include "../../../core/utils/constants/protocolConstants.h"
|
|
||||||
#include <QJsonArray>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
using namespace amnezia;
|
|
||||||
|
|
||||||
namespace amnezia {
|
|
||||||
|
|
||||||
QJsonObject MtProxyProtocolConfig::toJson() const {
|
|
||||||
QJsonObject obj;
|
|
||||||
|
|
||||||
if (!port.isEmpty()) {
|
|
||||||
obj[configKey::port] = port;
|
|
||||||
}
|
|
||||||
if (!secret.isEmpty()) {
|
|
||||||
obj[protocols::mtProxy::secretKey] = secret;
|
|
||||||
}
|
|
||||||
if (!tag.isEmpty()) {
|
|
||||||
obj[protocols::mtProxy::tagKey] = tag;
|
|
||||||
}
|
|
||||||
if (!tgLink.isEmpty()) {
|
|
||||||
obj[protocols::mtProxy::tgLinkKey] = tgLink;
|
|
||||||
}
|
|
||||||
if (!tmeLink.isEmpty()) {
|
|
||||||
obj[protocols::mtProxy::tmeLinkKey] = tmeLink;
|
|
||||||
}
|
|
||||||
obj[protocols::mtProxy::isEnabledKey] = isEnabled;
|
|
||||||
if (!publicHost.isEmpty()) {
|
|
||||||
obj[protocols::mtProxy::publicHostKey] = publicHost;
|
|
||||||
}
|
|
||||||
if (!transportMode.isEmpty()) {
|
|
||||||
obj[protocols::mtProxy::transportModeKey] = transportMode;
|
|
||||||
}
|
|
||||||
if (!tlsDomain.isEmpty()) {
|
|
||||||
obj[protocols::mtProxy::tlsDomainKey] = tlsDomain;
|
|
||||||
}
|
|
||||||
if (!additionalSecrets.isEmpty()) {
|
|
||||||
obj[protocols::mtProxy::additionalSecretsKey] = QJsonArray::fromStringList(additionalSecrets);
|
|
||||||
}
|
|
||||||
if (!workersMode.isEmpty()) {
|
|
||||||
obj[protocols::mtProxy::workersModeKey] = workersMode;
|
|
||||||
}
|
|
||||||
if (!workers.isEmpty()) {
|
|
||||||
obj[protocols::mtProxy::workersKey] = workers;
|
|
||||||
}
|
|
||||||
obj[protocols::mtProxy::natEnabledKey] = natEnabled;
|
|
||||||
if (!natInternalIp.isEmpty()) {
|
|
||||||
obj[protocols::mtProxy::natInternalIpKey] = natInternalIp;
|
|
||||||
}
|
|
||||||
if (!natExternalIp.isEmpty()) {
|
|
||||||
obj[protocols::mtProxy::natExternalIpKey] = natExternalIp;
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
MtProxyProtocolConfig MtProxyProtocolConfig::fromJson(const QJsonObject &json) {
|
|
||||||
MtProxyProtocolConfig config;
|
|
||||||
|
|
||||||
config.port = json.value(configKey::port).toString();
|
|
||||||
config.secret = json.value(protocols::mtProxy::secretKey).toString();
|
|
||||||
config.tag = json.value(protocols::mtProxy::tagKey).toString();
|
|
||||||
config.tgLink = json.value(protocols::mtProxy::tgLinkKey).toString();
|
|
||||||
config.tmeLink = json.value(protocols::mtProxy::tmeLinkKey).toString();
|
|
||||||
config.isEnabled = json.value(protocols::mtProxy::isEnabledKey).toBool(true);
|
|
||||||
config.publicHost = json.value(protocols::mtProxy::publicHostKey).toString();
|
|
||||||
config.transportMode = json.value(protocols::mtProxy::transportModeKey).toString();
|
|
||||||
config.tlsDomain = json.value(protocols::mtProxy::tlsDomainKey).toString();
|
|
||||||
for (const auto &v: json.value(protocols::mtProxy::additionalSecretsKey).toArray()) {
|
|
||||||
const QString s = v.toString();
|
|
||||||
if (!s.isEmpty()) {
|
|
||||||
config.additionalSecrets.append(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
config.workersMode = json.value(protocols::mtProxy::workersModeKey).toString();
|
|
||||||
config.workers = json.value(protocols::mtProxy::workersKey).toString();
|
|
||||||
config.natEnabled = json.value(protocols::mtProxy::natEnabledKey).toBool(false);
|
|
||||||
config.natInternalIp = json.value(protocols::mtProxy::natInternalIpKey).toString();
|
|
||||||
config.natExternalIp = json.value(protocols::mtProxy::natExternalIpKey).toString();
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MtProxyProtocolConfig::equalsDockerDeploymentSettings(const MtProxyProtocolConfig &other) const {
|
|
||||||
const auto normPort = [](const QString &p) {
|
|
||||||
return p.isEmpty() ? QString(protocols::mtProxy::defaultPort) : p;
|
|
||||||
};
|
|
||||||
const auto normTransport = [](const QString &t) {
|
|
||||||
return t.isEmpty() ? QString(protocols::mtProxy::transportModeStandard) : t;
|
|
||||||
};
|
|
||||||
const auto normWorkersMode = [](const QString &m) {
|
|
||||||
return m.isEmpty() ? QString(protocols::mtProxy::workersModeAuto) : m;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (normPort(port) != normPort(other.port)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (normTransport(transportMode) != normTransport(other.transportMode)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (tlsDomain != other.tlsDomain) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (secret != other.secret) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (tag != other.tag) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (publicHost != other.publicHost) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (normWorkersMode(workersMode) != normWorkersMode(other.workersMode)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (workers != other.workers) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (natEnabled != other.natEnabled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (natInternalIp != other.natInternalIp) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (natExternalIp != other.natExternalIp) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (isEnabled != other.isEnabled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList aa = additionalSecrets;
|
|
||||||
QStringList bb = other.additionalSecrets;
|
|
||||||
aa.removeAll(QString());
|
|
||||||
bb.removeAll(QString());
|
|
||||||
std::sort(aa.begin(), aa.end());
|
|
||||||
std::sort(bb.begin(), bb.end());
|
|
||||||
return aa == bb;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace amnezia
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
#ifndef MTPROXYPROTOCOLCONFIG_H
|
|
||||||
#define MTPROXYPROTOCOLCONFIG_H
|
|
||||||
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QString>
|
|
||||||
#include <QStringList>
|
|
||||||
|
|
||||||
namespace amnezia {
|
|
||||||
|
|
||||||
struct MtProxyProtocolConfig {
|
|
||||||
QString port;
|
|
||||||
QString secret;
|
|
||||||
QString tag;
|
|
||||||
QString tgLink;
|
|
||||||
QString tmeLink;
|
|
||||||
bool isEnabled = true;
|
|
||||||
QString publicHost;
|
|
||||||
QString transportMode;
|
|
||||||
QString tlsDomain;
|
|
||||||
QStringList additionalSecrets;
|
|
||||||
QString workersMode;
|
|
||||||
QString workers;
|
|
||||||
bool natEnabled = false;
|
|
||||||
QString natInternalIp;
|
|
||||||
QString natExternalIp;
|
|
||||||
|
|
||||||
QJsonObject toJson() const;
|
|
||||||
|
|
||||||
static MtProxyProtocolConfig fromJson(const QJsonObject &json);
|
|
||||||
|
|
||||||
// Port, transport, TLS, secrets, NAT, workers, isEnabled, additionalSecrets (order-independent).
|
|
||||||
// Ignores tgLink / tmeLink (derived / display).
|
|
||||||
bool equalsDockerDeploymentSettings(const MtProxyProtocolConfig &other) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace amnezia
|
|
||||||
|
|
||||||
#endif // MTPROXYPROTOCOLCONFIG_H
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
#include "telemtProtocolConfig.h"
|
|
||||||
|
|
||||||
#include "core/utils/constants/configKeys.h"
|
|
||||||
#include "core/utils/constants/protocolConstants.h"
|
|
||||||
|
|
||||||
#include <QJsonArray>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
using namespace amnezia;
|
|
||||||
|
|
||||||
QJsonObject TelemtProtocolConfig::toJson() const
|
|
||||||
{
|
|
||||||
QJsonObject obj;
|
|
||||||
if (!port.isEmpty()) {
|
|
||||||
obj[QString(configKey::port)] = port;
|
|
||||||
}
|
|
||||||
if (!secret.isEmpty()) {
|
|
||||||
obj[protocols::telemt::secretKey] = secret;
|
|
||||||
}
|
|
||||||
if (!tag.isEmpty()) {
|
|
||||||
obj[protocols::telemt::tagKey] = tag;
|
|
||||||
}
|
|
||||||
if (!tgLink.isEmpty()) {
|
|
||||||
obj[protocols::telemt::tgLinkKey] = tgLink;
|
|
||||||
}
|
|
||||||
if (!tmeLink.isEmpty()) {
|
|
||||||
obj[protocols::telemt::tmeLinkKey] = tmeLink;
|
|
||||||
}
|
|
||||||
obj[protocols::telemt::isEnabledKey] = isEnabled;
|
|
||||||
if (!publicHost.isEmpty()) {
|
|
||||||
obj[protocols::telemt::publicHostKey] = publicHost;
|
|
||||||
}
|
|
||||||
if (!transportMode.isEmpty()) {
|
|
||||||
obj[protocols::telemt::transportModeKey] = transportMode;
|
|
||||||
}
|
|
||||||
if (!tlsDomain.isEmpty()) {
|
|
||||||
obj[protocols::telemt::tlsDomainKey] = tlsDomain;
|
|
||||||
}
|
|
||||||
obj[protocols::telemt::maskEnabledKey] = maskEnabled;
|
|
||||||
obj[protocols::telemt::tlsEmulationKey] = tlsEmulation;
|
|
||||||
obj[protocols::telemt::useMiddleProxyKey] = useMiddleProxy;
|
|
||||||
if (!userName.isEmpty()) {
|
|
||||||
obj[protocols::telemt::userNameKey] = userName;
|
|
||||||
}
|
|
||||||
if (!additionalSecrets.isEmpty()) {
|
|
||||||
obj[protocols::telemt::additionalSecretsKey] = QJsonArray::fromStringList(additionalSecrets);
|
|
||||||
}
|
|
||||||
if (!workersMode.isEmpty()) {
|
|
||||||
obj[protocols::telemt::workersModeKey] = workersMode;
|
|
||||||
}
|
|
||||||
if (!workers.isEmpty()) {
|
|
||||||
obj[protocols::telemt::workersKey] = workers;
|
|
||||||
}
|
|
||||||
obj[protocols::telemt::natEnabledKey] = natEnabled;
|
|
||||||
if (!natInternalIp.isEmpty()) {
|
|
||||||
obj[protocols::telemt::natInternalIpKey] = natInternalIp;
|
|
||||||
}
|
|
||||||
if (!natExternalIp.isEmpty()) {
|
|
||||||
obj[protocols::telemt::natExternalIpKey] = natExternalIp;
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
TelemtProtocolConfig TelemtProtocolConfig::fromJson(const QJsonObject &json)
|
|
||||||
{
|
|
||||||
TelemtProtocolConfig c;
|
|
||||||
c.port = json.value(QString(configKey::port)).toString();
|
|
||||||
c.secret = json.value(protocols::telemt::secretKey).toString();
|
|
||||||
c.tag = json.value(protocols::telemt::tagKey).toString();
|
|
||||||
c.tgLink = json.value(protocols::telemt::tgLinkKey).toString();
|
|
||||||
c.tmeLink = json.value(protocols::telemt::tmeLinkKey).toString();
|
|
||||||
c.isEnabled = json.value(protocols::telemt::isEnabledKey).toBool(true);
|
|
||||||
c.publicHost = json.value(protocols::telemt::publicHostKey).toString();
|
|
||||||
c.transportMode = json.value(protocols::telemt::transportModeKey).toString();
|
|
||||||
c.tlsDomain = json.value(protocols::telemt::tlsDomainKey).toString();
|
|
||||||
c.maskEnabled = json.value(protocols::telemt::maskEnabledKey).toBool(true);
|
|
||||||
c.tlsEmulation = json.value(protocols::telemt::tlsEmulationKey).toBool(false);
|
|
||||||
c.useMiddleProxy = json.value(protocols::telemt::useMiddleProxyKey).toBool(true);
|
|
||||||
c.userName = json.value(protocols::telemt::userNameKey).toString();
|
|
||||||
for (const auto &v : json.value(protocols::telemt::additionalSecretsKey).toArray()) {
|
|
||||||
const QString s = v.toString();
|
|
||||||
if (!s.isEmpty()) {
|
|
||||||
c.additionalSecrets.append(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.workersMode = json.value(protocols::telemt::workersModeKey).toString();
|
|
||||||
c.workers = json.value(protocols::telemt::workersKey).toString();
|
|
||||||
c.natEnabled = json.value(protocols::telemt::natEnabledKey).toBool(false);
|
|
||||||
c.natInternalIp = json.value(protocols::telemt::natInternalIpKey).toString();
|
|
||||||
c.natExternalIp = json.value(protocols::telemt::natExternalIpKey).toString();
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TelemtProtocolConfig::equalsDockerDeploymentSettings(const TelemtProtocolConfig &other) const
|
|
||||||
{
|
|
||||||
const auto normPort = [](const QString &p) {
|
|
||||||
return p.isEmpty() ? QString(protocols::telemt::defaultPort) : p;
|
|
||||||
};
|
|
||||||
const auto normTransport = [](const QString &t) {
|
|
||||||
return t.isEmpty() ? QString(protocols::telemt::transportModeStandard) : t;
|
|
||||||
};
|
|
||||||
const auto normWorkersMode = [](const QString &m) {
|
|
||||||
return m.isEmpty() ? QString(protocols::telemt::workersModeAuto) : m;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (normPort(port) != normPort(other.port)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (normTransport(transportMode) != normTransport(other.transportMode)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (tlsDomain != other.tlsDomain) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (secret != other.secret) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (tag != other.tag) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (publicHost != other.publicHost) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (maskEnabled != other.maskEnabled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (tlsEmulation != other.tlsEmulation) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (useMiddleProxy != other.useMiddleProxy) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (userName != other.userName) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (normWorkersMode(workersMode) != normWorkersMode(other.workersMode)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (workers != other.workers) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (natEnabled != other.natEnabled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (natInternalIp != other.natInternalIp) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (natExternalIp != other.natExternalIp) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (isEnabled != other.isEnabled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList aa = additionalSecrets;
|
|
||||||
QStringList bb = other.additionalSecrets;
|
|
||||||
aa.removeAll(QString());
|
|
||||||
bb.removeAll(QString());
|
|
||||||
std::sort(aa.begin(), aa.end());
|
|
||||||
std::sort(bb.begin(), bb.end());
|
|
||||||
return aa == bb;
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
#ifndef TELEMTPROTOCOLCONFIG_H
|
|
||||||
#define TELEMTPROTOCOLCONFIG_H
|
|
||||||
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QString>
|
|
||||||
#include <QStringList>
|
|
||||||
|
|
||||||
namespace amnezia {
|
|
||||||
|
|
||||||
struct TelemtProtocolConfig {
|
|
||||||
QString port;
|
|
||||||
QString secret;
|
|
||||||
QString tag;
|
|
||||||
QString tgLink;
|
|
||||||
QString tmeLink;
|
|
||||||
bool isEnabled = true;
|
|
||||||
QString publicHost;
|
|
||||||
QString transportMode;
|
|
||||||
QString tlsDomain;
|
|
||||||
bool maskEnabled = true;
|
|
||||||
bool tlsEmulation = false;
|
|
||||||
bool useMiddleProxy = true;
|
|
||||||
QString userName;
|
|
||||||
QStringList additionalSecrets;
|
|
||||||
QString workersMode;
|
|
||||||
QString workers;
|
|
||||||
bool natEnabled = false;
|
|
||||||
QString natInternalIp;
|
|
||||||
QString natExternalIp;
|
|
||||||
|
|
||||||
QJsonObject toJson() const;
|
|
||||||
static TelemtProtocolConfig fromJson(const QJsonObject &json);
|
|
||||||
bool equalsDockerDeploymentSettings(const TelemtProtocolConfig &other) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace amnezia
|
|
||||||
|
|
||||||
#endif // TELEMTPROTOCOLCONFIG_H
|
|
||||||
@@ -3,173 +3,20 @@
|
|||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
|
|
||||||
#include "core/utils/protocolEnum.h"
|
#include "../../../core/utils/protocolEnum.h"
|
||||||
#include "core/protocols/protocolUtils.h"
|
#include "../../../core/protocols/protocolUtils.h"
|
||||||
#include "core/utils/constants/configKeys.h"
|
#include "../../../core/utils/constants/configKeys.h"
|
||||||
#include "core/utils/constants/protocolConstants.h"
|
#include "../../../core/utils/constants/protocolConstants.h"
|
||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
using namespace ProtocolUtils;
|
using namespace ProtocolUtils;
|
||||||
|
|
||||||
namespace amnezia
|
namespace amnezia
|
||||||
{
|
{
|
||||||
QJsonObject XrayXPaddingConfig::toJson() const
|
|
||||||
{
|
|
||||||
QJsonObject obj;
|
|
||||||
if (!bytesMin.isEmpty()) obj[configKey::xPaddingBytesMin] = bytesMin;
|
|
||||||
if (!bytesMax.isEmpty()) obj[configKey::xPaddingBytesMax] = bytesMax;
|
|
||||||
obj[configKey::xPaddingObfsMode] = obfsMode;
|
|
||||||
if (!key.isEmpty()) obj[configKey::xPaddingKey] = key;
|
|
||||||
if (!header.isEmpty()) obj[configKey::xPaddingHeader] = header;
|
|
||||||
if (!placement.isEmpty()) obj[configKey::xPaddingPlacement] = placement;
|
|
||||||
if (!method.isEmpty()) obj[configKey::xPaddingMethod] = method;
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
XrayXPaddingConfig XrayXPaddingConfig::fromJson(const QJsonObject &json)
|
|
||||||
{
|
|
||||||
XrayXPaddingConfig c;
|
|
||||||
c.bytesMin = json.value(configKey::xPaddingBytesMin).toString();
|
|
||||||
c.bytesMax = json.value(configKey::xPaddingBytesMax).toString();
|
|
||||||
c.obfsMode = json.value(configKey::xPaddingObfsMode).toBool(true);
|
|
||||||
c.key = json.value(configKey::xPaddingKey).toString(protocols::xray::defaultSite);
|
|
||||||
c.header = json.value(configKey::xPaddingHeader).toString();
|
|
||||||
c.placement = json.value(configKey::xPaddingPlacement).toString(protocols::xray::defaultXPaddingPlacement);
|
|
||||||
c.method = json.value(configKey::xPaddingMethod).toString(protocols::xray::defaultXPaddingMethod);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject XrayXmuxConfig::toJson() const
|
|
||||||
{
|
|
||||||
QJsonObject obj;
|
|
||||||
obj[configKey::xmuxEnabled] = enabled;
|
|
||||||
if (!maxConcurrencyMin.isEmpty()) obj[configKey::xmuxMaxConcurrencyMin] = maxConcurrencyMin;
|
|
||||||
if (!maxConcurrencyMax.isEmpty()) obj[configKey::xmuxMaxConcurrencyMax] = maxConcurrencyMax;
|
|
||||||
if (!maxConnectionsMin.isEmpty()) obj[configKey::xmuxMaxConnectionsMin] = maxConnectionsMin;
|
|
||||||
if (!maxConnectionsMax.isEmpty()) obj[configKey::xmuxMaxConnectionsMax] = maxConnectionsMax;
|
|
||||||
if (!cMaxReuseTimesMin.isEmpty()) obj[configKey::xmuxCMaxReuseTimesMin] = cMaxReuseTimesMin;
|
|
||||||
if (!cMaxReuseTimesMax.isEmpty()) obj[configKey::xmuxCMaxReuseTimesMax] = cMaxReuseTimesMax;
|
|
||||||
if (!hMaxRequestTimesMin.isEmpty()) obj[configKey::xmuxHMaxRequestTimesMin] = hMaxRequestTimesMin;
|
|
||||||
if (!hMaxRequestTimesMax.isEmpty()) obj[configKey::xmuxHMaxRequestTimesMax] = hMaxRequestTimesMax;
|
|
||||||
if (!hMaxReusableSecsMin.isEmpty()) obj[configKey::xmuxHMaxReusableSecsMin] = hMaxReusableSecsMin;
|
|
||||||
if (!hMaxReusableSecsMax.isEmpty()) obj[configKey::xmuxHMaxReusableSecsMax] = hMaxReusableSecsMax;
|
|
||||||
if (!hKeepAlivePeriod.isEmpty()) obj[configKey::xmuxHKeepAlivePeriod] = hKeepAlivePeriod;
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
XrayXmuxConfig XrayXmuxConfig::fromJson(const QJsonObject &json)
|
|
||||||
{
|
|
||||||
XrayXmuxConfig c;
|
|
||||||
c.enabled = json.value(configKey::xmuxEnabled).toBool(true);
|
|
||||||
c.maxConcurrencyMin = json.value(configKey::xmuxMaxConcurrencyMin).toString("0");
|
|
||||||
c.maxConcurrencyMax = json.value(configKey::xmuxMaxConcurrencyMax).toString("0");
|
|
||||||
c.maxConnectionsMin = json.value(configKey::xmuxMaxConnectionsMin).toString("0");
|
|
||||||
c.maxConnectionsMax = json.value(configKey::xmuxMaxConnectionsMax).toString("0");
|
|
||||||
c.cMaxReuseTimesMin = json.value(configKey::xmuxCMaxReuseTimesMin).toString("0");
|
|
||||||
c.cMaxReuseTimesMax = json.value(configKey::xmuxCMaxReuseTimesMax).toString("0");
|
|
||||||
c.hMaxRequestTimesMin = json.value(configKey::xmuxHMaxRequestTimesMin).toString("0");
|
|
||||||
c.hMaxRequestTimesMax = json.value(configKey::xmuxHMaxRequestTimesMax).toString("0");
|
|
||||||
c.hMaxReusableSecsMin = json.value(configKey::xmuxHMaxReusableSecsMin).toString("0");
|
|
||||||
c.hMaxReusableSecsMax = json.value(configKey::xmuxHMaxReusableSecsMax).toString("0");
|
|
||||||
c.hKeepAlivePeriod = json.value(configKey::xmuxHKeepAlivePeriod).toString();
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject XrayXhttpConfig::toJson() const
|
|
||||||
{
|
|
||||||
QJsonObject obj;
|
|
||||||
if (!mode.isEmpty()) obj[configKey::xhttpMode] = mode;
|
|
||||||
if (!host.isEmpty()) obj[configKey::xhttpHost] = host;
|
|
||||||
if (!path.isEmpty()) obj[configKey::xhttpPath] = path;
|
|
||||||
if (!headersTemplate.isEmpty()) obj[configKey::xhttpHeadersTemplate] = headersTemplate;
|
|
||||||
if (!uplinkMethod.isEmpty()) obj[configKey::xhttpUplinkMethod] = uplinkMethod;
|
|
||||||
obj[configKey::xhttpDisableGrpc] = disableGrpc;
|
|
||||||
obj[configKey::xhttpDisableSse] = disableSse;
|
|
||||||
|
|
||||||
if (!sessionPlacement.isEmpty()) obj[configKey::xhttpSessionPlacement] = sessionPlacement;
|
|
||||||
if (!sessionKey.isEmpty()) obj[configKey::xhttpSessionKey] = sessionKey;
|
|
||||||
if (!seqPlacement.isEmpty()) obj[configKey::xhttpSeqPlacement] = seqPlacement;
|
|
||||||
if (!seqKey.isEmpty()) obj[configKey::xhttpSeqKey] = seqKey;
|
|
||||||
if (!uplinkDataPlacement.isEmpty()) obj[configKey::xhttpUplinkDataPlacement] = uplinkDataPlacement;
|
|
||||||
if (!uplinkDataKey.isEmpty()) obj[configKey::xhttpUplinkDataKey] = uplinkDataKey;
|
|
||||||
|
|
||||||
if (!uplinkChunkSize.isEmpty()) obj[configKey::xhttpUplinkChunkSize] = uplinkChunkSize;
|
|
||||||
if (!scMaxBufferedPosts.isEmpty()) obj[configKey::xhttpScMaxBufferedPosts] = scMaxBufferedPosts;
|
|
||||||
if (!scMaxEachPostBytesMin.isEmpty()) obj[configKey::xhttpScMaxEachPostBytesMin] = scMaxEachPostBytesMin;
|
|
||||||
if (!scMaxEachPostBytesMax.isEmpty()) obj[configKey::xhttpScMaxEachPostBytesMax] = scMaxEachPostBytesMax;
|
|
||||||
if (!scMinPostsIntervalMsMin.isEmpty()) obj[configKey::xhttpScMinPostsIntervalMsMin] = scMinPostsIntervalMsMin;
|
|
||||||
if (!scMinPostsIntervalMsMax.isEmpty()) obj[configKey::xhttpScMinPostsIntervalMsMax] = scMinPostsIntervalMsMax;
|
|
||||||
if (!scStreamUpServerSecsMin.isEmpty()) obj[configKey::xhttpScStreamUpServerSecsMin] = scStreamUpServerSecsMin;
|
|
||||||
if (!scStreamUpServerSecsMax.isEmpty()) obj[configKey::xhttpScStreamUpServerSecsMax] = scStreamUpServerSecsMax;
|
|
||||||
|
|
||||||
obj["xPadding"] = xPadding.toJson();
|
|
||||||
obj["xmux"] = xmux.toJson();
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
XrayXhttpConfig XrayXhttpConfig::fromJson(const QJsonObject &json)
|
|
||||||
{
|
|
||||||
XrayXhttpConfig c;
|
|
||||||
c.mode = json.value(configKey::xhttpMode).toString(protocols::xray::defaultXhttpMode);
|
|
||||||
c.host = json.value(configKey::xhttpHost).toString(protocols::xray::defaultSite);
|
|
||||||
c.path = json.value(configKey::xhttpPath).toString();
|
|
||||||
c.headersTemplate = json.value(configKey::xhttpHeadersTemplate).toString(protocols::xray::defaultXhttpHeadersTemplate);
|
|
||||||
c.uplinkMethod = json.value(configKey::xhttpUplinkMethod).toString(protocols::xray::defaultXhttpUplinkMethod);
|
|
||||||
c.disableGrpc = json.value(configKey::xhttpDisableGrpc).toBool(true);
|
|
||||||
c.disableSse = json.value(configKey::xhttpDisableSse).toBool(true);
|
|
||||||
|
|
||||||
c.sessionPlacement = json.value(configKey::xhttpSessionPlacement).toString(protocols::xray::defaultXhttpSessionPlacement);
|
|
||||||
c.sessionKey = json.value(configKey::xhttpSessionKey).toString();
|
|
||||||
c.seqPlacement = json.value(configKey::xhttpSeqPlacement).toString(protocols::xray::defaultXhttpSessionPlacement);
|
|
||||||
c.seqKey = json.value(configKey::xhttpSeqKey).toString();
|
|
||||||
c.uplinkDataPlacement = json.value(configKey::xhttpUplinkDataPlacement).toString(protocols::xray::defaultXhttpUplinkDataPlacement);
|
|
||||||
c.uplinkDataKey = json.value(configKey::xhttpUplinkDataKey).toString();
|
|
||||||
|
|
||||||
c.uplinkChunkSize = json.value(configKey::xhttpUplinkChunkSize).toString("0");
|
|
||||||
c.scMaxBufferedPosts = json.value(configKey::xhttpScMaxBufferedPosts).toString();
|
|
||||||
c.scMaxEachPostBytesMin = json.value(configKey::xhttpScMaxEachPostBytesMin).toString("1");
|
|
||||||
c.scMaxEachPostBytesMax = json.value(configKey::xhttpScMaxEachPostBytesMax).toString("100");
|
|
||||||
c.scMinPostsIntervalMsMin = json.value(configKey::xhttpScMinPostsIntervalMsMin).toString("100");
|
|
||||||
c.scMinPostsIntervalMsMax = json.value(configKey::xhttpScMinPostsIntervalMsMax).toString("800");
|
|
||||||
c.scStreamUpServerSecsMin = json.value(configKey::xhttpScStreamUpServerSecsMin).toString("1");
|
|
||||||
c.scStreamUpServerSecsMax = json.value(configKey::xhttpScStreamUpServerSecsMax).toString("100");
|
|
||||||
|
|
||||||
c.xPadding = XrayXPaddingConfig::fromJson(json.value("xPadding").toObject());
|
|
||||||
c.xmux = XrayXmuxConfig::fromJson(json.value("xmux").toObject());
|
|
||||||
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject XrayMkcpConfig::toJson() const
|
|
||||||
{
|
|
||||||
QJsonObject obj;
|
|
||||||
if (!tti.isEmpty()) obj[configKey::mkcpTti] = tti;
|
|
||||||
if (!uplinkCapacity.isEmpty()) obj[configKey::mkcpUplinkCapacity] = uplinkCapacity;
|
|
||||||
if (!downlinkCapacity.isEmpty()) obj[configKey::mkcpDownlinkCapacity] = downlinkCapacity;
|
|
||||||
if (!readBufferSize.isEmpty()) obj[configKey::mkcpReadBufferSize] = readBufferSize;
|
|
||||||
if (!writeBufferSize.isEmpty()) obj[configKey::mkcpWriteBufferSize] = writeBufferSize;
|
|
||||||
obj[configKey::mkcpCongestion] = congestion;
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
XrayMkcpConfig XrayMkcpConfig::fromJson(const QJsonObject &json)
|
|
||||||
{
|
|
||||||
XrayMkcpConfig c;
|
|
||||||
c.tti = json.value(configKey::mkcpTti).toString();
|
|
||||||
c.uplinkCapacity = json.value(configKey::mkcpUplinkCapacity).toString();
|
|
||||||
c.downlinkCapacity = json.value(configKey::mkcpDownlinkCapacity).toString();
|
|
||||||
c.readBufferSize = json.value(configKey::mkcpReadBufferSize).toString();
|
|
||||||
c.writeBufferSize = json.value(configKey::mkcpWriteBufferSize).toString();
|
|
||||||
c.congestion = json.value(configKey::mkcpCongestion).toBool(true);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject XrayServerConfig::toJson() const
|
QJsonObject XrayServerConfig::toJson() const
|
||||||
{
|
{
|
||||||
QJsonObject obj;
|
QJsonObject obj;
|
||||||
|
|
||||||
// Existing fields
|
|
||||||
if (!port.isEmpty()) {
|
if (!port.isEmpty()) {
|
||||||
obj[configKey::port] = port;
|
obj[configKey::port] = port;
|
||||||
}
|
}
|
||||||
@@ -187,91 +34,55 @@ QJsonObject XrayServerConfig::toJson() const
|
|||||||
obj[configKey::isThirdPartyConfig] = isThirdPartyConfig;
|
obj[configKey::isThirdPartyConfig] = isThirdPartyConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
// New: Security
|
|
||||||
if (!security.isEmpty()) {
|
|
||||||
obj[configKey::xraySecurity] = security;
|
|
||||||
}
|
|
||||||
if (!flow.isEmpty()) {
|
|
||||||
obj[configKey::xrayFlow] = flow;
|
|
||||||
}
|
|
||||||
if (!fingerprint.isEmpty()) {
|
|
||||||
obj[configKey::xrayFingerprint] = fingerprint;
|
|
||||||
}
|
|
||||||
if (!sni.isEmpty()) {
|
|
||||||
obj[configKey::xraySni] = sni;
|
|
||||||
}
|
|
||||||
if (!alpn.isEmpty()) {
|
|
||||||
obj[configKey::xrayAlpn] = alpn;
|
|
||||||
}
|
|
||||||
|
|
||||||
// New: Transport
|
|
||||||
if (!transport.isEmpty()) {
|
|
||||||
obj[configKey::xrayTransport] = transport;
|
|
||||||
}
|
|
||||||
obj["xhttp"] = xhttp.toJson();
|
|
||||||
obj["mkcp"] = mkcp.toJson();
|
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
XrayServerConfig XrayServerConfig::fromJson(const QJsonObject& json)
|
XrayServerConfig XrayServerConfig::fromJson(const QJsonObject& json)
|
||||||
{
|
{
|
||||||
XrayServerConfig c;
|
XrayServerConfig config;
|
||||||
|
|
||||||
// Existing fields
|
config.port = json.value(configKey::port).toString();
|
||||||
c.port = json.value(configKey::port).toString();
|
config.transportProto = json.value(configKey::transportProto).toString();
|
||||||
c.transportProto = json.value(configKey::transportProto).toString();
|
config.subnetAddress = json.value(configKey::subnetAddress).toString();
|
||||||
c.subnetAddress = json.value(configKey::subnetAddress).toString();
|
config.site = json.value(configKey::site).toString();
|
||||||
c.site = json.value(configKey::site).toString();
|
|
||||||
c.isThirdPartyConfig = json.value(configKey::isThirdPartyConfig).toBool(false);
|
|
||||||
|
|
||||||
// New: Security
|
config.isThirdPartyConfig = json.value(configKey::isThirdPartyConfig).toBool(false);
|
||||||
c.security = json.value(configKey::xraySecurity).toString(protocols::xray::defaultSecurity);
|
|
||||||
c.flow = json.value(configKey::xrayFlow).toString(protocols::xray::defaultFlow);
|
|
||||||
c.fingerprint = json.value(configKey::xrayFingerprint).toString(protocols::xray::defaultFingerprint);
|
|
||||||
if (c.fingerprint.contains(QLatin1String("Mozilla/5.0"), Qt::CaseInsensitive)) {
|
|
||||||
c.fingerprint = QString::fromLatin1(protocols::xray::defaultFingerprint);
|
|
||||||
}
|
|
||||||
c.sni = json.value(configKey::xraySni).toString(protocols::xray::defaultSni);
|
|
||||||
c.alpn = json.value(configKey::xrayAlpn).toString(protocols::xray::defaultAlpn);
|
|
||||||
|
|
||||||
// New: Transport
|
return config;
|
||||||
c.transport = json.value(configKey::xrayTransport).toString(protocols::xray::defaultTransport);
|
|
||||||
c.xhttp = XrayXhttpConfig::fromJson(json.value("xhttp").toObject());
|
|
||||||
c.mkcp = XrayMkcpConfig::fromJson(json.value("mkcp").toObject());
|
|
||||||
|
|
||||||
return c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XrayServerConfig::hasEqualServerSettings(const XrayServerConfig& other) const
|
bool XrayServerConfig::hasEqualServerSettings(const XrayServerConfig& other) const
|
||||||
{
|
{
|
||||||
return port == other.port
|
return port == other.port && site == other.site;
|
||||||
&& site == other.site
|
|
||||||
&& security == other.security
|
|
||||||
&& flow == other.flow
|
|
||||||
&& transport == other.transport
|
|
||||||
&& fingerprint == other.fingerprint
|
|
||||||
&& sni == other.sni;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject XrayClientConfig::toJson() const
|
QJsonObject XrayClientConfig::toJson() const
|
||||||
{
|
{
|
||||||
QJsonObject obj;
|
QJsonObject obj;
|
||||||
if (!nativeConfig.isEmpty()) obj[configKey::config] = nativeConfig;
|
|
||||||
if (!localPort.isEmpty()) obj[configKey::localPort] = localPort;
|
if (!nativeConfig.isEmpty()) {
|
||||||
if (!id.isEmpty()) obj[configKey::clientId] = id;
|
obj[configKey::config] = nativeConfig;
|
||||||
|
}
|
||||||
|
if (!localPort.isEmpty()) {
|
||||||
|
obj[configKey::localPort] = localPort;
|
||||||
|
}
|
||||||
|
if (!id.isEmpty()) {
|
||||||
|
obj[configKey::clientId] = id;
|
||||||
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
XrayClientConfig XrayClientConfig::fromJson(const QJsonObject& json)
|
XrayClientConfig XrayClientConfig::fromJson(const QJsonObject& json)
|
||||||
{
|
{
|
||||||
XrayClientConfig c;
|
XrayClientConfig config;
|
||||||
c.nativeConfig = json.value(configKey::config).toString();
|
|
||||||
c.localPort = json.value(configKey::localPort).toString();
|
|
||||||
c.id = json.value(configKey::clientId).toString();
|
|
||||||
|
|
||||||
if (c.id.isEmpty() && !c.nativeConfig.isEmpty()) {
|
config.nativeConfig = json.value(configKey::config).toString();
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(c.nativeConfig.toUtf8());
|
config.localPort = json.value(configKey::localPort).toString();
|
||||||
|
config.id = json.value(configKey::clientId).toString();
|
||||||
|
|
||||||
|
if (config.id.isEmpty() && !config.nativeConfig.isEmpty()) {
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(config.nativeConfig.toUtf8());
|
||||||
if (!doc.isNull() && doc.isObject()) {
|
if (!doc.isNull() && doc.isObject()) {
|
||||||
QJsonObject configObj = doc.object();
|
QJsonObject configObj = doc.object();
|
||||||
if (configObj.contains(protocols::xray::outbounds)) {
|
if (configObj.contains(protocols::xray::outbounds)) {
|
||||||
@@ -289,7 +100,7 @@ XrayClientConfig XrayClientConfig::fromJson(const QJsonObject &json)
|
|||||||
if (!users.isEmpty()) {
|
if (!users.isEmpty()) {
|
||||||
QJsonObject user = users[0].toObject();
|
QJsonObject user = users[0].toObject();
|
||||||
if (user.contains(protocols::xray::id)) {
|
if (user.contains(protocols::xray::id)) {
|
||||||
c.id = user[protocols::xray::id].toString();
|
config.id = user[protocols::xray::id].toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -301,7 +112,7 @@ XrayClientConfig XrayClientConfig::fromJson(const QJsonObject &json)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return c;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject XrayProtocolConfig::toJson() const
|
QJsonObject XrayProtocolConfig::toJson() const
|
||||||
@@ -309,6 +120,7 @@ QJsonObject XrayProtocolConfig::toJson() const
|
|||||||
QJsonObject obj = serverConfig.toJson();
|
QJsonObject obj = serverConfig.toJson();
|
||||||
|
|
||||||
if (clientConfig.has_value()) {
|
if (clientConfig.has_value()) {
|
||||||
|
// Third-party import: nativeConfig is raw Xray JSON (inbounds/outbounds)
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(clientConfig->nativeConfig.toUtf8());
|
QJsonDocument doc = QJsonDocument::fromJson(clientConfig->nativeConfig.toUtf8());
|
||||||
if (!doc.isNull() && doc.isObject() && doc.object().contains(protocols::xray::outbounds)
|
if (!doc.isNull() && doc.isObject() && doc.object().contains(protocols::xray::outbounds)
|
||||||
&& !doc.object().contains(configKey::config)) {
|
&& !doc.object().contains(configKey::config)) {
|
||||||
@@ -324,14 +136,16 @@ QJsonObject XrayProtocolConfig::toJson() const
|
|||||||
|
|
||||||
XrayProtocolConfig XrayProtocolConfig::fromJson(const QJsonObject& json)
|
XrayProtocolConfig XrayProtocolConfig::fromJson(const QJsonObject& json)
|
||||||
{
|
{
|
||||||
XrayProtocolConfig c;
|
XrayProtocolConfig config;
|
||||||
c.serverConfig = XrayServerConfig::fromJson(json);
|
|
||||||
|
config.serverConfig = XrayServerConfig::fromJson(json);
|
||||||
|
|
||||||
QString lastConfigStr = json.value(configKey::lastConfig).toString();
|
QString lastConfigStr = json.value(configKey::lastConfig).toString();
|
||||||
if (!lastConfigStr.isEmpty()) {
|
if (!lastConfigStr.isEmpty()) {
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(lastConfigStr.toUtf8());
|
QJsonDocument doc = QJsonDocument::fromJson(lastConfigStr.toUtf8());
|
||||||
if (doc.isObject()) {
|
if (doc.isObject()) {
|
||||||
QJsonObject parsed = doc.object();
|
QJsonObject parsed = doc.object();
|
||||||
|
// Third-party import stores raw Xray config (inbounds/outbounds) directly
|
||||||
if (parsed.contains(protocols::xray::outbounds) && !parsed.contains(configKey::config)) {
|
if (parsed.contains(protocols::xray::outbounds) && !parsed.contains(configKey::config)) {
|
||||||
XrayClientConfig clientCfg;
|
XrayClientConfig clientCfg;
|
||||||
clientCfg.nativeConfig = lastConfigStr;
|
clientCfg.nativeConfig = lastConfigStr;
|
||||||
@@ -344,14 +158,14 @@ XrayProtocolConfig XrayProtocolConfig::fromJson(const QJsonObject &json)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.clientConfig = clientCfg;
|
config.clientConfig = clientCfg;
|
||||||
} else {
|
} else {
|
||||||
c.clientConfig = XrayClientConfig::fromJson(parsed);
|
config.clientConfig = XrayClientConfig::fromJson(parsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return c;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XrayProtocolConfig::hasClientConfig() const
|
bool XrayProtocolConfig::hasClientConfig() const
|
||||||
@@ -370,3 +184,4 @@ void XrayProtocolConfig::clearClientConfig()
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace amnezia
|
} // namespace amnezia
|
||||||
|
|
||||||
|
|||||||
@@ -2,96 +2,12 @@
|
|||||||
#define XRAYPROTOCOLCONFIG_H
|
#define XRAYPROTOCOLCONFIG_H
|
||||||
|
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include "core/utils/constants/protocolConstants.h"
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
namespace amnezia
|
namespace amnezia
|
||||||
{
|
{
|
||||||
|
|
||||||
// ── xPadding ─────────────────────────────────────────────────────────────────
|
|
||||||
struct XrayXPaddingConfig {
|
|
||||||
QString bytesMin; // xPaddingBytes min
|
|
||||||
QString bytesMax; // xPaddingBytes max
|
|
||||||
bool obfsMode = true; // xPaddingObfsMode
|
|
||||||
QString key; // xPaddingKey
|
|
||||||
QString header; // xPaddingHeader
|
|
||||||
QString placement = protocols::xray::defaultXPaddingPlacement; // xPaddingPlacement: Cookie|Header|Query|Body
|
|
||||||
QString method = protocols::xray::defaultXPaddingMethod; // xPaddingMethod: Repeat-x|Random|Zero
|
|
||||||
|
|
||||||
QJsonObject toJson() const;
|
|
||||||
static XrayXPaddingConfig fromJson(const QJsonObject &json);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ── xmux ─────────────────────────────────────────────────────────────────────
|
|
||||||
struct XrayXmuxConfig {
|
|
||||||
bool enabled = true;
|
|
||||||
|
|
||||||
QString maxConcurrencyMin = "0";
|
|
||||||
QString maxConcurrencyMax = "0";
|
|
||||||
QString maxConnectionsMin = "0";
|
|
||||||
QString maxConnectionsMax = "0";
|
|
||||||
QString cMaxReuseTimesMin = "0";
|
|
||||||
QString cMaxReuseTimesMax = "0";
|
|
||||||
QString hMaxRequestTimesMin = "0";
|
|
||||||
QString hMaxRequestTimesMax = "0";
|
|
||||||
QString hMaxReusableSecsMin = "0";
|
|
||||||
QString hMaxReusableSecsMax = "0";
|
|
||||||
QString hKeepAlivePeriod;
|
|
||||||
|
|
||||||
QJsonObject toJson() const;
|
|
||||||
static XrayXmuxConfig fromJson(const QJsonObject &json);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ── XHTTP transport ───────────────────────────────────────────────────────────
|
|
||||||
struct XrayXhttpConfig {
|
|
||||||
QString mode = protocols::xray::defaultXhttpMode; // Auto|Packet-up|Stream-up|Stream-one
|
|
||||||
QString host = protocols::xray::defaultXhttpHost;
|
|
||||||
QString path;
|
|
||||||
QString headersTemplate = protocols::xray::defaultXhttpHeadersTemplate; // HTTP|None
|
|
||||||
QString uplinkMethod = protocols::xray::defaultXhttpUplinkMethod; // POST|PUT|PATCH
|
|
||||||
bool disableGrpc = true;
|
|
||||||
bool disableSse = true;
|
|
||||||
|
|
||||||
// Session & Sequence
|
|
||||||
QString sessionPlacement = protocols::xray::defaultXhttpSessionPlacement;
|
|
||||||
QString sessionKey = protocols::xray::defaultXhttpSessionKey;
|
|
||||||
QString seqPlacement = protocols::xray::defaultXhttpSeqPlacement;
|
|
||||||
QString seqKey;
|
|
||||||
QString uplinkDataPlacement = protocols::xray::defaultXhttpUplinkDataPlacement;
|
|
||||||
QString uplinkDataKey;
|
|
||||||
|
|
||||||
// Traffic Shaping
|
|
||||||
QString uplinkChunkSize = protocols::xray::defaultXhttpUplinkChunkSize;
|
|
||||||
QString scMaxBufferedPosts;
|
|
||||||
QString scMaxEachPostBytesMin = protocols::xray::defaultXhttpScMaxEachPostBytesMin;
|
|
||||||
QString scMaxEachPostBytesMax = protocols::xray::defaultXhttpScMaxEachPostBytesMax;
|
|
||||||
QString scMinPostsIntervalMsMin = protocols::xray::defaultXhttpScMinPostsIntervalMsMin;
|
|
||||||
QString scMinPostsIntervalMsMax = protocols::xray::defaultXhttpScMinPostsIntervalMsMax;
|
|
||||||
QString scStreamUpServerSecsMin = protocols::xray::defaultXhttpScStreamUpServerSecsMin;
|
|
||||||
QString scStreamUpServerSecsMax = protocols::xray::defaultXhttpScStreamUpServerSecsMax;
|
|
||||||
|
|
||||||
XrayXPaddingConfig xPadding;
|
|
||||||
XrayXmuxConfig xmux;
|
|
||||||
|
|
||||||
QJsonObject toJson() const;
|
|
||||||
static XrayXhttpConfig fromJson(const QJsonObject &json);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ── mKCP transport ────────────────────────────────────────────────────────────
|
|
||||||
struct XrayMkcpConfig {
|
|
||||||
QString tti;
|
|
||||||
QString uplinkCapacity;
|
|
||||||
QString downlinkCapacity;
|
|
||||||
QString readBufferSize;
|
|
||||||
QString writeBufferSize;
|
|
||||||
bool congestion = true;
|
|
||||||
|
|
||||||
QJsonObject toJson() const;
|
|
||||||
static XrayMkcpConfig fromJson(const QJsonObject &json);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ── Server config (settings editable by user) ─────────────────────────────────
|
|
||||||
struct XrayServerConfig {
|
struct XrayServerConfig {
|
||||||
QString port;
|
QString port;
|
||||||
QString transportProto;
|
QString transportProto;
|
||||||
@@ -99,26 +15,12 @@ struct XrayServerConfig {
|
|||||||
QString site;
|
QString site;
|
||||||
bool isThirdPartyConfig = false;
|
bool isThirdPartyConfig = false;
|
||||||
|
|
||||||
// New: Security
|
|
||||||
QString security = protocols::xray::defaultSecurity;
|
|
||||||
QString flow = protocols::xray::defaultFlow;
|
|
||||||
QString fingerprint = protocols::xray::defaultFingerprint;
|
|
||||||
QString sni = protocols::xray::defaultSni;
|
|
||||||
QString alpn = protocols::xray::defaultAlpn;
|
|
||||||
|
|
||||||
// New: Transport
|
|
||||||
QString transport = protocols::xray::defaultTransport;
|
|
||||||
XrayXhttpConfig xhttp;
|
|
||||||
XrayMkcpConfig mkcp;
|
|
||||||
|
|
||||||
QJsonObject toJson() const;
|
QJsonObject toJson() const;
|
||||||
|
|
||||||
static XrayServerConfig fromJson(const QJsonObject& json);
|
static XrayServerConfig fromJson(const QJsonObject& json);
|
||||||
|
|
||||||
bool hasEqualServerSettings(const XrayServerConfig& other) const;
|
bool hasEqualServerSettings(const XrayServerConfig& other) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── Client config (generated, not edited by user) ─────────────────────────────
|
|
||||||
struct XrayClientConfig {
|
struct XrayClientConfig {
|
||||||
QString nativeConfig;
|
QString nativeConfig;
|
||||||
QString localPort;
|
QString localPort;
|
||||||
@@ -128,7 +30,6 @@ struct XrayClientConfig {
|
|||||||
static XrayClientConfig fromJson(const QJsonObject& json);
|
static XrayClientConfig fromJson(const QJsonObject& json);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── Top-level protocol config ──────────────────────────────────────────────────
|
|
||||||
struct XrayProtocolConfig {
|
struct XrayProtocolConfig {
|
||||||
XrayServerConfig serverConfig;
|
XrayServerConfig serverConfig;
|
||||||
std::optional<XrayClientConfig> clientConfig;
|
std::optional<XrayClientConfig> clientConfig;
|
||||||
@@ -144,3 +45,4 @@ struct XrayProtocolConfig {
|
|||||||
} // namespace amnezia
|
} // namespace amnezia
|
||||||
|
|
||||||
#endif // XRAYPROTOCOLCONFIG_H
|
#endif // XRAYPROTOCOLCONFIG_H
|
||||||
|
|
||||||
|
|||||||
@@ -35,9 +35,6 @@ QJsonObject NativeServerConfig::toJson() const
|
|||||||
if (!description.isEmpty()) {
|
if (!description.isEmpty()) {
|
||||||
obj[configKey::description] = this->description;
|
obj[configKey::description] = this->description;
|
||||||
}
|
}
|
||||||
if (!displayName.isEmpty()) {
|
|
||||||
obj[configKey::displayName] = displayName;
|
|
||||||
}
|
|
||||||
if (!hostName.isEmpty()) {
|
if (!hostName.isEmpty()) {
|
||||||
obj[configKey::hostName] = hostName;
|
obj[configKey::hostName] = hostName;
|
||||||
}
|
}
|
||||||
@@ -70,7 +67,6 @@ NativeServerConfig NativeServerConfig::fromJson(const QJsonObject& json)
|
|||||||
NativeServerConfig config;
|
NativeServerConfig config;
|
||||||
|
|
||||||
config.description = json.value(configKey::description).toString();
|
config.description = json.value(configKey::description).toString();
|
||||||
config.displayName = json.value(configKey::displayName).toString();
|
|
||||||
config.hostName = json.value(configKey::hostName).toString();
|
config.hostName = json.value(configKey::hostName).toString();
|
||||||
|
|
||||||
QJsonArray containersArray = json.value(configKey::containers).toArray();
|
QJsonArray containersArray = json.value(configKey::containers).toArray();
|
||||||
@@ -90,10 +86,6 @@ NativeServerConfig NativeServerConfig::fromJson(const QJsonObject& json)
|
|||||||
config.dns1 = json.value(configKey::dns1).toString();
|
config.dns1 = json.value(configKey::dns1).toString();
|
||||||
config.dns2 = json.value(configKey::dns2).toString();
|
config.dns2 = json.value(configKey::dns2).toString();
|
||||||
|
|
||||||
if (config.displayName.isEmpty()) {
|
|
||||||
config.displayName = config.description.isEmpty() ? config.hostName : config.description;
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ using namespace ContainerEnumNS;
|
|||||||
|
|
||||||
struct NativeServerConfig {
|
struct NativeServerConfig {
|
||||||
QString description;
|
QString description;
|
||||||
QString displayName;
|
|
||||||
QString hostName;
|
QString hostName;
|
||||||
QMap<DockerContainer, ContainerConfig> containers;
|
QMap<DockerContainer, ContainerConfig> containers;
|
||||||
DockerContainer defaultContainer;
|
DockerContainer defaultContainer;
|
||||||
|
|||||||
@@ -1,170 +0,0 @@
|
|||||||
#include "selfHostedAdminServerConfig.h"
|
|
||||||
|
|
||||||
#include <QJsonArray>
|
|
||||||
|
|
||||||
#include "core/protocols/protocolUtils.h"
|
|
||||||
#include "core/utils/constants/configKeys.h"
|
|
||||||
#include "core/utils/constants/protocolConstants.h"
|
|
||||||
#include "core/utils/containerEnum.h"
|
|
||||||
#include "core/utils/containers/containerUtils.h"
|
|
||||||
#include "core/utils/protocolEnum.h"
|
|
||||||
#include "core/utils/networkUtilities.h"
|
|
||||||
|
|
||||||
namespace amnezia
|
|
||||||
{
|
|
||||||
|
|
||||||
using namespace ContainerEnumNS;
|
|
||||||
|
|
||||||
bool SelfHostedAdminServerConfig::hasCredentials() const
|
|
||||||
{
|
|
||||||
return !userName.isEmpty() && !password.isEmpty() && port > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SelfHostedAdminServerConfig::isReadOnly() const
|
|
||||||
{
|
|
||||||
return !hasCredentials();
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerCredentials SelfHostedAdminServerConfig::credentials() const
|
|
||||||
{
|
|
||||||
ServerCredentials creds;
|
|
||||||
creds.hostName = hostName;
|
|
||||||
creds.userName = userName;
|
|
||||||
creds.secretData = password;
|
|
||||||
creds.port = port;
|
|
||||||
return creds;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SelfHostedAdminServerConfig::hasContainers() const
|
|
||||||
{
|
|
||||||
return !containers.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
ContainerConfig SelfHostedAdminServerConfig::containerConfig(DockerContainer container) const
|
|
||||||
{
|
|
||||||
if (!containers.contains(container)) {
|
|
||||||
return ContainerConfig{};
|
|
||||||
}
|
|
||||||
return containers.value(container);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SelfHostedAdminServerConfig::updateContainerConfig(DockerContainer container, const ContainerConfig &config)
|
|
||||||
{
|
|
||||||
containers[container] = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SelfHostedAdminServerConfig::clearCachedClientProfile(DockerContainer container)
|
|
||||||
{
|
|
||||||
if (ContainerUtils::containerService(container) == ServiceType::Other) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ContainerConfig cleared = containerConfig(container);
|
|
||||||
cleared.protocolConfig.clearClientConfig();
|
|
||||||
containers[container] = cleared;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPair<QString, QString> SelfHostedAdminServerConfig::getDnsPair(bool isAmneziaDnsEnabled, const QString &primaryDns,
|
|
||||||
const QString &secondaryDns) const
|
|
||||||
{
|
|
||||||
QString d1 = dns1;
|
|
||||||
QString d2 = dns2;
|
|
||||||
const bool dnsOnServer = containers.contains(DockerContainer::Dns);
|
|
||||||
|
|
||||||
if (d1.isEmpty() || !NetworkUtilities::checkIPv4Format(d1)) {
|
|
||||||
d1 = (isAmneziaDnsEnabled && dnsOnServer) ? protocols::dns::amneziaDnsIp : primaryDns;
|
|
||||||
}
|
|
||||||
if (d2.isEmpty() || !NetworkUtilities::checkIPv4Format(d2)) {
|
|
||||||
d2 = secondaryDns;
|
|
||||||
}
|
|
||||||
return { d1, d2 };
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject SelfHostedAdminServerConfig::toJson() const
|
|
||||||
{
|
|
||||||
QJsonObject obj;
|
|
||||||
|
|
||||||
if (!description.isEmpty()) {
|
|
||||||
obj[configKey::description] = this->description;
|
|
||||||
}
|
|
||||||
if (!displayName.isEmpty()) {
|
|
||||||
obj[configKey::displayName] = displayName;
|
|
||||||
}
|
|
||||||
if (!hostName.isEmpty()) {
|
|
||||||
obj[configKey::hostName] = hostName;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonArray containersArray;
|
|
||||||
for (auto it = containers.begin(); it != containers.end(); ++it) {
|
|
||||||
QJsonObject containerObj = it.value().toJson();
|
|
||||||
containersArray.append(containerObj);
|
|
||||||
}
|
|
||||||
if (!containersArray.isEmpty()) {
|
|
||||||
obj[configKey::containers] = containersArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defaultContainer != DockerContainer::None) {
|
|
||||||
obj[configKey::defaultContainer] = ContainerUtils::containerToString(defaultContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dns1.isEmpty()) {
|
|
||||||
obj[configKey::dns1] = dns1;
|
|
||||||
}
|
|
||||||
if (!dns2.isEmpty()) {
|
|
||||||
obj[configKey::dns2] = dns2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!userName.isEmpty()) {
|
|
||||||
obj[configKey::userName] = userName;
|
|
||||||
}
|
|
||||||
if (!password.isEmpty()) {
|
|
||||||
obj[configKey::password] = password;
|
|
||||||
}
|
|
||||||
if (port > 0) {
|
|
||||||
obj[configKey::port] = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
SelfHostedAdminServerConfig SelfHostedAdminServerConfig::fromJson(const QJsonObject &json)
|
|
||||||
{
|
|
||||||
SelfHostedAdminServerConfig config;
|
|
||||||
|
|
||||||
config.description = json.value(configKey::description).toString();
|
|
||||||
config.displayName = json.value(configKey::displayName).toString();
|
|
||||||
config.hostName = json.value(configKey::hostName).toString();
|
|
||||||
|
|
||||||
QJsonArray containersArray = json.value(configKey::containers).toArray();
|
|
||||||
for (const QJsonValue &val : containersArray) {
|
|
||||||
QJsonObject containerObj = val.toObject();
|
|
||||||
ContainerConfig cc = ContainerConfig::fromJson(containerObj);
|
|
||||||
|
|
||||||
QString containerStr = containerObj.value(configKey::container).toString();
|
|
||||||
DockerContainer container = ContainerUtils::containerFromString(containerStr);
|
|
||||||
|
|
||||||
config.containers.insert(container, cc);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString defaultContainerStr = json.value(configKey::defaultContainer).toString();
|
|
||||||
config.defaultContainer = ContainerUtils::containerFromString(defaultContainerStr);
|
|
||||||
|
|
||||||
config.dns1 = json.value(configKey::dns1).toString();
|
|
||||||
config.dns2 = json.value(configKey::dns2).toString();
|
|
||||||
|
|
||||||
config.userName = json.value(configKey::userName).toString();
|
|
||||||
config.password = json.value(configKey::password).toString();
|
|
||||||
if (json.contains(configKey::port)) {
|
|
||||||
config.port = json.value(configKey::port).toInt();
|
|
||||||
} else {
|
|
||||||
config.port = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.displayName.isEmpty()) {
|
|
||||||
config.displayName = config.description.isEmpty() ? config.hostName : config.description;
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace amnezia
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
#ifndef SELFHOSTEDADMINSERVERCONFIG_H
|
|
||||||
#define SELFHOSTEDADMINSERVERCONFIG_H
|
|
||||||
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QMap>
|
|
||||||
#include <QPair>
|
|
||||||
|
|
||||||
#include "core/utils/containerEnum.h"
|
|
||||||
#include "core/utils/containers/containerUtils.h"
|
|
||||||
#include "core/utils/protocolEnum.h"
|
|
||||||
#include "core/models/containerConfig.h"
|
|
||||||
#include "core/utils/errorCodes.h"
|
|
||||||
#include "core/utils/routeModes.h"
|
|
||||||
#include "core/utils/commonStructs.h"
|
|
||||||
|
|
||||||
namespace amnezia
|
|
||||||
{
|
|
||||||
|
|
||||||
using namespace ContainerEnumNS;
|
|
||||||
|
|
||||||
struct SelfHostedAdminServerConfig {
|
|
||||||
QString description;
|
|
||||||
QString displayName;
|
|
||||||
QString hostName;
|
|
||||||
QMap<DockerContainer, ContainerConfig> containers;
|
|
||||||
DockerContainer defaultContainer;
|
|
||||||
QString dns1;
|
|
||||||
QString dns2;
|
|
||||||
|
|
||||||
QString userName;
|
|
||||||
QString password;
|
|
||||||
int port = 0;
|
|
||||||
|
|
||||||
bool hasCredentials() const;
|
|
||||||
bool isReadOnly() const;
|
|
||||||
ServerCredentials credentials() const;
|
|
||||||
bool hasContainers() const;
|
|
||||||
ContainerConfig containerConfig(DockerContainer container) const;
|
|
||||||
|
|
||||||
void updateContainerConfig(DockerContainer container, const ContainerConfig &config);
|
|
||||||
|
|
||||||
void clearCachedClientProfile(DockerContainer container);
|
|
||||||
|
|
||||||
QPair<QString, QString> getDnsPair(bool isAmneziaDnsEnabled, const QString &primaryDns,
|
|
||||||
const QString &secondaryDns) const;
|
|
||||||
|
|
||||||
QJsonObject toJson() const;
|
|
||||||
static SelfHostedAdminServerConfig fromJson(const QJsonObject &json);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace amnezia
|
|
||||||
|
|
||||||
#endif // SELFHOSTEDADMINSERVERCONFIG_H
|
|
||||||
+48
-22
@@ -1,40 +1,53 @@
|
|||||||
#include "selfHostedUserServerConfig.h"
|
#include "selfHostedServerConfig.h"
|
||||||
|
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "core/protocols/protocolUtils.h"
|
|
||||||
#include "core/utils/constants/configKeys.h"
|
|
||||||
#include "core/utils/constants/protocolConstants.h"
|
|
||||||
#include "core/utils/containerEnum.h"
|
#include "core/utils/containerEnum.h"
|
||||||
#include "core/utils/containers/containerUtils.h"
|
#include "core/utils/containers/containerUtils.h"
|
||||||
#include "core/utils/protocolEnum.h"
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
|
||||||
namespace amnezia
|
namespace amnezia
|
||||||
{
|
{
|
||||||
|
|
||||||
using namespace ContainerEnumNS;
|
using namespace ContainerEnumNS;
|
||||||
|
|
||||||
bool SelfHostedUserServerConfig::hasCredentials() const
|
bool SelfHostedServerConfig::hasCredentials() const
|
||||||
{
|
{
|
||||||
return false;
|
return userName.has_value() && password.has_value() && port.has_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SelfHostedUserServerConfig::isReadOnly() const
|
bool SelfHostedServerConfig::isReadOnly() const
|
||||||
{
|
{
|
||||||
return true;
|
return !hasCredentials();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ServerCredentials> SelfHostedUserServerConfig::credentials() const
|
std::optional<ServerCredentials> SelfHostedServerConfig::credentials() const
|
||||||
{
|
{
|
||||||
|
if (!hasCredentials()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SelfHostedUserServerConfig::hasContainers() const
|
ServerCredentials creds;
|
||||||
|
creds.hostName = hostName;
|
||||||
|
creds.userName = userName.value();
|
||||||
|
creds.secretData = password.value();
|
||||||
|
creds.port = port.value();
|
||||||
|
|
||||||
|
return creds;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SelfHostedServerConfig::hasContainers() const
|
||||||
{
|
{
|
||||||
return !containers.isEmpty();
|
return !containers.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
ContainerConfig SelfHostedUserServerConfig::containerConfig(DockerContainer container) const
|
ContainerConfig SelfHostedServerConfig::containerConfig(DockerContainer container) const
|
||||||
{
|
{
|
||||||
if (!containers.contains(container)) {
|
if (!containers.contains(container)) {
|
||||||
return ContainerConfig{};
|
return ContainerConfig{};
|
||||||
@@ -42,16 +55,13 @@ ContainerConfig SelfHostedUserServerConfig::containerConfig(DockerContainer cont
|
|||||||
return containers.value(container);
|
return containers.value(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject SelfHostedUserServerConfig::toJson() const
|
QJsonObject SelfHostedServerConfig::toJson() const
|
||||||
{
|
{
|
||||||
QJsonObject obj;
|
QJsonObject obj;
|
||||||
|
|
||||||
if (!description.isEmpty()) {
|
if (!description.isEmpty()) {
|
||||||
obj[configKey::description] = this->description;
|
obj[configKey::description] = this->description;
|
||||||
}
|
}
|
||||||
if (!displayName.isEmpty()) {
|
|
||||||
obj[configKey::displayName] = displayName;
|
|
||||||
}
|
|
||||||
if (!hostName.isEmpty()) {
|
if (!hostName.isEmpty()) {
|
||||||
obj[configKey::hostName] = hostName;
|
obj[configKey::hostName] = hostName;
|
||||||
}
|
}
|
||||||
@@ -76,26 +86,35 @@ QJsonObject SelfHostedUserServerConfig::toJson() const
|
|||||||
obj[configKey::dns2] = dns2;
|
obj[configKey::dns2] = dns2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (userName.has_value()) {
|
||||||
|
obj[configKey::userName] = userName.value();
|
||||||
|
}
|
||||||
|
if (password.has_value()) {
|
||||||
|
obj[configKey::password] = password.value();
|
||||||
|
}
|
||||||
|
if (port.has_value()) {
|
||||||
|
obj[configKey::port] = port.value();
|
||||||
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
SelfHostedUserServerConfig SelfHostedUserServerConfig::fromJson(const QJsonObject &json)
|
SelfHostedServerConfig SelfHostedServerConfig::fromJson(const QJsonObject& json)
|
||||||
{
|
{
|
||||||
SelfHostedUserServerConfig config;
|
SelfHostedServerConfig config;
|
||||||
|
|
||||||
config.description = json.value(configKey::description).toString();
|
config.description = json.value(configKey::description).toString();
|
||||||
config.displayName = json.value(configKey::displayName).toString();
|
|
||||||
config.hostName = json.value(configKey::hostName).toString();
|
config.hostName = json.value(configKey::hostName).toString();
|
||||||
|
|
||||||
QJsonArray containersArray = json.value(configKey::containers).toArray();
|
QJsonArray containersArray = json.value(configKey::containers).toArray();
|
||||||
for (const QJsonValue& val : containersArray) {
|
for (const QJsonValue& val : containersArray) {
|
||||||
QJsonObject containerObj = val.toObject();
|
QJsonObject containerObj = val.toObject();
|
||||||
ContainerConfig cc = ContainerConfig::fromJson(containerObj);
|
ContainerConfig containerConfig = ContainerConfig::fromJson(containerObj);
|
||||||
|
|
||||||
QString containerStr = containerObj.value(configKey::container).toString();
|
QString containerStr = containerObj.value(configKey::container).toString();
|
||||||
DockerContainer container = ContainerUtils::containerFromString(containerStr);
|
DockerContainer container = ContainerUtils::containerFromString(containerStr);
|
||||||
|
|
||||||
config.containers.insert(container, cc);
|
config.containers.insert(container, containerConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString defaultContainerStr = json.value(configKey::defaultContainer).toString();
|
QString defaultContainerStr = json.value(configKey::defaultContainer).toString();
|
||||||
@@ -104,11 +123,18 @@ SelfHostedUserServerConfig SelfHostedUserServerConfig::fromJson(const QJsonObjec
|
|||||||
config.dns1 = json.value(configKey::dns1).toString();
|
config.dns1 = json.value(configKey::dns1).toString();
|
||||||
config.dns2 = json.value(configKey::dns2).toString();
|
config.dns2 = json.value(configKey::dns2).toString();
|
||||||
|
|
||||||
if (config.displayName.isEmpty()) {
|
if (json.contains(configKey::userName)) {
|
||||||
config.displayName = config.description.isEmpty() ? config.hostName : config.description;
|
config.userName = json.value(configKey::userName).toString();
|
||||||
|
}
|
||||||
|
if (json.contains(configKey::password)) {
|
||||||
|
config.password = json.value(configKey::password).toString();
|
||||||
|
}
|
||||||
|
if (json.contains(configKey::port)) {
|
||||||
|
config.port = json.value(configKey::port).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace amnezia
|
} // namespace amnezia
|
||||||
|
|
||||||
+12
-6
@@ -1,5 +1,5 @@
|
|||||||
#ifndef SELFHOSTEDUSERSERVERCONFIG_H
|
#ifndef SELFHOSTEDSERVERCONFIG_H
|
||||||
#define SELFHOSTEDUSERSERVERCONFIG_H
|
#define SELFHOSTEDSERVERCONFIG_H
|
||||||
|
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
@@ -9,6 +9,8 @@
|
|||||||
#include "core/utils/containers/containerUtils.h"
|
#include "core/utils/containers/containerUtils.h"
|
||||||
#include "core/utils/protocolEnum.h"
|
#include "core/utils/protocolEnum.h"
|
||||||
#include "core/models/containerConfig.h"
|
#include "core/models/containerConfig.h"
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
#include "core/utils/commonStructs.h"
|
#include "core/utils/commonStructs.h"
|
||||||
|
|
||||||
namespace amnezia
|
namespace amnezia
|
||||||
@@ -16,24 +18,28 @@ namespace amnezia
|
|||||||
|
|
||||||
using namespace ContainerEnumNS;
|
using namespace ContainerEnumNS;
|
||||||
|
|
||||||
struct SelfHostedUserServerConfig {
|
struct SelfHostedServerConfig {
|
||||||
QString description;
|
QString description;
|
||||||
QString displayName;
|
|
||||||
QString hostName;
|
QString hostName;
|
||||||
QMap<DockerContainer, ContainerConfig> containers;
|
QMap<DockerContainer, ContainerConfig> containers;
|
||||||
DockerContainer defaultContainer;
|
DockerContainer defaultContainer;
|
||||||
QString dns1;
|
QString dns1;
|
||||||
QString dns2;
|
QString dns2;
|
||||||
|
|
||||||
|
std::optional<QString> userName;
|
||||||
|
std::optional<QString> password;
|
||||||
|
std::optional<int> port;
|
||||||
|
|
||||||
bool hasCredentials() const;
|
bool hasCredentials() const;
|
||||||
bool isReadOnly() const;
|
bool isReadOnly() const;
|
||||||
std::optional<ServerCredentials> credentials() const;
|
std::optional<ServerCredentials> credentials() const;
|
||||||
bool hasContainers() const;
|
bool hasContainers() const;
|
||||||
ContainerConfig containerConfig(DockerContainer container) const;
|
ContainerConfig containerConfig(DockerContainer container) const;
|
||||||
QJsonObject toJson() const;
|
QJsonObject toJson() const;
|
||||||
static SelfHostedUserServerConfig fromJson(const QJsonObject &json);
|
static SelfHostedServerConfig fromJson(const QJsonObject& json);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace amnezia
|
} // namespace amnezia
|
||||||
|
|
||||||
#endif // SELFHOSTEDUSERSERVERCONFIG_H
|
#endif // SELFHOSTEDSERVERCONFIG_H
|
||||||
|
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
#include "serverConfig.h"
|
||||||
|
|
||||||
|
#include "core/utils/api/apiUtils.h"
|
||||||
|
#include "core/utils/networkUtilities.h"
|
||||||
|
#include "core/models/selfhosted/selfHostedServerConfig.h"
|
||||||
|
#include "core/models/selfhosted/nativeServerConfig.h"
|
||||||
|
#include "core/models/api/apiV1ServerConfig.h"
|
||||||
|
#include "core/models/api/apiV2ServerConfig.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
|
||||||
|
namespace amnezia
|
||||||
|
{
|
||||||
|
|
||||||
|
using namespace ContainerEnumNS;
|
||||||
|
|
||||||
|
QString ServerConfig::description() const
|
||||||
|
{
|
||||||
|
return std::visit([](const auto& v) { return v.description; }, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ServerConfig::hostName() const
|
||||||
|
{
|
||||||
|
return std::visit([](const auto& v) { return v.hostName; }, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ServerConfig::displayName() const
|
||||||
|
{
|
||||||
|
if (isApiV1()) {
|
||||||
|
const auto *apiV1 = as<ApiV1ServerConfig>();
|
||||||
|
return apiV1 ? apiV1->name : description();
|
||||||
|
}
|
||||||
|
if (isApiV2()) {
|
||||||
|
const auto *apiV2 = as<ApiV2ServerConfig>();
|
||||||
|
return apiV2 ? apiV2->name : description();
|
||||||
|
}
|
||||||
|
QString name = description();
|
||||||
|
return name.isEmpty() ? hostName() : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<DockerContainer, ContainerConfig> ServerConfig::containers() const
|
||||||
|
{
|
||||||
|
return std::visit([](const auto& v) { return v.containers; }, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
DockerContainer ServerConfig::defaultContainer() const
|
||||||
|
{
|
||||||
|
return std::visit([](const auto& v) { return v.defaultContainer; }, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ServerConfig::dns1() const
|
||||||
|
{
|
||||||
|
return std::visit([](const auto& v) { return v.dns1; }, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ServerConfig::dns2() const
|
||||||
|
{
|
||||||
|
return std::visit([](const auto& v) { return v.dns2; }, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ServerConfig::hasContainers() const
|
||||||
|
{
|
||||||
|
return std::visit([](const auto& v) { return v.hasContainers(); }, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainerConfig ServerConfig::containerConfig(DockerContainer container) const
|
||||||
|
{
|
||||||
|
return std::visit([container](const auto& v) { return v.containerConfig(container); }, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ServerConfig::crc() const
|
||||||
|
{
|
||||||
|
return std::visit([](const auto& v) -> int {
|
||||||
|
using T = std::decay_t<decltype(v)>;
|
||||||
|
if constexpr (std::is_same_v<T, ApiV1ServerConfig> ||
|
||||||
|
std::is_same_v<T, ApiV2ServerConfig>) {
|
||||||
|
return v.crc;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ServerConfig::configVersion() const
|
||||||
|
{
|
||||||
|
return std::visit([](const auto& v) -> int {
|
||||||
|
using T = std::decay_t<decltype(v)>;
|
||||||
|
if constexpr (std::is_same_v<T, ApiV1ServerConfig>) {
|
||||||
|
return apiDefs::ConfigSource::Telegram;
|
||||||
|
} else if constexpr (std::is_same_v<T, ApiV2ServerConfig>) {
|
||||||
|
return apiDefs::ConfigSource::AmneziaGateway;
|
||||||
|
}
|
||||||
|
return 0; // SelfHostedServerConfig or NativeServerConfig
|
||||||
|
}, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ServerConfig::isSelfHosted() const
|
||||||
|
{
|
||||||
|
return std::holds_alternative<SelfHostedServerConfig>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ServerConfig::isNative() const
|
||||||
|
{
|
||||||
|
return std::holds_alternative<NativeServerConfig>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ServerConfig::isApiV1() const
|
||||||
|
{
|
||||||
|
return std::holds_alternative<ApiV1ServerConfig>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ServerConfig::isApiV2() const
|
||||||
|
{
|
||||||
|
return std::holds_alternative<ApiV2ServerConfig>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ServerConfig::isApiConfig() const
|
||||||
|
{
|
||||||
|
return isApiV1() || isApiV2();
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ServerConfig::toJson() const
|
||||||
|
{
|
||||||
|
return std::visit([](const auto& v) { return v.toJson(); }, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerConfig ServerConfig::fromJson(const QJsonObject& json)
|
||||||
|
{
|
||||||
|
apiDefs::ConfigType configType = apiUtils::getConfigType(json);
|
||||||
|
|
||||||
|
switch (configType) {
|
||||||
|
case apiDefs::ConfigType::SelfHosted: {
|
||||||
|
bool hasThirdPartyConfig = false;
|
||||||
|
QJsonArray containersArray = json.value(configKey::containers).toArray();
|
||||||
|
for (const QJsonValue& val : containersArray) {
|
||||||
|
QJsonObject containerObj = val.toObject();
|
||||||
|
for (auto it = containerObj.begin(); it != containerObj.end(); ++it) {
|
||||||
|
QString key = it.key();
|
||||||
|
if (key != configKey::container) {
|
||||||
|
QJsonObject protocolObj = it.value().toObject();
|
||||||
|
if (protocolObj.contains(configKey::isThirdPartyConfig) &&
|
||||||
|
protocolObj.value(configKey::isThirdPartyConfig).toBool()) {
|
||||||
|
hasThirdPartyConfig = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasThirdPartyConfig) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasThirdPartyConfig) {
|
||||||
|
return ServerConfig{NativeServerConfig::fromJson(json)};
|
||||||
|
} else {
|
||||||
|
return ServerConfig{SelfHostedServerConfig::fromJson(json)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case apiDefs::ConfigType::AmneziaPremiumV1:
|
||||||
|
case apiDefs::ConfigType::AmneziaFreeV2:
|
||||||
|
return ServerConfig{ApiV1ServerConfig::fromJson(json)};
|
||||||
|
case apiDefs::ConfigType::AmneziaPremiumV2:
|
||||||
|
case apiDefs::ConfigType::AmneziaFreeV3:
|
||||||
|
case apiDefs::ConfigType::ExternalPremium:
|
||||||
|
return ServerConfig{ApiV2ServerConfig::fromJson(json)};
|
||||||
|
default: {
|
||||||
|
// Check if any container has isThirdPartyConfig
|
||||||
|
bool hasThirdPartyConfig = false;
|
||||||
|
QJsonArray containersArray = json.value(configKey::containers).toArray();
|
||||||
|
for (const QJsonValue& val : containersArray) {
|
||||||
|
QJsonObject containerObj = val.toObject();
|
||||||
|
// Check all protocol keys in the container object
|
||||||
|
for (auto it = containerObj.begin(); it != containerObj.end(); ++it) {
|
||||||
|
QString key = it.key();
|
||||||
|
if (key != configKey::container) {
|
||||||
|
QJsonObject protocolObj = it.value().toObject();
|
||||||
|
if (protocolObj.contains(configKey::isThirdPartyConfig) &&
|
||||||
|
protocolObj.value(configKey::isThirdPartyConfig).toBool()) {
|
||||||
|
hasThirdPartyConfig = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasThirdPartyConfig) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasThirdPartyConfig) {
|
||||||
|
return ServerConfig{NativeServerConfig::fromJson(json)};
|
||||||
|
} else {
|
||||||
|
return ServerConfig{SelfHostedServerConfig::fromJson(json)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QPair<QString, QString> ServerConfig::getDnsPair(bool isAmneziaDnsEnabled,
|
||||||
|
const QString &primaryDns,
|
||||||
|
const QString &secondaryDns) const
|
||||||
|
{
|
||||||
|
QPair<QString, QString> dns;
|
||||||
|
|
||||||
|
QMap<DockerContainer, ContainerConfig> serverContainers = containers();
|
||||||
|
|
||||||
|
bool isDnsContainerInstalled = false;
|
||||||
|
for (auto it = serverContainers.begin(); it != serverContainers.end(); ++it) {
|
||||||
|
if (it.key() == DockerContainer::Dns) {
|
||||||
|
isDnsContainerInstalled = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dns.first = dns1();
|
||||||
|
dns.second = dns2();
|
||||||
|
|
||||||
|
if (dns.first.isEmpty() || !NetworkUtilities::checkIPv4Format(dns.first)) {
|
||||||
|
if (isAmneziaDnsEnabled && isDnsContainerInstalled) {
|
||||||
|
dns.first = protocols::dns::amneziaDnsIp;
|
||||||
|
} else {
|
||||||
|
dns.first = primaryDns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dns.second.isEmpty() || !NetworkUtilities::checkIPv4Format(dns.second)) {
|
||||||
|
dns.second = secondaryDns;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dns;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace amnezia
|
||||||
|
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
#ifndef SERVERCONFIG_H
|
||||||
|
#define SERVERCONFIG_H
|
||||||
|
|
||||||
|
#include <variant>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/models/selfhosted/selfHostedServerConfig.h"
|
||||||
|
#include "core/models/selfhosted/nativeServerConfig.h"
|
||||||
|
#include "core/models/api/apiV1ServerConfig.h"
|
||||||
|
#include "core/models/api/apiV2ServerConfig.h"
|
||||||
|
#include "core/models/containerConfig.h"
|
||||||
|
|
||||||
|
namespace amnezia
|
||||||
|
{
|
||||||
|
|
||||||
|
using namespace ContainerEnumNS;
|
||||||
|
|
||||||
|
struct ServerConfig {
|
||||||
|
using Variant = std::variant<
|
||||||
|
SelfHostedServerConfig,
|
||||||
|
NativeServerConfig,
|
||||||
|
ApiV1ServerConfig,
|
||||||
|
ApiV2ServerConfig
|
||||||
|
>;
|
||||||
|
|
||||||
|
Variant data;
|
||||||
|
|
||||||
|
ServerConfig() = default;
|
||||||
|
ServerConfig(const Variant& v) : data(v) {}
|
||||||
|
ServerConfig(Variant&& v) : data(std::move(v)) {}
|
||||||
|
|
||||||
|
template<typename T, typename = std::enable_if_t<!std::is_same<std::remove_cv_t<std::remove_reference_t<T>>, ServerConfig>::value>>
|
||||||
|
ServerConfig(const T& v) : data(v) {}
|
||||||
|
|
||||||
|
template<typename T, typename = std::enable_if_t<!std::is_same<std::remove_cv_t<std::remove_reference_t<T>>, ServerConfig>::value>>
|
||||||
|
ServerConfig(T&& v) : data(std::forward<T>(v)) {}
|
||||||
|
|
||||||
|
QString description() const;
|
||||||
|
QString hostName() const;
|
||||||
|
QString displayName() const;
|
||||||
|
QMap<DockerContainer, ContainerConfig> containers() const;
|
||||||
|
DockerContainer defaultContainer() const;
|
||||||
|
QString dns1() const;
|
||||||
|
QString dns2() const;
|
||||||
|
bool hasContainers() const;
|
||||||
|
ContainerConfig containerConfig(DockerContainer container) const;
|
||||||
|
|
||||||
|
int crc() const;
|
||||||
|
int configVersion() const;
|
||||||
|
|
||||||
|
bool isSelfHosted() const;
|
||||||
|
bool isNative() const;
|
||||||
|
bool isApiV1() const;
|
||||||
|
bool isApiV2() const;
|
||||||
|
bool isApiConfig() const;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T* as() {
|
||||||
|
return std::get_if<T>(&data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const T* as() const {
|
||||||
|
return std::get_if<T>(&data);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject toJson() const;
|
||||||
|
static ServerConfig fromJson(const QJsonObject& json);
|
||||||
|
|
||||||
|
template<typename Visitor>
|
||||||
|
auto visit(Visitor&& visitor) {
|
||||||
|
return std::visit(std::forward<Visitor>(visitor), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Visitor>
|
||||||
|
auto visit(Visitor&& visitor) const {
|
||||||
|
return std::visit(std::forward<Visitor>(visitor), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPair<QString, QString> getDnsPair(bool isAmneziaDnsEnabled,
|
||||||
|
const QString &primaryDns,
|
||||||
|
const QString &secondaryDns) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace amnezia
|
||||||
|
|
||||||
|
#endif // SERVERCONFIG_H
|
||||||
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
#include "serverDescription.h"
|
|
||||||
|
|
||||||
#include <QMap>
|
|
||||||
|
|
||||||
#include "core/utils/serverConfigUtils.h"
|
|
||||||
#include "core/utils/constants/apiKeys.h"
|
|
||||||
#include "core/utils/constants/apiConstants.h"
|
|
||||||
#include "core/utils/constants/protocolConstants.h"
|
|
||||||
#include "core/utils/api/apiUtils.h"
|
|
||||||
#include "core/utils/containers/containerUtils.h"
|
|
||||||
#include "core/protocols/protocolUtils.h"
|
|
||||||
#include "core/models/protocols/awgProtocolConfig.h"
|
|
||||||
|
|
||||||
using namespace amnezia;
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
bool computeHasInstalledVpnContainers(const QMap<DockerContainer, ContainerConfig> &containers)
|
|
||||||
{
|
|
||||||
for (auto it = containers.begin(); it != containers.end(); ++it) {
|
|
||||||
const DockerContainer container = it.key();
|
|
||||||
if (ContainerUtils::containerService(container) == ServiceType::Vpn || container == DockerContainer::SSXray) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
ServerDescription buildBaseDescription(const T &server)
|
|
||||||
{
|
|
||||||
ServerDescription row;
|
|
||||||
row.hostName = server.hostName;
|
|
||||||
row.defaultContainer = server.defaultContainer;
|
|
||||||
row.primaryDnsIsAmnezia = (server.dns1 == protocols::dns::amneziaDnsIp);
|
|
||||||
row.hasInstalledVpnContainers = computeHasInstalledVpnContainers(server.containers);
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString getBaseDescription(const QMap<DockerContainer, ContainerConfig> &containers,
|
|
||||||
bool isAmneziaDnsEnabled,
|
|
||||||
bool hasWriteAccess,
|
|
||||||
bool primaryDnsIsAmnezia)
|
|
||||||
{
|
|
||||||
QString description;
|
|
||||||
if (hasWriteAccess) {
|
|
||||||
const bool isDnsInstalled = containers.contains(DockerContainer::Dns);
|
|
||||||
if (isAmneziaDnsEnabled && isDnsInstalled) {
|
|
||||||
description += QStringLiteral("Amnezia DNS | ");
|
|
||||||
}
|
|
||||||
} else if (primaryDnsIsAmnezia) {
|
|
||||||
description += QStringLiteral("Amnezia DNS | ");
|
|
||||||
}
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString getProtocolName(DockerContainer defaultContainer, const QMap<DockerContainer, ContainerConfig> &containers)
|
|
||||||
{
|
|
||||||
QString containerName = ContainerUtils::containerHumanNames().value(defaultContainer);
|
|
||||||
QString protocolVersion;
|
|
||||||
|
|
||||||
if (ContainerUtils::isAwgContainer(defaultContainer)) {
|
|
||||||
const auto it = containers.constFind(defaultContainer);
|
|
||||||
if (it != containers.cend()) {
|
|
||||||
if (const AwgProtocolConfig *awg = it->getAwgProtocolConfig()) {
|
|
||||||
protocolVersion = ProtocolUtils::getProtocolVersionString(awg->toJson());
|
|
||||||
if (defaultContainer == DockerContainer::Awg && !awg->serverConfig.isThirdPartyConfig) {
|
|
||||||
containerName = QStringLiteral("AmneziaWG Legacy");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return containerName + protocolVersion + QStringLiteral(" | ");
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace amnezia
|
|
||||||
{
|
|
||||||
|
|
||||||
ServerDescription buildServerDescription(const SelfHostedAdminServerConfig &server, bool isAmneziaDnsEnabled)
|
|
||||||
{
|
|
||||||
ServerDescription row = buildBaseDescription(server);
|
|
||||||
row.selfHostedSshCredentials.hostName = server.hostName;
|
|
||||||
row.selfHostedSshCredentials.userName = server.userName;
|
|
||||||
row.selfHostedSshCredentials.secretData = server.password;
|
|
||||||
row.selfHostedSshCredentials.port = server.port > 0 ? server.port : 22;
|
|
||||||
|
|
||||||
row.hasWriteAccess = !row.selfHostedSshCredentials.userName.isEmpty()
|
|
||||||
&& !row.selfHostedSshCredentials.secretData.isEmpty();
|
|
||||||
|
|
||||||
row.serverName = server.displayName;
|
|
||||||
row.baseDescription = getBaseDescription(server.containers, isAmneziaDnsEnabled, row.hasWriteAccess, row.primaryDnsIsAmnezia);
|
|
||||||
|
|
||||||
const QString protocolName = getProtocolName(server.defaultContainer, server.containers);
|
|
||||||
row.expandedServerDescription = row.baseDescription + row.hostName;
|
|
||||||
row.collapsedServerDescription = row.baseDescription + protocolName + row.hostName;
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerDescription buildServerDescription(const SelfHostedUserServerConfig &server, bool isAmneziaDnsEnabled)
|
|
||||||
{
|
|
||||||
ServerDescription row = buildBaseDescription(server);
|
|
||||||
row.selfHostedSshCredentials.hostName = server.hostName;
|
|
||||||
row.selfHostedSshCredentials.port = 22;
|
|
||||||
row.hasWriteAccess = false;
|
|
||||||
|
|
||||||
row.serverName = server.displayName;
|
|
||||||
row.baseDescription = getBaseDescription(server.containers, isAmneziaDnsEnabled, row.hasWriteAccess, row.primaryDnsIsAmnezia);
|
|
||||||
|
|
||||||
const QString protocolName = getProtocolName(server.defaultContainer, server.containers);
|
|
||||||
row.expandedServerDescription = row.baseDescription + row.hostName;
|
|
||||||
row.collapsedServerDescription = row.baseDescription + protocolName + row.hostName;
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerDescription buildServerDescription(const NativeServerConfig &server, bool isAmneziaDnsEnabled)
|
|
||||||
{
|
|
||||||
ServerDescription row = buildBaseDescription(server);
|
|
||||||
row.hasWriteAccess = false;
|
|
||||||
|
|
||||||
row.serverName = server.displayName;
|
|
||||||
row.baseDescription = getBaseDescription(server.containers, isAmneziaDnsEnabled, row.hasWriteAccess, row.primaryDnsIsAmnezia);
|
|
||||||
|
|
||||||
const QString protocolName = getProtocolName(server.defaultContainer, server.containers);
|
|
||||||
row.expandedServerDescription = row.baseDescription + row.hostName;
|
|
||||||
row.collapsedServerDescription = row.baseDescription + protocolName + row.hostName;
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerDescription buildServerDescription(const LegacyApiServerConfig &server, bool /*isAmneziaDnsEnabled*/)
|
|
||||||
{
|
|
||||||
ServerDescription row = buildBaseDescription(server);
|
|
||||||
row.configVersion = serverConfigUtils::ConfigSource::Telegram;
|
|
||||||
row.isApiV1 = true;
|
|
||||||
row.isServerFromGatewayApi = false;
|
|
||||||
row.hasWriteAccess = false;
|
|
||||||
|
|
||||||
row.serverName = server.displayName;
|
|
||||||
row.baseDescription = server.description;
|
|
||||||
|
|
||||||
const QString fullDescriptionForCollapsed = row.baseDescription;
|
|
||||||
row.collapsedServerDescription = fullDescriptionForCollapsed;
|
|
||||||
row.expandedServerDescription = fullDescriptionForCollapsed;
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerDescription buildServerDescription(const ApiV2ServerConfig &server, bool /*isAmneziaDnsEnabled*/)
|
|
||||||
{
|
|
||||||
ServerDescription row = buildBaseDescription(server);
|
|
||||||
row.configVersion = serverConfigUtils::ConfigSource::AmneziaGateway;
|
|
||||||
row.isApiV2 = true;
|
|
||||||
row.isServerFromGatewayApi = true;
|
|
||||||
row.isPremium = server.isPremium() || server.isExternalPremium();
|
|
||||||
row.hasWriteAccess = false;
|
|
||||||
|
|
||||||
row.serverName = server.displayName;
|
|
||||||
row.baseDescription = server.apiConfig.serverCountryCode.isEmpty() ? server.description : server.apiConfig.serverCountryName;
|
|
||||||
|
|
||||||
row.isCountrySelectionAvailable = !server.apiConfig.availableCountries.isEmpty();
|
|
||||||
row.apiAvailableCountries = server.apiConfig.availableCountries;
|
|
||||||
row.apiServerCountryCode = server.apiConfig.serverCountryCode;
|
|
||||||
|
|
||||||
row.isAdVisible = server.apiConfig.serviceInfo.isAdVisible;
|
|
||||||
row.adHeader = server.apiConfig.serviceInfo.adHeader;
|
|
||||||
row.adDescription = server.apiConfig.serviceInfo.adDescription;
|
|
||||||
row.adEndpoint = server.apiConfig.serviceInfo.adEndpoint;
|
|
||||||
row.isRenewalAvailable = server.apiConfig.serviceInfo.isRenewalAvailable;
|
|
||||||
|
|
||||||
if (!server.apiConfig.isInAppPurchase) {
|
|
||||||
if (server.apiConfig.subscriptionExpiredByServer) {
|
|
||||||
row.isSubscriptionExpired = true;
|
|
||||||
} else if (!server.apiConfig.subscription.endDate.isEmpty()) {
|
|
||||||
row.isSubscriptionExpired = apiUtils::isSubscriptionExpired(server.apiConfig.subscription.endDate);
|
|
||||||
row.isSubscriptionExpiringSoon = apiUtils::isSubscriptionExpiringSoon(server.apiConfig.subscription.endDate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString fullDescriptionForCollapsed = row.baseDescription;
|
|
||||||
row.collapsedServerDescription = fullDescriptionForCollapsed;
|
|
||||||
row.expandedServerDescription = fullDescriptionForCollapsed;
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace amnezia
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
#ifndef SERVERDESCRIPTION_H
|
|
||||||
#define SERVERDESCRIPTION_H
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QJsonArray>
|
|
||||||
|
|
||||||
#include "core/utils/containerEnum.h"
|
|
||||||
#include "core/utils/selfhosted/sshSession.h"
|
|
||||||
#include "core/models/selfhosted/selfHostedAdminServerConfig.h"
|
|
||||||
#include "core/models/selfhosted/selfHostedUserServerConfig.h"
|
|
||||||
#include "core/models/selfhosted/nativeServerConfig.h"
|
|
||||||
#include "core/models/api/legacyApiServerConfig.h"
|
|
||||||
#include "core/models/api/apiV2ServerConfig.h"
|
|
||||||
|
|
||||||
namespace amnezia
|
|
||||||
{
|
|
||||||
|
|
||||||
struct ServerDescription
|
|
||||||
{
|
|
||||||
QString serverId;
|
|
||||||
|
|
||||||
QString serverName;
|
|
||||||
QString baseDescription;
|
|
||||||
QString hostName;
|
|
||||||
|
|
||||||
int configVersion = 0;
|
|
||||||
|
|
||||||
ServerCredentials selfHostedSshCredentials;
|
|
||||||
bool hasWriteAccess = false;
|
|
||||||
|
|
||||||
bool primaryDnsIsAmnezia = false;
|
|
||||||
DockerContainer defaultContainer = DockerContainer::None;
|
|
||||||
bool hasInstalledVpnContainers = false;
|
|
||||||
|
|
||||||
bool isApiV1 = false;
|
|
||||||
bool isApiV2 = false;
|
|
||||||
bool isServerFromGatewayApi = false;
|
|
||||||
bool isPremium = false;
|
|
||||||
|
|
||||||
bool isCountrySelectionAvailable = false;
|
|
||||||
QJsonArray apiAvailableCountries;
|
|
||||||
QString apiServerCountryCode;
|
|
||||||
|
|
||||||
bool isAdVisible = false;
|
|
||||||
QString adHeader;
|
|
||||||
QString adDescription;
|
|
||||||
QString adEndpoint;
|
|
||||||
bool isRenewalAvailable = false;
|
|
||||||
bool isSubscriptionExpired = false;
|
|
||||||
bool isSubscriptionExpiringSoon = false;
|
|
||||||
|
|
||||||
QString collapsedServerDescription;
|
|
||||||
QString expandedServerDescription;
|
|
||||||
};
|
|
||||||
|
|
||||||
ServerDescription buildServerDescription(const SelfHostedAdminServerConfig &server, bool isAmneziaDnsEnabled);
|
|
||||||
ServerDescription buildServerDescription(const SelfHostedUserServerConfig &server, bool isAmneziaDnsEnabled);
|
|
||||||
ServerDescription buildServerDescription(const NativeServerConfig &server, bool isAmneziaDnsEnabled);
|
|
||||||
ServerDescription buildServerDescription(const LegacyApiServerConfig &server, bool isAmneziaDnsEnabled);
|
|
||||||
ServerDescription buildServerDescription(const ApiV2ServerConfig &server, bool isAmneziaDnsEnabled);
|
|
||||||
|
|
||||||
} // namespace amnezia
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -170,6 +170,11 @@ ErrorCode OpenVpnProtocol::start()
|
|||||||
{
|
{
|
||||||
OpenVpnProtocol::stop();
|
OpenVpnProtocol::stop();
|
||||||
|
|
||||||
|
if (!QFileInfo::exists(Utils::openVpnExecPath())) {
|
||||||
|
setLastError(ErrorCode::OpenVpnExecutableMissing);
|
||||||
|
return lastError();
|
||||||
|
}
|
||||||
|
|
||||||
if (!QFileInfo::exists(configPath())) {
|
if (!QFileInfo::exists(configPath())) {
|
||||||
setLastError(ErrorCode::OpenVpnConfigMissing);
|
setLastError(ErrorCode::OpenVpnConfigMissing);
|
||||||
return lastError();
|
return lastError();
|
||||||
|
|||||||
@@ -68,10 +68,7 @@ QMap<Proto, QString> ProtocolUtils::protocolHumanNames()
|
|||||||
{ Proto::TorWebSite, "Website in Tor network" },
|
{ Proto::TorWebSite, "Website in Tor network" },
|
||||||
{ Proto::Dns, "DNS Service" },
|
{ Proto::Dns, "DNS Service" },
|
||||||
{ Proto::Sftp, QObject::tr("SFTP service") },
|
{ Proto::Sftp, QObject::tr("SFTP service") },
|
||||||
{ Proto::Socks5Proxy, QObject::tr("SOCKS5 proxy server") },
|
{ Proto::Socks5Proxy, QObject::tr("SOCKS5 proxy server") } };
|
||||||
{ Proto::MtProxy, QObject::tr("MTProxy (Telegram)") },
|
|
||||||
{ Proto::Telemt, QObject::tr("Telemt (Telegram)") },
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<Proto, QString> ProtocolUtils::protocolDescriptions()
|
QMap<Proto, QString> ProtocolUtils::protocolDescriptions()
|
||||||
@@ -95,8 +92,6 @@ ServiceType ProtocolUtils::protocolService(Proto p)
|
|||||||
case Proto::Dns: return ServiceType::Other;
|
case Proto::Dns: return ServiceType::Other;
|
||||||
case Proto::Sftp: return ServiceType::Other;
|
case Proto::Sftp: return ServiceType::Other;
|
||||||
case Proto::Socks5Proxy: return ServiceType::Other;
|
case Proto::Socks5Proxy: return ServiceType::Other;
|
||||||
case Proto::MtProxy: return ServiceType::Other;
|
|
||||||
case Proto::Telemt: return ServiceType::Other;
|
|
||||||
default: return ServiceType::Other;
|
default: return ServiceType::Other;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,8 +104,6 @@ int ProtocolUtils::getPortForInstall(Proto p)
|
|||||||
case OpenVpn:
|
case OpenVpn:
|
||||||
case Socks5Proxy:
|
case Socks5Proxy:
|
||||||
return QRandomGenerator::global()->bounded(30000, 50000);
|
return QRandomGenerator::global()->bounded(30000, 50000);
|
||||||
case MtProxy:
|
|
||||||
case Telemt:
|
|
||||||
default:
|
default:
|
||||||
return defaultPort(p);
|
return defaultPort(p);
|
||||||
}
|
}
|
||||||
@@ -130,8 +123,6 @@ int ProtocolUtils::defaultPort(Proto p)
|
|||||||
case Proto::Dns: return 53;
|
case Proto::Dns: return 53;
|
||||||
case Proto::Sftp: return 222;
|
case Proto::Sftp: return 222;
|
||||||
case Proto::Socks5Proxy: return 38080;
|
case Proto::Socks5Proxy: return 38080;
|
||||||
case Proto::MtProxy: return QString(protocols::mtProxy::defaultPort).toInt();
|
|
||||||
case Proto::Telemt: return QString(protocols::telemt::defaultPort).toInt();
|
|
||||||
default: return -1;
|
default: return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,8 +141,6 @@ bool ProtocolUtils::defaultPortChangeable(Proto p)
|
|||||||
case Proto::Dns: return false;
|
case Proto::Dns: return false;
|
||||||
case Proto::Sftp: return true;
|
case Proto::Sftp: return true;
|
||||||
case Proto::Socks5Proxy: return true;
|
case Proto::Socks5Proxy: return true;
|
||||||
case Proto::MtProxy: return true;
|
|
||||||
case Proto::Telemt: return true;
|
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,8 +161,6 @@ TransportProto ProtocolUtils::defaultTransportProto(Proto p)
|
|||||||
case Proto::Dns: return TransportProto::Udp;
|
case Proto::Dns: return TransportProto::Udp;
|
||||||
case Proto::Sftp: return TransportProto::Tcp;
|
case Proto::Sftp: return TransportProto::Tcp;
|
||||||
case Proto::Socks5Proxy: return TransportProto::Tcp;
|
case Proto::Socks5Proxy: return TransportProto::Tcp;
|
||||||
case Proto::MtProxy: return TransportProto::Tcp;
|
|
||||||
case Proto::Telemt: return TransportProto::Tcp;
|
|
||||||
default: return TransportProto::Udp;
|
default: return TransportProto::Udp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -193,10 +180,9 @@ bool ProtocolUtils::defaultTransportProtoChangeable(Proto p)
|
|||||||
case Proto::Dns: return false;
|
case Proto::Dns: return false;
|
||||||
case Proto::Sftp: return false;
|
case Proto::Sftp: return false;
|
||||||
case Proto::Socks5Proxy: return false;
|
case Proto::Socks5Proxy: return false;
|
||||||
case Proto::MtProxy: return false;
|
|
||||||
case Proto::Telemt: return false;
|
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ProtocolUtils::key_proto_config_data(Proto p)
|
QString ProtocolUtils::key_proto_config_data(Proto p)
|
||||||
@@ -222,3 +208,4 @@ QString ProtocolUtils::getProtocolVersionString(const QJsonObject &protocolConfi
|
|||||||
if (version == protocols::awg::awgV1_5) return QObject::tr(" (version 1.5)");
|
if (version == protocols::awg::awgV1_5) return QObject::tr(" (version 1.5)");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Regular → Executable
+23
-85
@@ -1,18 +1,16 @@
|
|||||||
#include "xrayProtocol.h"
|
#include "xrayProtocol.h"
|
||||||
|
|
||||||
#include "core/protocols/protocolUtils.h"
|
|
||||||
#include "core/utils/constants/configKeys.h"
|
#include "core/utils/constants/configKeys.h"
|
||||||
#include "core/utils/constants/protocolConstants.h"
|
|
||||||
#include "core/utils/ipcClient.h"
|
#include "core/utils/ipcClient.h"
|
||||||
#include "core/utils/networkUtilities.h"
|
#include "core/utils/networkUtilities.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
#include "core/utils/serialization/serialization.h"
|
#include "core/utils/serialization/serialization.h"
|
||||||
#include "ipc.h"
|
#include "ipc.h"
|
||||||
|
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QNetworkInterface>
|
#include <QNetworkInterface>
|
||||||
|
#include <QJsonDocument>
|
||||||
#include <QtCore/qlogging.h>
|
#include <QtCore/qlogging.h>
|
||||||
#include <QtCore/qobjectdefs.h>
|
#include <QtCore/qobjectdefs.h>
|
||||||
#include <QtCore/qprocess.h>
|
#include <QtCore/qprocess.h>
|
||||||
@@ -45,17 +43,7 @@ XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) :
|
|||||||
if (xrayConfiguration.isEmpty()) {
|
if (xrayConfiguration.isEmpty()) {
|
||||||
xrayConfiguration = configuration.value(ProtocolUtils::key_proto_config_data(Proto::SSXray)).toObject();
|
xrayConfiguration = configuration.value(ProtocolUtils::key_proto_config_data(Proto::SSXray)).toObject();
|
||||||
}
|
}
|
||||||
|
m_xrayConfig = xrayConfiguration;
|
||||||
if (xrayConfiguration.isEmpty()) {
|
|
||||||
qWarning() << "Xray config wrapper is empty";
|
|
||||||
m_xrayConfig = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
m_xrayConfig = QJsonDocument::fromJson(xrayConfiguration.value(amnezia::configKey::config).toString().toUtf8()).object();
|
|
||||||
if (m_xrayConfig.isEmpty()) {
|
|
||||||
qWarning() << "Xray config string is not a valid JSON object";
|
|
||||||
m_xrayConfig = {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
XrayProtocol::~XrayProtocol()
|
XrayProtocol::~XrayProtocol()
|
||||||
@@ -81,39 +69,22 @@ ErrorCode XrayProtocol::start()
|
|||||||
m_socksPassword = creds.password;
|
m_socksPassword = creds.password;
|
||||||
m_socksPort = creds.port;
|
m_socksPort = creds.port;
|
||||||
|
|
||||||
QString xrayConfigStr = QJsonDocument(m_xrayConfig).toJson(QJsonDocument::Compact);
|
const QString xrayConfigStr = QJsonDocument(m_xrayConfig).toJson(QJsonDocument::Compact);
|
||||||
if (xrayConfigStr.isEmpty()) {
|
if (xrayConfigStr.isEmpty()) {
|
||||||
qCritical() << "Xray config is empty";
|
qCritical() << "Xray config is empty";
|
||||||
return ErrorCode::XrayExecutableCrashed;
|
return ErrorCode::XrayExecutableCrashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix fingerprint: old configs may contain "Mozilla/5.0" which xray-core rejects.
|
return IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||||
// Replace with the correct default at runtime so stale stored configs still work.
|
|
||||||
if (xrayConfigStr.contains("Mozilla/5.0", Qt::CaseInsensitive)) {
|
|
||||||
xrayConfigStr.replace("Mozilla/5.0", amnezia::protocols::xray::defaultFingerprint,
|
|
||||||
Qt::CaseInsensitive);
|
|
||||||
qDebug() << "XrayProtocol: patched legacy fingerprint to"
|
|
||||||
<< amnezia::protocols::xray::defaultFingerprint;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix inbound listen address: old configs may use "10.33.0.2" which doesn't exist
|
|
||||||
// until TUN is created. xray must listen on 127.0.0.1 so tun2socks can connect.
|
|
||||||
if (xrayConfigStr.contains(amnezia::protocols::xray::defaultLocalAddr)) {
|
|
||||||
xrayConfigStr.replace(amnezia::protocols::xray::defaultLocalAddr,
|
|
||||||
amnezia::protocols::xray::defaultLocalListenAddr);
|
|
||||||
qDebug() << "XrayProtocol: patched legacy inbound listen address to 127.0.0.1";
|
|
||||||
}
|
|
||||||
|
|
||||||
return IpcClient::withInterface(
|
|
||||||
[&](QSharedPointer<IpcInterfaceReplica> iface) {
|
|
||||||
auto xrayStart = iface->xrayStart(xrayConfigStr);
|
auto xrayStart = iface->xrayStart(xrayConfigStr);
|
||||||
if (!xrayStart.waitForFinished() || !xrayStart.returnValue()) {
|
if (!xrayStart.waitForFinished() || !xrayStart.returnValue()) {
|
||||||
qCritical() << "Failed to start xray";
|
qCritical() << "Failed to start xray";
|
||||||
return ErrorCode::XrayExecutableCrashed;
|
return ErrorCode::XrayExecutableCrashed;
|
||||||
}
|
}
|
||||||
return startTun2Socks();
|
return startTun2Socks();
|
||||||
},
|
}, [] () {
|
||||||
[]() { return ErrorCode::AmneziaServiceConnectionFailed; });
|
return ErrorCode::AmneziaServiceConnectionFailed;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void XrayProtocol::stop()
|
void XrayProtocol::stop()
|
||||||
@@ -172,21 +143,20 @@ ErrorCode XrayProtocol::startTun2Socks()
|
|||||||
return ErrorCode::AmneziaServiceConnectionFailed;
|
return ErrorCode::AmneziaServiceConnectionFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString proxyUrl = QString("socks5://%1:%2@127.0.0.1:%3").arg(m_socksUser, m_socksPassword, QString::number(m_socksPort));
|
const QString proxyUrl = QString("socks5://%1:%2@127.0.0.1:%3")
|
||||||
|
.arg(m_socksUser, m_socksPassword, QString::number(m_socksPort));
|
||||||
|
|
||||||
m_tun2socksProcess->setProgram(PermittedProcess::Tun2Socks);
|
m_tun2socksProcess->setProgram(PermittedProcess::Tun2Socks);
|
||||||
m_tun2socksProcess->setArguments({"-device", QString("tun://%1").arg(tunName), "-proxy", proxyUrl});
|
m_tun2socksProcess->setArguments({"-device", QString("tun://%1").arg(tunName), "-proxy", proxyUrl});
|
||||||
|
|
||||||
connect(
|
connect(m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::readyReadStandardOutput, this, [this]() {
|
||||||
m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::readyReadStandardError, this,
|
auto readAllStandardOutput = m_tun2socksProcess->readAllStandardOutput();
|
||||||
[this]() {
|
if (!readAllStandardOutput.waitForFinished()) {
|
||||||
auto readAllStandardError = m_tun2socksProcess->readAllStandardError();
|
|
||||||
if (!readAllStandardError.waitForFinished()) {
|
|
||||||
qWarning() << "Failed to read output from tun2socks";
|
qWarning() << "Failed to read output from tun2socks";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString line = readAllStandardError.returnValue();
|
const QString line = readAllStandardOutput.returnValue();
|
||||||
|
|
||||||
if (!line.contains("[TCP]") && !line.contains("[UDP]"))
|
if (!line.contains("[TCP]") && !line.contains("[UDP]"))
|
||||||
qDebug() << "[tun2socks]:" << line;
|
qDebug() << "[tun2socks]:" << line;
|
||||||
@@ -201,39 +171,9 @@ ErrorCode XrayProtocol::startTun2Socks()
|
|||||||
setConnectionState(Vpn::ConnectionState::Connected);
|
setConnectionState(Vpn::ConnectionState::Connected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}, Qt::QueuedConnection);
|
||||||
Qt::QueuedConnection);
|
|
||||||
|
|
||||||
connect(
|
|
||||||
m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::finished, this,
|
|
||||||
[this](int exitCode, QProcess::ExitStatus exitStatus) {
|
|
||||||
// Check stdout for "resource busy" — the TUN device was not yet released
|
|
||||||
// by the previous tun2socks instance. Retry after a short delay.
|
|
||||||
bool resourceBusy = false;
|
|
||||||
if (m_tun2socksProcess) {
|
|
||||||
auto readOut = m_tun2socksProcess->readAllStandardOutput();
|
|
||||||
if (readOut.waitForFinished()) {
|
|
||||||
resourceBusy = readOut.returnValue().contains("resource busy");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resourceBusy && m_tun2socksRetryCount < maxTun2SocksRetries) {
|
|
||||||
m_tun2socksRetryCount++;
|
|
||||||
qWarning() << QString("Tun2socks: TUN resource busy, retrying (%1/%2) in %3ms...")
|
|
||||||
.arg(m_tun2socksRetryCount)
|
|
||||||
.arg(maxTun2SocksRetries)
|
|
||||||
.arg(tun2socksRetryDelayMs);
|
|
||||||
QTimer::singleShot(tun2socksRetryDelayMs, this, [this]() {
|
|
||||||
if (ErrorCode err = startTun2Socks(); err != ErrorCode::NoError) {
|
|
||||||
stop();
|
|
||||||
setLastError(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_tun2socksRetryCount = 0;
|
|
||||||
|
|
||||||
|
connect(m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::finished, this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||||
if (exitStatus == QProcess::ExitStatus::CrashExit) {
|
if (exitStatus == QProcess::ExitStatus::CrashExit) {
|
||||||
qCritical() << "Tun2socks process crashed!";
|
qCritical() << "Tun2socks process crashed!";
|
||||||
} else {
|
} else {
|
||||||
@@ -241,17 +181,14 @@ ErrorCode XrayProtocol::startTun2Socks()
|
|||||||
}
|
}
|
||||||
stop();
|
stop();
|
||||||
setLastError(ErrorCode::Tun2SockExecutableCrashed);
|
setLastError(ErrorCode::Tun2SockExecutableCrashed);
|
||||||
},
|
}, Qt::QueuedConnection);
|
||||||
Qt::QueuedConnection);
|
|
||||||
|
|
||||||
m_tun2socksProcess->start();
|
m_tun2socksProcess->start();
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode XrayProtocol::setupRouting()
|
ErrorCode XrayProtocol::setupRouting() {
|
||||||
{
|
return IpcClient::withInterface([this](QSharedPointer<IpcInterfaceReplica> iface) -> ErrorCode {
|
||||||
return IpcClient::withInterface(
|
|
||||||
[this](QSharedPointer<IpcInterfaceReplica> iface) -> ErrorCode {
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
const int inetAdapterIndex = NetworkUtilities::AdapterIndexTo(QHostAddress(m_remoteAddress));
|
const int inetAdapterIndex = NetworkUtilities::AdapterIndexTo(QHostAddress(m_remoteAddress));
|
||||||
#endif
|
#endif
|
||||||
@@ -295,8 +232,7 @@ ErrorCode XrayProtocol::setupRouting()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_routeMode == amnezia::RouteMode::VpnAllSites) {
|
if (m_routeMode == amnezia::RouteMode::VpnAllSites) {
|
||||||
static const QStringList subnets = { "1.0.0.0/8", "2.0.0.0/7", "4.0.0.0/6", "8.0.0.0/5",
|
static const QStringList subnets = { "1.0.0.0/8", "2.0.0.0/7", "4.0.0.0/6", "8.0.0.0/5", "16.0.0.0/4", "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/1" };
|
||||||
"16.0.0.0/4", "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/1" };
|
|
||||||
|
|
||||||
auto routeAddList = iface->routeAddList(m_vpnGateway, subnets);
|
auto routeAddList = iface->routeAddList(m_vpnGateway, subnets);
|
||||||
if (!routeAddList.waitForFinished() || routeAddList.returnValue() != subnets.count()) {
|
if (!routeAddList.waitForFinished() || routeAddList.returnValue() != subnets.count()) {
|
||||||
@@ -329,5 +265,7 @@ ErrorCode XrayProtocol::setupRouting()
|
|||||||
#endif
|
#endif
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
},
|
},
|
||||||
[]() { return ErrorCode::AmneziaServiceConnectionFailed; });
|
[] () {
|
||||||
|
return ErrorCode::AmneziaServiceConnectionFailed;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,9 +35,6 @@ private:
|
|||||||
int m_socksPort = 10808;
|
int m_socksPort = 10808;
|
||||||
|
|
||||||
QSharedPointer<IpcProcessInterfaceReplica> m_tun2socksProcess;
|
QSharedPointer<IpcProcessInterfaceReplica> m_tun2socksProcess;
|
||||||
int m_tun2socksRetryCount = 0;
|
|
||||||
static constexpr int maxTun2SocksRetries = 5;
|
|
||||||
static constexpr int tun2socksRetryDelayMs = 400;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // XRAYPROTOCOL_H
|
#endif // XRAYPROTOCOL_H
|
||||||
|
|||||||
@@ -2,16 +2,15 @@
|
|||||||
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
|
||||||
#include "core/utils/errorCodes.h"
|
#include "core/utils/errorCodes.h"
|
||||||
#include "core/utils/routeModes.h"
|
#include "core/utils/routeModes.h"
|
||||||
#include "core/utils/commonStructs.h"
|
#include "core/utils/commonStructs.h"
|
||||||
#include "core/utils/serverConfigUtils.h"
|
#include "core/utils/api/apiEnums.h"
|
||||||
#include "core/utils/constants/apiKeys.h"
|
#include "core/utils/constants/apiKeys.h"
|
||||||
#include "core/utils/constants/apiConstants.h"
|
#include "core/utils/constants/apiConstants.h"
|
||||||
#include "core/utils/constants/configKeys.h"
|
#include "core/models/serverConfig.h"
|
||||||
#include "core/utils/networkUtilities.h"
|
#include "core/utils/networkUtilities.h"
|
||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
@@ -451,12 +450,4 @@ void SecureAppSettingsRepository::setInstallationUuid(const QString &uuid)
|
|||||||
m_settings->setValue("Conf/installationUuid", uuid);
|
m_settings->setValue("Conf/installationUuid", uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray SecureAppSettingsRepository::xraySavedConfigs() const
|
|
||||||
{
|
|
||||||
return value("Xray/savedConfigs").toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SecureAppSettingsRepository::setXraySavedConfigs(const QByteArray &data)
|
|
||||||
{
|
|
||||||
setValue("Xray/savedConfigs", data);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -92,9 +92,6 @@ public:
|
|||||||
|
|
||||||
QString nextAvailableServerName() const;
|
QString nextAvailableServerName() const;
|
||||||
|
|
||||||
QByteArray xraySavedConfigs() const;
|
|
||||||
void setXraySavedConfigs(const QByteArray &data);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void appLanguageChanged(QLocale locale);
|
void appLanguageChanged(QLocale locale);
|
||||||
void allowedDnsServersChanged(const QStringList &servers);
|
void allowedDnsServersChanged(const QStringList &servers);
|
||||||
|
|||||||
@@ -1,44 +1,26 @@
|
|||||||
#include "secureServersRepository.h"
|
#include "secureServersRepository.h"
|
||||||
|
|
||||||
#include <QJsonArray>
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonValue>
|
#include <QJsonArray>
|
||||||
#include <QUuid>
|
|
||||||
|
|
||||||
#include "core/utils/serverConfigUtils.h"
|
#include "core/utils/api/apiEnums.h"
|
||||||
#include "core/utils/constants/apiKeys.h"
|
#include "core/utils/constants/apiKeys.h"
|
||||||
|
#include "core/utils/constants/apiConstants.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
|
#include "core/models/containerConfig.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
#include "core/utils/constants/configKeys.h"
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
using namespace amnezia;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
QString readStorageServerId(const QJsonObject &json)
|
|
||||||
{
|
|
||||||
return json.value(QString(configKey::storageServerId)).toString().trimmed();
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject withoutStorageServerId(const QJsonObject &json)
|
|
||||||
{
|
|
||||||
QJsonObject o = json;
|
|
||||||
o.remove(QString(configKey::storageServerId));
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject embedStorageServerId(const QString &serverId, const QJsonObject &payloadSansId)
|
|
||||||
{
|
|
||||||
QJsonObject o = payloadSansId;
|
|
||||||
o.insert(QString(configKey::storageServerId), serverId);
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
SecureServersRepository::SecureServersRepository(SecureQSettings* settings, QObject *parent)
|
SecureServersRepository::SecureServersRepository(SecureQSettings* settings, QObject *parent)
|
||||||
: QObject(parent), m_settings(settings)
|
: QObject(parent), m_settings(settings)
|
||||||
{
|
{
|
||||||
loadFromStorage();
|
QJsonArray arr = QJsonDocument::fromJson(value("Servers/serversList").toByteArray()).array();
|
||||||
persistDefaultServerFields();
|
for (const QJsonValue &val : arr) {
|
||||||
|
m_servers.append(ServerConfig::fromJson(val.toObject()));
|
||||||
|
}
|
||||||
|
m_defaultServerIndex = value("Servers/defaultServerIndex", 0).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant SecureServersRepository::value(const QString &key, const QVariant &defaultValue) const
|
QVariant SecureServersRepository::value(const QString &key, const QVariant &defaultValue) const
|
||||||
@@ -51,322 +33,216 @@ void SecureServersRepository::setValue(const QString &key, const QVariant &value
|
|||||||
m_settings->setValue(key, value);
|
m_settings->setValue(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SecureServersRepository::clearServerStateMaps()
|
|
||||||
{
|
|
||||||
m_serverJsonById.clear();
|
|
||||||
m_orderedServerIds.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString SecureServersRepository::normalizedOrGeneratedServerId(const QString &candidateId) const
|
|
||||||
{
|
|
||||||
const QString trimmed = candidateId.trimmed();
|
|
||||||
if (!trimmed.isEmpty() && !m_serverJsonById.contains(trimmed)) {
|
|
||||||
return trimmed;
|
|
||||||
}
|
|
||||||
return QUuid::createUuid().toString(QUuid::WithoutBraces);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SecureServersRepository::updateDefaultServerFromStorage()
|
|
||||||
{
|
|
||||||
const QString storedDefaultId = value(QStringLiteral("Servers/defaultServerId"), QString()).toString();
|
|
||||||
if (!storedDefaultId.isEmpty() && m_serverJsonById.contains(storedDefaultId)) {
|
|
||||||
m_defaultServerId = storedDefaultId;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int storedDefaultIndex = value("Servers/defaultServerIndex", 0).toInt();
|
|
||||||
if (storedDefaultIndex >= 0 && storedDefaultIndex < m_orderedServerIds.size()) {
|
|
||||||
m_defaultServerId = m_orderedServerIds.at(storedDefaultIndex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_orderedServerIds.isEmpty()) {
|
|
||||||
m_defaultServerId = m_orderedServerIds.first();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_defaultServerId.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SecureServersRepository::persistDefaultServerFields()
|
|
||||||
{
|
|
||||||
if (m_orderedServerIds.isEmpty()) {
|
|
||||||
m_defaultServerId.clear();
|
|
||||||
} else if (!m_orderedServerIds.contains(m_defaultServerId)) {
|
|
||||||
m_defaultServerId = m_orderedServerIds.first();
|
|
||||||
}
|
|
||||||
|
|
||||||
setValue("Servers/defaultServerId", m_defaultServerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SecureServersRepository::loadFromStorage()
|
|
||||||
{
|
|
||||||
clearServerStateMaps();
|
|
||||||
|
|
||||||
const QJsonArray serversArray =
|
|
||||||
QJsonDocument::fromJson(value(QStringLiteral("Servers/serversList"), QByteArray()).toByteArray())
|
|
||||||
.array();
|
|
||||||
|
|
||||||
for (int i = 0; i < serversArray.size(); ++i) {
|
|
||||||
const QJsonObject json = serversArray.at(i).toObject();
|
|
||||||
const QString candidateId = readStorageServerId(json);
|
|
||||||
const QString serverId = normalizedOrGeneratedServerId(candidateId);
|
|
||||||
const QJsonObject strippedJson = withoutStorageServerId(json);
|
|
||||||
const serverConfigUtils::ConfigType kind = serverConfigUtils::configTypeFromJson(strippedJson);
|
|
||||||
|
|
||||||
if (m_serverJsonById.contains(serverId) || kind == serverConfigUtils::ConfigType::Invalid) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
m_serverJsonById.insert(serverId, embedStorageServerId(serverId, strippedJson));
|
|
||||||
m_orderedServerIds.append(serverId);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDefaultServerFromStorage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SecureServersRepository::syncToStorage()
|
void SecureServersRepository::syncToStorage()
|
||||||
{
|
{
|
||||||
QJsonArray serversArray;
|
QJsonArray arr;
|
||||||
|
for (const ServerConfig &cfg : m_servers) {
|
||||||
for (const QString &serverId : m_orderedServerIds) {
|
arr.append(cfg.toJson());
|
||||||
if (!m_serverJsonById.contains(serverId)) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
serversArray.append(m_serverJsonById.value(serverId));
|
setValue("Servers/serversList", QJsonDocument(arr).toJson());
|
||||||
}
|
|
||||||
|
|
||||||
setValue("Servers/serversList", QJsonDocument(serversArray).toJson());
|
|
||||||
persistDefaultServerFields();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SecureServersRepository::invalidateCache()
|
void SecureServersRepository::invalidateCache()
|
||||||
{
|
{
|
||||||
loadFromStorage();
|
m_servers.clear();
|
||||||
|
QJsonArray arr = QJsonDocument::fromJson(value("Servers/serversList").toByteArray()).array();
|
||||||
|
for (const QJsonValue &val : arr) {
|
||||||
|
m_servers.append(ServerConfig::fromJson(val.toObject()));
|
||||||
|
}
|
||||||
|
m_defaultServerIndex = value("Servers/defaultServerIndex", 0).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SecureServersRepository::clearServers()
|
void SecureServersRepository::setServersArray(const QJsonArray &servers)
|
||||||
{
|
{
|
||||||
clearServerStateMaps();
|
m_servers.clear();
|
||||||
|
for (const QJsonValue &val : servers) {
|
||||||
m_defaultServerId.clear();
|
m_servers.append(ServerConfig::fromJson(val.toObject()));
|
||||||
|
}
|
||||||
syncToStorage();
|
syncToStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SecureServersRepository::addServer(const QString &serverId, const QJsonObject &serverJson, serverConfigUtils::ConfigType kind)
|
void SecureServersRepository::addServer(const ServerConfig &server)
|
||||||
{
|
{
|
||||||
const QString id = normalizedOrGeneratedServerId(serverId);
|
m_servers.append(server);
|
||||||
if (m_serverJsonById.contains(id) || kind == serverConfigUtils::ConfigType::Invalid) {
|
syncToStorage();
|
||||||
return id;
|
emit serverAdded(server);
|
||||||
}
|
}
|
||||||
const QJsonObject strippedJson = withoutStorageServerId(serverJson);
|
|
||||||
if (serverConfigUtils::configTypeFromJson(strippedJson) != kind) {
|
void SecureServersRepository::editServer(int index, const ServerConfig &server)
|
||||||
return id;
|
{
|
||||||
|
if (index < 0 || index >= m_servers.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_servers.replace(index, server);
|
||||||
|
syncToStorage();
|
||||||
|
emit serverEdited(index, server);
|
||||||
}
|
}
|
||||||
m_serverJsonById.insert(id, embedStorageServerId(id, strippedJson));
|
|
||||||
|
|
||||||
m_orderedServerIds.append(id);
|
void SecureServersRepository::removeServer(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= m_servers.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int defaultIndex = m_defaultServerIndex;
|
||||||
|
m_servers.removeAt(index);
|
||||||
|
|
||||||
if (m_defaultServerId.isEmpty()) {
|
if (defaultIndex == index) {
|
||||||
m_defaultServerId = id;
|
setDefaultServer(0);
|
||||||
|
} else if (defaultIndex > index) {
|
||||||
|
setDefaultServer(defaultIndex - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_servers.isEmpty()) {
|
||||||
|
setDefaultServer(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
syncToStorage();
|
syncToStorage();
|
||||||
emit serverAdded(id);
|
emit serverRemoved(index);
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SecureServersRepository::editServer(const QString &serverId, const QJsonObject &serverJson, serverConfigUtils::ConfigType kind)
|
ServerConfig SecureServersRepository::server(int index) const
|
||||||
{
|
{
|
||||||
if (indexOfServerId(serverId) < 0 || kind == serverConfigUtils::ConfigType::Invalid) {
|
if (index < 0 || index >= m_servers.size()) {
|
||||||
return;
|
return SelfHostedServerConfig{};
|
||||||
}
|
}
|
||||||
if (!m_serverJsonById.contains(serverId)) {
|
return m_servers.at(index);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QJsonObject oldJson = m_serverJsonById.value(serverId);
|
QVector<ServerConfig> SecureServersRepository::servers() const
|
||||||
const serverConfigUtils::ConfigType oldKind = serverConfigUtils::configTypeFromJson(withoutStorageServerId(oldJson));
|
|
||||||
|
|
||||||
m_serverJsonById.remove(serverId);
|
|
||||||
|
|
||||||
const QJsonObject strippedNew = withoutStorageServerId(serverJson);
|
|
||||||
if (serverConfigUtils::configTypeFromJson(strippedNew) != kind) {
|
|
||||||
const QJsonObject strippedOld = withoutStorageServerId(oldJson);
|
|
||||||
if (oldKind != serverConfigUtils::ConfigType::Invalid && serverConfigUtils::configTypeFromJson(strippedOld) == oldKind) {
|
|
||||||
m_serverJsonById.insert(serverId, embedStorageServerId(serverId, strippedOld));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_serverJsonById.insert(serverId, embedStorageServerId(serverId, strippedNew));
|
|
||||||
|
|
||||||
syncToStorage();
|
|
||||||
emit serverEdited(serverId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SecureServersRepository::removeServer(const QString &serverId)
|
|
||||||
{
|
{
|
||||||
const int removedIndex = indexOfServerId(serverId);
|
return m_servers;
|
||||||
if (removedIndex < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!m_serverJsonById.contains(serverId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString previousDefaultId = m_defaultServerId;
|
|
||||||
const int previousDefaultIndex = defaultServerIndex();
|
|
||||||
|
|
||||||
m_serverJsonById.remove(serverId);
|
|
||||||
m_orderedServerIds.removeAt(removedIndex);
|
|
||||||
|
|
||||||
if (m_orderedServerIds.isEmpty()) {
|
|
||||||
m_defaultServerId.clear();
|
|
||||||
} else if (m_defaultServerId == serverId) {
|
|
||||||
const int fallbackIndex = qMin(removedIndex, m_orderedServerIds.size() - 1);
|
|
||||||
m_defaultServerId = m_orderedServerIds.at(fallbackIndex);
|
|
||||||
} else if (!m_orderedServerIds.contains(m_defaultServerId)) {
|
|
||||||
m_defaultServerId = m_orderedServerIds.first();
|
|
||||||
}
|
|
||||||
|
|
||||||
const int newDefaultIndex = defaultServerIndex();
|
|
||||||
if (previousDefaultId != m_defaultServerId || previousDefaultIndex != newDefaultIndex) {
|
|
||||||
emit defaultServerChanged(m_defaultServerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
syncToStorage();
|
|
||||||
emit serverRemoved(serverId, removedIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
serverConfigUtils::ConfigType SecureServersRepository::serverKind(const QString &serverId) const
|
|
||||||
{
|
|
||||||
const auto it = m_serverJsonById.constFind(serverId);
|
|
||||||
if (it == m_serverJsonById.constEnd()) {
|
|
||||||
return serverConfigUtils::ConfigType::Invalid;
|
|
||||||
}
|
|
||||||
return serverConfigUtils::configTypeFromJson(withoutStorageServerId(it.value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<SelfHostedAdminServerConfig> SecureServersRepository::selfHostedAdminConfig(const QString &serverId) const
|
|
||||||
{
|
|
||||||
const auto it = m_serverJsonById.constFind(serverId);
|
|
||||||
if (it == m_serverJsonById.constEnd()) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
const QJsonObject strippedJson = withoutStorageServerId(it.value());
|
|
||||||
if (serverConfigUtils::configTypeFromJson(strippedJson) != serverConfigUtils::ConfigType::SelfHostedAdmin) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return SelfHostedAdminServerConfig::fromJson(strippedJson);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<SelfHostedUserServerConfig> SecureServersRepository::selfHostedUserConfig(const QString &serverId) const
|
|
||||||
{
|
|
||||||
const auto it = m_serverJsonById.constFind(serverId);
|
|
||||||
if (it == m_serverJsonById.constEnd()) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
const QJsonObject strippedJson = withoutStorageServerId(it.value());
|
|
||||||
if (serverConfigUtils::configTypeFromJson(strippedJson) != serverConfigUtils::ConfigType::SelfHostedUser) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return SelfHostedUserServerConfig::fromJson(strippedJson);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<NativeServerConfig> SecureServersRepository::nativeConfig(const QString &serverId) const
|
|
||||||
{
|
|
||||||
const auto it = m_serverJsonById.constFind(serverId);
|
|
||||||
if (it == m_serverJsonById.constEnd()) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
const QJsonObject strippedJson = withoutStorageServerId(it.value());
|
|
||||||
if (serverConfigUtils::configTypeFromJson(strippedJson) != serverConfigUtils::ConfigType::Native) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return NativeServerConfig::fromJson(strippedJson);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<ApiV2ServerConfig> SecureServersRepository::apiV2Config(const QString &serverId) const
|
|
||||||
{
|
|
||||||
const auto it = m_serverJsonById.constFind(serverId);
|
|
||||||
if (it == m_serverJsonById.constEnd()) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
const QJsonObject strippedJson = withoutStorageServerId(it.value());
|
|
||||||
if (!serverConfigUtils::isApiV2Subscription(serverConfigUtils::configTypeFromJson(strippedJson))) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return ApiV2ServerConfig::fromJson(strippedJson);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<LegacyApiServerConfig> SecureServersRepository::legacyApiConfig(const QString &serverId) const
|
|
||||||
{
|
|
||||||
const auto it = m_serverJsonById.constFind(serverId);
|
|
||||||
if (it == m_serverJsonById.constEnd()) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
const QJsonObject strippedJson = withoutStorageServerId(it.value());
|
|
||||||
if (!serverConfigUtils::isLegacyApiSubscription(serverConfigUtils::configTypeFromJson(strippedJson))) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return LegacyApiServerConfig::fromJson(strippedJson);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int SecureServersRepository::serversCount() const
|
int SecureServersRepository::serversCount() const
|
||||||
{
|
{
|
||||||
return m_orderedServerIds.size();
|
return m_servers.size();
|
||||||
}
|
|
||||||
|
|
||||||
QString SecureServersRepository::serverIdAt(int index) const
|
|
||||||
{
|
|
||||||
if (index < 0 || index >= m_orderedServerIds.size()) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
return m_orderedServerIds.at(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<QString> SecureServersRepository::orderedServerIds() const
|
|
||||||
{
|
|
||||||
return m_orderedServerIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SecureServersRepository::indexOfServerId(const QString &serverId) const
|
|
||||||
{
|
|
||||||
return m_orderedServerIds.indexOf(serverId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int SecureServersRepository::defaultServerIndex() const
|
int SecureServersRepository::defaultServerIndex() const
|
||||||
{
|
{
|
||||||
if (m_orderedServerIds.isEmpty()) {
|
return m_defaultServerIndex;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const int idx = m_orderedServerIds.indexOf(m_defaultServerId);
|
|
||||||
return idx >= 0 ? idx : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SecureServersRepository::defaultServerId() const
|
void SecureServersRepository::setDefaultServer(int index)
|
||||||
{
|
{
|
||||||
return m_defaultServerId;
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_servers.size() > 0 && index >= m_servers.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_servers.isEmpty() && index != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_defaultServerIndex == index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_defaultServerIndex = index;
|
||||||
|
setValue("Servers/defaultServerIndex", index);
|
||||||
|
emit defaultServerChanged(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SecureServersRepository::setDefaultServer(const QString &serverId)
|
void SecureServersRepository::setDefaultContainer(int serverIndex, DockerContainer container)
|
||||||
{
|
{
|
||||||
if (m_orderedServerIds.isEmpty()) {
|
ServerConfig config = server(serverIndex);
|
||||||
return;
|
config.visit([container](auto& arg) {
|
||||||
}
|
arg.defaultContainer = container;
|
||||||
if (!m_serverJsonById.contains(serverId)) {
|
});
|
||||||
return;
|
editServer(serverIndex, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (indexOfServerId(serverId) < 0) {
|
ContainerConfig SecureServersRepository::containerConfig(int serverIndex, DockerContainer container) const
|
||||||
return;
|
{
|
||||||
|
ServerConfig config = server(serverIndex);
|
||||||
|
return config.containerConfig(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_defaultServerId == serverId) {
|
void SecureServersRepository::setContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config)
|
||||||
return;
|
{
|
||||||
|
ServerConfig serverConfig = server(serverIndex);
|
||||||
|
serverConfig.visit([container, &config](auto& arg) {
|
||||||
|
arg.containers[container] = config;
|
||||||
|
});
|
||||||
|
editServer(serverIndex, serverConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_defaultServerId = serverId;
|
void SecureServersRepository::clearLastConnectionConfig(int serverIndex, DockerContainer container)
|
||||||
persistDefaultServerFields();
|
{
|
||||||
emit defaultServerChanged(m_defaultServerId);
|
ServerConfig serverConfig = server(serverIndex);
|
||||||
|
ContainerConfig containerCfg = serverConfig.containerConfig(container);
|
||||||
|
|
||||||
|
containerCfg.protocolConfig.clearClientConfig();
|
||||||
|
|
||||||
|
setContainerConfig(serverIndex, container, containerCfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerCredentials SecureServersRepository::serverCredentials(int index) const
|
||||||
|
{
|
||||||
|
ServerConfig config = server(index);
|
||||||
|
|
||||||
|
if (config.isSelfHosted()) {
|
||||||
|
const SelfHostedServerConfig* selfHosted = config.as<SelfHostedServerConfig>();
|
||||||
|
if (!selfHosted) return ServerCredentials();
|
||||||
|
auto creds = selfHosted->credentials();
|
||||||
|
if (creds.has_value()) {
|
||||||
|
return creds.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ServerCredentials{};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SecureServersRepository::hasServerWithVpnKey(const QString &vpnKey) const
|
||||||
|
{
|
||||||
|
QString normalizedInput = vpnKey.trimmed();
|
||||||
|
if (normalizedInput.startsWith(QStringLiteral("vpn://"), Qt::CaseInsensitive)) {
|
||||||
|
normalizedInput = normalizedInput.mid(QStringLiteral("vpn://").size());
|
||||||
|
}
|
||||||
|
if (normalizedInput.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<ServerConfig> serversList = servers();
|
||||||
|
for (const ServerConfig& serverConfig : serversList) {
|
||||||
|
if (serverConfig.isApiV1()) {
|
||||||
|
const ApiV1ServerConfig* apiV1 = serverConfig.as<ApiV1ServerConfig>();
|
||||||
|
if (!apiV1) continue;
|
||||||
|
QString storedKey = apiV1->vpnKey();
|
||||||
|
if (storedKey.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QString normalizedStored = storedKey.trimmed();
|
||||||
|
if (normalizedStored.startsWith(QStringLiteral("vpn://"), Qt::CaseInsensitive)) {
|
||||||
|
normalizedStored = normalizedStored.mid(QStringLiteral("vpn://").size());
|
||||||
|
}
|
||||||
|
if (normalizedInput == normalizedStored) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (serverConfig.isApiV2()) {
|
||||||
|
const ApiV2ServerConfig* apiV2 = serverConfig.as<ApiV2ServerConfig>();
|
||||||
|
if (!apiV2) continue;
|
||||||
|
QString storedKey = apiV2->vpnKey();
|
||||||
|
if (storedKey.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QString normalizedStored = storedKey.trimmed();
|
||||||
|
if (normalizedStored.startsWith(QStringLiteral("vpn://"), Qt::CaseInsensitive)) {
|
||||||
|
normalizedStored = normalizedStored.mid(QStringLiteral("vpn://").size());
|
||||||
|
}
|
||||||
|
if (normalizedInput == normalizedStored) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SecureServersRepository::hasServerWithCrc(quint16 crc) const
|
||||||
|
{
|
||||||
|
for (const ServerConfig& serverConfig : m_servers) {
|
||||||
|
if (static_cast<quint16>(serverConfig.crc()) == crc) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,14 @@
|
|||||||
#ifndef SECURESERVERSREPOSITORY_H
|
#ifndef SECURESERVERSREPOSITORY_H
|
||||||
#define SECURESERVERSREPOSITORY_H
|
#define SECURESERVERSREPOSITORY_H
|
||||||
|
|
||||||
#include <QHash>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include "core/models/selfhosted/selfHostedAdminServerConfig.h"
|
#include "core/models/serverConfig.h"
|
||||||
#include "core/models/selfhosted/selfHostedUserServerConfig.h"
|
|
||||||
#include "core/models/selfhosted/nativeServerConfig.h"
|
|
||||||
#include "core/models/api/apiV2ServerConfig.h"
|
|
||||||
#include "core/models/api/legacyApiServerConfig.h"
|
|
||||||
#include "core/models/containerConfig.h"
|
#include "core/models/containerConfig.h"
|
||||||
#include "core/utils/serverConfigUtils.h"
|
|
||||||
#include "secureQSettings.h"
|
#include "secureQSettings.h"
|
||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
@@ -26,55 +20,45 @@ class SecureServersRepository : public QObject
|
|||||||
public:
|
public:
|
||||||
explicit SecureServersRepository(SecureQSettings* settings, QObject *parent = nullptr);
|
explicit SecureServersRepository(SecureQSettings* settings, QObject *parent = nullptr);
|
||||||
|
|
||||||
QString addServer(const QString &serverId, const QJsonObject &serverJson, serverConfigUtils::ConfigType kind);
|
void addServer(const ServerConfig &server);
|
||||||
void editServer(const QString &serverId, const QJsonObject &serverJson, serverConfigUtils::ConfigType kind);
|
void editServer(int index, const ServerConfig &server);
|
||||||
void removeServer(const QString &serverId);
|
void removeServer(int index);
|
||||||
serverConfigUtils::ConfigType serverKind(const QString &serverId) const;
|
ServerConfig server(int index) const;
|
||||||
|
QVector<ServerConfig> servers() const;
|
||||||
std::optional<SelfHostedAdminServerConfig> selfHostedAdminConfig(const QString &serverId) const;
|
|
||||||
std::optional<SelfHostedUserServerConfig> selfHostedUserConfig(const QString &serverId) const;
|
|
||||||
std::optional<NativeServerConfig> nativeConfig(const QString &serverId) const;
|
|
||||||
std::optional<ApiV2ServerConfig> apiV2Config(const QString &serverId) const;
|
|
||||||
std::optional<LegacyApiServerConfig> legacyApiConfig(const QString &serverId) const;
|
|
||||||
|
|
||||||
int serversCount() const;
|
int serversCount() const;
|
||||||
int indexOfServerId(const QString &serverId) const;
|
|
||||||
QString serverIdAt(int index) const;
|
|
||||||
QVector<QString> orderedServerIds() const;
|
|
||||||
|
|
||||||
int defaultServerIndex() const;
|
int defaultServerIndex() const;
|
||||||
QString defaultServerId() const;
|
void setDefaultServer(int index);
|
||||||
void setDefaultServer(const QString &serverId);
|
|
||||||
|
|
||||||
void clearServers();
|
void setDefaultContainer(int serverIndex, DockerContainer container);
|
||||||
|
ContainerConfig containerConfig(int serverIndex, DockerContainer container) const;
|
||||||
|
void setContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config);
|
||||||
|
void clearLastConnectionConfig(int serverIndex, DockerContainer container);
|
||||||
|
|
||||||
|
ServerCredentials serverCredentials(int index) const;
|
||||||
|
bool hasServerWithVpnKey(const QString &vpnKey) const;
|
||||||
|
bool hasServerWithCrc(quint16 crc) const;
|
||||||
|
|
||||||
|
void setServersArray(const QJsonArray &servers);
|
||||||
|
|
||||||
void invalidateCache();
|
void invalidateCache();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void serverAdded(const QString &serverId);
|
void serverAdded(ServerConfig config);
|
||||||
void serverEdited(const QString &serverId);
|
void serverEdited(int index, ServerConfig config);
|
||||||
void serverRemoved(const QString &serverId, int removedIndex);
|
void serverRemoved(int index);
|
||||||
void defaultServerChanged(const QString &defaultServerId);
|
void defaultServerChanged(int index);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadFromStorage();
|
|
||||||
void updateDefaultServerFromStorage();
|
|
||||||
void persistDefaultServerFields();
|
|
||||||
|
|
||||||
QString normalizedOrGeneratedServerId(const QString &candidateId) const;
|
|
||||||
|
|
||||||
void syncToStorage();
|
void syncToStorage();
|
||||||
QVariant value(const QString &key, const QVariant &defaultValue) const;
|
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
|
||||||
void setValue(const QString &key, const QVariant &value);
|
void setValue(const QString &key, const QVariant &value);
|
||||||
|
|
||||||
void clearServerStateMaps();
|
|
||||||
|
|
||||||
SecureQSettings* m_settings;
|
SecureQSettings* m_settings;
|
||||||
|
|
||||||
QHash<QString, QJsonObject> m_serverJsonById;
|
QVector<ServerConfig> m_servers;
|
||||||
QVector<QString> m_orderedServerIds;
|
int m_defaultServerIndex = 0;
|
||||||
|
|
||||||
QString m_defaultServerId;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SECURESERVERSREPOSITORY_H
|
#endif // SECURESERVERSREPOSITORY_H
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef APIENUMS_H
|
||||||
|
#define APIENUMS_H
|
||||||
|
|
||||||
|
namespace apiDefs
|
||||||
|
{
|
||||||
|
enum ConfigType {
|
||||||
|
AmneziaFreeV2 = 0,
|
||||||
|
AmneziaFreeV3,
|
||||||
|
AmneziaPremiumV1,
|
||||||
|
AmneziaPremiumV2,
|
||||||
|
AmneziaTrialV2,
|
||||||
|
SelfHosted,
|
||||||
|
ExternalPremium,
|
||||||
|
ExternalTrial
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ConfigSource {
|
||||||
|
Telegram = 1,
|
||||||
|
AmneziaGateway
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // APIENUMS_H
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "apiUtils.h"
|
#include "apiUtils.h"
|
||||||
|
|
||||||
#include "core/utils/serverConfigUtils.h"
|
|
||||||
#include "core/utils/constants/configKeys.h"
|
#include "core/utils/constants/configKeys.h"
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
@@ -76,6 +75,63 @@ bool apiUtils::isSubscriptionExpiringSoon(const QString &subscriptionEndDate, in
|
|||||||
return endDate <= nowUtc.addDays(withinDays);
|
return endDate <= nowUtc.addDays(withinDays);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool apiUtils::isServerFromApi(const QJsonObject &serverConfigObject)
|
||||||
|
{
|
||||||
|
auto configVersion = serverConfigObject.value(configKey::configVersion).toInt();
|
||||||
|
switch (configVersion) {
|
||||||
|
case apiDefs::ConfigSource::Telegram: return true;
|
||||||
|
case apiDefs::ConfigSource::AmneziaGateway: return true;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObject)
|
||||||
|
{
|
||||||
|
auto configVersion = serverConfigObject.value(configKey::configVersion).toInt();
|
||||||
|
|
||||||
|
switch (configVersion) {
|
||||||
|
case apiDefs::ConfigSource::Telegram: {
|
||||||
|
constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT);
|
||||||
|
constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT);
|
||||||
|
|
||||||
|
auto apiEndpoint = serverConfigObject.value(apiDefs::key::apiEndpoint).toString();
|
||||||
|
|
||||||
|
if (apiEndpoint.contains(premiumV1Endpoint)) {
|
||||||
|
return apiDefs::ConfigType::AmneziaPremiumV1;
|
||||||
|
} else if (apiEndpoint.contains(freeV2Endpoint)) {
|
||||||
|
return apiDefs::ConfigType::AmneziaFreeV2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
case apiDefs::ConfigSource::AmneziaGateway: {
|
||||||
|
constexpr QLatin1String servicePremium("amnezia-premium");
|
||||||
|
constexpr QLatin1String serviceFree("amnezia-free");
|
||||||
|
constexpr QLatin1String serviceExternalPremium("external-premium");
|
||||||
|
constexpr QLatin1String serviceExternalTrial("external-trial");
|
||||||
|
|
||||||
|
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;
|
||||||
|
} else if (serviceType == serviceExternalTrial) {
|
||||||
|
return apiDefs::ConfigType::ExternalTrial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return apiDefs::ConfigType::SelfHosted;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
apiDefs::ConfigSource apiUtils::getConfigSource(const QJsonObject &serverConfigObject)
|
||||||
|
{
|
||||||
|
return static_cast<apiDefs::ConfigSource>(serverConfigObject.value(configKey::configVersion).toInt());
|
||||||
|
}
|
||||||
|
|
||||||
amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &sslErrors, const QString &replyErrorString,
|
amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &sslErrors, const QString &replyErrorString,
|
||||||
const QNetworkReply::NetworkError &replyError, const int httpStatusCode,
|
const QNetworkReply::NetworkError &replyError, const int httpStatusCode,
|
||||||
const QByteArray &responseBody)
|
const QByteArray &responseBody)
|
||||||
@@ -103,6 +159,10 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
|
|||||||
return amnezia::ErrorCode::ApiUpdateRequestError;
|
return amnezia::ErrorCode::ApiUpdateRequestError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qDebug() << QString::fromUtf8(responseBody);
|
||||||
|
qDebug() << replyError;
|
||||||
|
qDebug() << httpStatusCode;
|
||||||
|
|
||||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseBody);
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseBody);
|
||||||
if (jsonDoc.isObject()) {
|
if (jsonDoc.isObject()) {
|
||||||
QJsonObject jsonObj = jsonDoc.object();
|
QJsonObject jsonObj = jsonDoc.object();
|
||||||
@@ -137,14 +197,14 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
|
|||||||
|
|
||||||
bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject)
|
bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject)
|
||||||
{
|
{
|
||||||
static const QSet<serverConfigUtils::ConfigType> premiumTypes = { serverConfigUtils::ConfigType::AmneziaPremiumV1, serverConfigUtils::ConfigType::AmneziaPremiumV2,
|
static const QSet<apiDefs::ConfigType> premiumTypes = { apiDefs::ConfigType::AmneziaPremiumV1, apiDefs::ConfigType::AmneziaPremiumV2,
|
||||||
serverConfigUtils::ConfigType::ExternalPremium };
|
apiDefs::ConfigType::ExternalPremium, apiDefs::ConfigType::ExternalTrial };
|
||||||
return premiumTypes.contains(serverConfigUtils::configTypeFromJson(serverConfigObject));
|
return premiumTypes.contains(getConfigType(serverConfigObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject)
|
QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject)
|
||||||
{
|
{
|
||||||
if (serverConfigUtils::configTypeFromJson(serverConfigObject) != serverConfigUtils::ConfigType::AmneziaPremiumV1) {
|
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV1) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,8 +242,9 @@ QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject)
|
|||||||
|
|
||||||
QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject)
|
QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject)
|
||||||
{
|
{
|
||||||
auto configType = serverConfigUtils::configTypeFromJson(serverConfigObject);
|
auto configType = apiUtils::getConfigType(serverConfigObject);
|
||||||
if (configType != serverConfigUtils::ConfigType::AmneziaPremiumV2 && configType != serverConfigUtils::ConfigType::ExternalPremium) {
|
if (configType != apiDefs::ConfigType::AmneziaPremiumV2 && configType != apiDefs::ConfigType::ExternalPremium
|
||||||
|
&& configType != apiDefs::ConfigType::ExternalTrial) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "core/utils/serverConfigUtils.h"
|
#include "core/utils/api/apiEnums.h"
|
||||||
#include "core/utils/constants/apiKeys.h"
|
#include "core/utils/constants/apiKeys.h"
|
||||||
#include "core/utils/constants/apiConstants.h"
|
#include "core/utils/constants/apiConstants.h"
|
||||||
#include "core/utils/errorCodes.h"
|
#include "core/utils/errorCodes.h"
|
||||||
@@ -13,12 +13,17 @@
|
|||||||
|
|
||||||
namespace apiUtils
|
namespace apiUtils
|
||||||
{
|
{
|
||||||
|
bool isServerFromApi(const QJsonObject &serverConfigObject);
|
||||||
|
|
||||||
bool isSubscriptionExpired(const QString &subscriptionEndDate);
|
bool isSubscriptionExpired(const QString &subscriptionEndDate);
|
||||||
|
|
||||||
bool isSubscriptionExpiringSoon(const QString &subscriptionEndDate, int withinDays = 30);
|
bool isSubscriptionExpiringSoon(const QString &subscriptionEndDate, int withinDays = 30);
|
||||||
|
|
||||||
bool isPremiumServer(const QJsonObject &serverConfigObject);
|
bool isPremiumServer(const QJsonObject &serverConfigObject);
|
||||||
|
|
||||||
|
apiDefs::ConfigType getConfigType(const QJsonObject &serverConfigObject);
|
||||||
|
apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject);
|
||||||
|
|
||||||
amnezia::ErrorCode checkNetworkReplyErrors(const QList<QSslError> &sslErrors, const QString &replyErrorString,
|
amnezia::ErrorCode checkNetworkReplyErrors(const QList<QSslError> &sslErrors, const QString &replyErrorString,
|
||||||
const QNetworkReply::NetworkError &replyError, const int httpStatusCode,
|
const QNetworkReply::NetworkError &replyError, const int httpStatusCode,
|
||||||
const QByteArray &responseBody);
|
const QByteArray &responseBody);
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
|
|
||||||
namespace apiDefs
|
namespace apiDefs
|
||||||
{
|
{
|
||||||
|
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
|
||||||
constexpr int requestTimeoutMsecs = 12 * 1000; // 12 secs
|
}
|
||||||
|
|
||||||
} // namespace apiDefs
|
|
||||||
|
|
||||||
#endif // APICONSTANTS_H
|
#endif // APICONSTANTS_H
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user