mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-22 02:01:08 +07:00
Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fa72d8de43 | |||
| e43973f8ad | |||
| 8d28beacd8 | |||
| 495d59da07 | |||
| 528015d17e | |||
| 55237f951e | |||
| 1b4f3d5872 | |||
| 8eefe7d26b | |||
| fd34bf08f1 | |||
| a7734d6093 | |||
| 526a7cad89 | |||
| 13955dd36b | |||
| d81c1e5c1a | |||
| 3ad6a7a46d | |||
| de5e2635e3 | |||
| b08154671e | |||
| af2ade1d20 | |||
| f456db5392 | |||
| ea71e9b87d | |||
| 7f3a4aaa14 | |||
| 7c455591f6 | |||
| f8e229ff7b | |||
| b033fefa31 | |||
| aab0927242 | |||
| ca8164fde4 | |||
| 7efb532bc9 | |||
| 98915b3cf2 | |||
| fa5076dff0 | |||
| dbb918b68c | |||
| 5078d1a2ca | |||
| 6907886c44 | |||
| 2f01aac483 | |||
| b94cddcb1c | |||
| 2b5c2cb021 | |||
| 729d6ee6d3 | |||
| cd87253844 | |||
| 8cdaa0c2ee | |||
| d563d66b28 | |||
| 37327e30d3 | |||
| e0af63ce1c | |||
| 041d0fcab5 | |||
| 2a6960a4fb | |||
| 8481367fb6 | |||
| 70d6f9996d | |||
| 616d58e901 | |||
| 0bfbd82536 | |||
| 544ad4ba6e | |||
| 02be6dc5f9 | |||
| bfcf7f0305 | |||
| 2bce595ade | |||
| cd1e561fd4 | |||
| 9bd1e6a0f5 | |||
| 5058c9aa6f | |||
| d78416835c | |||
| 40e6c6aae3 | |||
| 911a999c64 | |||
| b4f4184aa6 | |||
| 5c6db4b7a4 | |||
| f6277cdbb2 | |||
| 99312e61d3 | |||
| 9f0ae75a2f | |||
| 7960d8015d | |||
| 5dcc64e5e5 | |||
| 964436ad43 | |||
| 4fc3900fd5 | |||
| 8f5e42dd61 | |||
| 24895752c1 | |||
| 87eccfb4ca | |||
| a983d0504e | |||
| d0b8535395 | |||
| f84480cf56 | |||
| de7a026ec1 | |||
| a128c7d247 | |||
| f316f0e25a | |||
| ea5242e29b | |||
| b31a62c55f | |||
| 02e3107a23 | |||
| 1862850108 |
@@ -0,0 +1,56 @@
|
|||||||
|
# .github/actions/setup-keychain/action.yml
|
||||||
|
name: Setup apple keychain
|
||||||
|
description: Creates and configures a temporary build keychain
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
keychain-path:
|
||||||
|
description: Name of the keychain
|
||||||
|
required: true
|
||||||
|
keychain-password:
|
||||||
|
description: Temporary keychain password
|
||||||
|
required: true
|
||||||
|
app-cert-base64:
|
||||||
|
description: Base64-encoded P12 app certificate
|
||||||
|
required: true
|
||||||
|
app-cert-password:
|
||||||
|
description: Application certificate password
|
||||||
|
required: true
|
||||||
|
installer-cert-base64:
|
||||||
|
description: Base64-encoded P12 installer certificate
|
||||||
|
required: true
|
||||||
|
installer-cert-password:
|
||||||
|
description: Installer certificate password
|
||||||
|
required: true
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Create keychain
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
KEYCHAIN_PATH: ${{ inputs.keychain-path }}
|
||||||
|
KEYCHAIN_PASSWORD: ${{ inputs.keychain-password }}
|
||||||
|
APP_CERT_BASE64: ${{ inputs.app-cert-base64 }}
|
||||||
|
APP_CERT_PASSWORD: ${{ inputs.app-cert-password }}
|
||||||
|
INSTALLER_CERT_BASE64: ${{ inputs.installer-cert-base64 }}
|
||||||
|
INSTALLER_CERT_PASSWORD: ${{ inputs.installer-cert-password }}
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
|
||||||
|
APP_CERT_PATH=$RUNNER_TEMP/DeveloperIdApplicationCertificate.p12
|
||||||
|
INSTALLER_CERT_PATH=$RUNNER_TEMP/DeveloperIdInstallerCertificate.p12
|
||||||
|
|
||||||
|
echo -n "$APP_CERT_BASE64" | base64 --decode -o "$APP_CERT_PATH"
|
||||||
|
echo -n "$INSTALLER_CERT_BASE64" | base64 --decode -o "$INSTALLER_CERT_PATH"
|
||||||
|
|
||||||
|
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||||||
|
security default-keychain -s "$KEYCHAIN_PATH"
|
||||||
|
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
|
||||||
|
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||||||
|
|
||||||
|
security import "${{ github.action_path }}/DeveloperIDG2CA.cer" -k "$KEYCHAIN_PATH" -A
|
||||||
|
security import "$APP_CERT_PATH" -k "$KEYCHAIN_PATH" -P "$APP_CERT_PASSWORD" -A -t cert -f pkcs12
|
||||||
|
security import "$INSTALLER_CERT_PATH" -k "$KEYCHAIN_PATH" -P "$INSTALLER_CERT_PASSWORD" -A -t cert -f pkcs12
|
||||||
|
|
||||||
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||||||
|
security list-keychain -d user -s "$KEYCHAIN_PATH"
|
||||||
+92
-199
@@ -40,44 +40,34 @@ 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.26.2"
|
||||||
|
|
||||||
|
- 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'
|
||||||
run: |
|
shell: bash
|
||||||
sudo apt-get install libxkbcommon-x11-0 libsecret-1-dev
|
env:
|
||||||
export QT_BIN_DIR=${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64/bin
|
QT_INSTALL_DIR: ${{ runner.temp }}
|
||||||
export QIF_BIN_DIR=${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin
|
run: ./deploy/build.sh
|
||||||
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@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: AmneziaVPN_${{ env.VERSION }}_linux_x64.tar.zip
|
path: deploy/build/AmneziaVPN-*-Linux.run
|
||||||
path: deploy/AmneziaVPN_${{ env.VERSION }}_linux_x64.tar.zip
|
archive: false
|
||||||
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'
|
||||||
@@ -95,7 +85,6 @@ jobs:
|
|||||||
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 }}
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
@@ -111,17 +100,6 @@ 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:
|
||||||
@@ -158,39 +136,34 @@ 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.26.2"
|
||||||
|
|
||||||
- name: 'Build project'
|
- name: 'Build project'
|
||||||
shell: cmd
|
shell: cmd
|
||||||
|
env:
|
||||||
|
QT_INSTALL_DIR: ${{ runner.temp }}
|
||||||
run: |
|
run: |
|
||||||
set BUILD_ARCH=${{ env.BUILD_ARCH }}
|
set WIX_ROOT_PATH="${{ env.USERPROFILE }}\.dotnet\tools"
|
||||||
set QT_BIN_DIR="${{ runner.temp }}\\Qt\\${{ env.QT_VERSION }}\\msvc2022_64\\bin"
|
deploy\build.bat --installer all
|
||||||
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: 'Rename Windows installer'
|
- name: 'Upload WIX installer artifact'
|
||||||
shell: cmd
|
uses: actions/upload-artifact@v7
|
||||||
run: |
|
|
||||||
copy AmneziaVPN_x${{ env.BUILD_ARCH }}.exe AmneziaVPN_%VERSION%_x64.exe
|
|
||||||
|
|
||||||
- name: 'Upload installer artifact'
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
with:
|
||||||
name: AmneziaVPN_${{ env.VERSION }}_x64.exe
|
path: deploy/build/AmneziaVPN-*-win64.msi
|
||||||
path: AmneziaVPN_${{ env.VERSION }}_x64.exe
|
archive: false
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
- name: 'Upload MSI installer artifact'
|
- name: 'Upload IFW installer artifact'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: AmneziaVPN_Windows_MSI_installer
|
path: deploy/build/AmneziaVPN-*-win64.exe
|
||||||
path: AmneziaVPN_x${{ env.BUILD_ARCH }}.msi
|
archive: false
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
- name: 'Upload unpacked artifact'
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: AmneziaVPN_Windows_unpacked
|
|
||||||
path: deploy\\build_${{ env.BUILD_ARCH }}\\client\\Release
|
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
@@ -258,11 +231,13 @@ jobs:
|
|||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
fetch-depth: 10
|
fetch-depth: 10
|
||||||
|
|
||||||
# - name: 'Setup ccache'
|
- name: 'Setup python'
|
||||||
# uses: hendrikmuhs/ccache-action@v1.2
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.14
|
||||||
|
|
||||||
- name: 'Install dependencies'
|
- name: 'Install deps'
|
||||||
run: pip install jsonschema jinja2
|
run: pip install "conan==2.26.2" jsonschema jinja2
|
||||||
|
|
||||||
- name: 'Build project'
|
- name: 'Build project'
|
||||||
run: |
|
run: |
|
||||||
@@ -285,93 +260,6 @@ jobs:
|
|||||||
IOS_APP_PROVISIONING_PROFILE: ${{ secrets.IOS_APP_PROVISIONING_PROFILE }}
|
IOS_APP_PROVISIONING_PROFILE: ${{ secrets.IOS_APP_PROVISIONING_PROFILE }}
|
||||||
IOS_NE_PROVISIONING_PROFILE: ${{ secrets.IOS_NE_PROVISIONING_PROFILE }}
|
IOS_NE_PROVISIONING_PROFILE: ${{ secrets.IOS_NE_PROVISIONING_PROFILE }}
|
||||||
|
|
||||||
# - name: 'Upload appstore .ipa and dSYMs to artifacts'
|
|
||||||
# uses: actions/upload-artifact@v4
|
|
||||||
# with:
|
|
||||||
# name: app-store ipa & dsyms
|
|
||||||
# path: |
|
|
||||||
# ${{ github.workspace }}/AmneziaVPN-iOS.ipa
|
|
||||||
# ${{ github.workspace }}/*.app.dSYM.zip
|
|
||||||
# retention-days: 7
|
|
||||||
|
|
||||||
# ------------------------------------------------------
|
|
||||||
|
|
||||||
Build-MacOS-old:
|
|
||||||
runs-on: macos-latest
|
|
||||||
|
|
||||||
env:
|
|
||||||
# Keep compat with MacOS 10.15 aka Catalina by Qt 6.4
|
|
||||||
QT_VERSION: 6.4.3
|
|
||||||
|
|
||||||
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
|
|
||||||
|
|
||||||
MAC_APP_CERT_CERT: ${{ secrets.MAC_APP_CERT_CERT }}
|
|
||||||
MAC_SIGNER_ID: ${{ secrets.MAC_SIGNER_ID }}
|
|
||||||
MAC_APP_CERT_PW: ${{ secrets.MAC_APP_CERT_PW }}
|
|
||||||
|
|
||||||
MAC_INSTALLER_SIGNER_CERT: ${{ secrets.MAC_INSTALLER_SIGNER_CERT }}
|
|
||||||
MAC_INSTALLER_SIGNER_ID: ${{ secrets.MAC_INSTALLER_SIGNER_ID }}
|
|
||||||
MAC_INSTALL_CERT_PW: ${{ secrets.MAC_INSTALL_CERT_PW }}
|
|
||||||
|
|
||||||
APPLE_DEV_EMAIL: ${{ secrets.APPLE_DEV_EMAIL }}
|
|
||||||
APPLE_DEV_PASSWORD: ${{ secrets.APPLE_DEV_PASSWORD }}
|
|
||||||
|
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
|
||||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
|
||||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
|
||||||
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
|
||||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: 'Setup xcode'
|
|
||||||
uses: maxim-lobanov/setup-xcode@v1
|
|
||||||
with:
|
|
||||||
xcode-version: '15.4.0'
|
|
||||||
|
|
||||||
- name: 'Install Qt'
|
|
||||||
uses: jurplel/install-qt-action@v3
|
|
||||||
with:
|
|
||||||
version: ${{ env.QT_VERSION }}
|
|
||||||
host: 'mac'
|
|
||||||
target: 'desktop'
|
|
||||||
arch: 'clang_64'
|
|
||||||
modules: 'qtremoteobjects qt5compat qtshadertools'
|
|
||||||
dir: ${{ runner.temp }}
|
|
||||||
setup-python: 'true'
|
|
||||||
set-env: 'true'
|
|
||||||
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
|
||||||
|
|
||||||
|
|
||||||
- name: 'Get sources'
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: 'true'
|
|
||||||
fetch-depth: 10
|
|
||||||
|
|
||||||
# - name: 'Setup ccache'
|
|
||||||
# uses: hendrikmuhs/ccache-action@v1.2
|
|
||||||
|
|
||||||
- name: 'Build project'
|
|
||||||
run: |
|
|
||||||
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
|
||||||
bash deploy/build_macos.sh -n
|
|
||||||
|
|
||||||
- name: 'Upload installer artifact'
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: AmneziaVPN_MacOS_old_installer
|
|
||||||
path: deploy/build/pkg/AmneziaVPN.pkg
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
- name: 'Upload unpacked artifact'
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: AmneziaVPN_MacOS_old_unpacked
|
|
||||||
path: deploy/build/client/AmneziaVPN.app
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
Build-MacOS:
|
Build-MacOS:
|
||||||
@@ -379,19 +267,8 @@ jobs:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
QT_VERSION: 6.10.1
|
QT_VERSION: 6.10.1
|
||||||
|
KEYCHAIN_NAME: "build.keychain"
|
||||||
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
|
KEYCHAIN_PASSWORD: ""
|
||||||
|
|
||||||
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 }}
|
||||||
@@ -422,45 +299,48 @@ 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.26.2"
|
||||||
|
|
||||||
- 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'
|
- name: 'Install certs'
|
||||||
id: get_version
|
uses: ./.github/actions/setup-keychain
|
||||||
run: |
|
with:
|
||||||
VERSION=$(grep 'set(AMNEZIAVPN_VERSION' CMakeLists.txt | sed -E 's/.*AMNEZIAVPN_VERSION ([0-9]+.[0-9]+.[0-9]+.[0-9]+)\)/\1/')
|
keychain-path: ${{ env.KEYCHAIN_NAME }}
|
||||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
keychain-password: ${{ env.KEYCHAIN_PASSWORD }}
|
||||||
echo "Version: $VERSION"
|
app-cert-base64: ${{ secrets.MAC_APP_CERT_CERT }}
|
||||||
|
app-cert-password: ${{ secrets.MAC_APP_CERT_PW }}
|
||||||
# - name: 'Setup ccache'
|
installer-cert-base64: ${{ secrets.MAC_INSTALLER_SIGNER_CERT }}
|
||||||
# uses: hendrikmuhs/ccache-action@v1.2
|
installer-cert-password: ${{ secrets.MAC_INSTALL_CERT_PW }}
|
||||||
|
|
||||||
- name: 'Build project'
|
- name: 'Build project'
|
||||||
run: |
|
env:
|
||||||
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
QT_INSTALL_DIR: ${{ runner.temp }}
|
||||||
bash deploy/build_macos.sh -n
|
CODESIGN_KEYCHAIN: ${{ env.KEYCHAIN_NAME }}
|
||||||
|
CODESIGN_SIGNATURE: ${{ secrets.MAC_SIGNER_ID }}
|
||||||
- name: 'Pack macOS installer'
|
CODESIGN_INSTALLER_KEYCHAIN: ${{ env.KEYCHAIN_NAME }}
|
||||||
run: |
|
CODESIGN_INSTALLER_SIGNATURE: ${{ secrets.MAC_INSTALLER_SIGNER_ID }}
|
||||||
cd deploy/build/pkg
|
NOTARYTOOL_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
|
||||||
zip -r ../../AmneziaVPN_${VERSION}_macos.zip AmneziaVPN.pkg
|
NOTARYTOOL_EMAIL: ${{ secrets.APPLE_DEV_EMAIL }}
|
||||||
cd ../../..
|
NOTARYTOOL_PASSWORD: ${{ secrets.APPLE_DEV_PASSWORD }}
|
||||||
|
shell: bash
|
||||||
|
run: deploy/build.sh
|
||||||
|
|
||||||
- name: 'Upload installer artifact'
|
- name: 'Upload installer artifact'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: AmneziaVPN_${{ env.VERSION }}_macos.zip
|
path: deploy/build/AmneziaVPN-*-Darwin.pkg
|
||||||
path: deploy/AmneziaVPN_${{ env.VERSION }}_macos.zip
|
archive: false
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
- name: 'Upload unpacked artifact'
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: AmneziaVPN_MacOS_unpacked
|
|
||||||
path: deploy/build/client/AmneziaVPN.app
|
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
Build-MacOS-NE:
|
Build-MacOS-NE:
|
||||||
@@ -519,8 +399,13 @@ jobs:
|
|||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
fetch-depth: 10
|
fetch-depth: 10
|
||||||
|
|
||||||
# - name: 'Setup ccache'
|
- name: 'Setup python'
|
||||||
# uses: hendrikmuhs/ccache-action@v1.2
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.14
|
||||||
|
|
||||||
|
- name: 'Install conan'
|
||||||
|
run: pip install "conan==2.26.2"
|
||||||
|
|
||||||
- name: 'Build project'
|
- name: 'Build project'
|
||||||
run: |
|
run: |
|
||||||
@@ -652,6 +537,14 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo $KEYSTORE_BASE64 | base64 --decode > android.keystore
|
echo $KEYSTORE_BASE64 | base64 --decode > android.keystore
|
||||||
|
|
||||||
|
- name: 'Setup python'
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.14
|
||||||
|
|
||||||
|
- name: 'Install conan'
|
||||||
|
run: pip install "conan==2.26.2"
|
||||||
|
|
||||||
- name: 'Build project'
|
- name: 'Build project'
|
||||||
env:
|
env:
|
||||||
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
|
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
- name: Verify git tag
|
- name: Verify git tag
|
||||||
run: |
|
run: |
|
||||||
TAG_NAME=${{ inputs.RELEASE_VERSION }}
|
TAG_NAME=${{ inputs.RELEASE_VERSION }}
|
||||||
CMAKE_TAG=$(grep 'project.*VERSION' CMakeLists.txt | sed -E 's/.* ([0-9]+.[0-9]+.[0-9]+.[0-9]+)$/\1/')
|
CMAKE_TAG=$(grep 'set(AMNEZIAVPN_VERSION' CMakeLists.txt | sed -E 's/.*AMNEZIAVPN_VERSION ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*/\1/')
|
||||||
if [[ "$TAG_NAME" == "$CMAKE_TAG" ]]; then
|
if [[ "$TAG_NAME" == "$CMAKE_TAG" ]]; then
|
||||||
echo "Git tag ($TAG_NAME) matches CMakeLists.txt version ($CMAKE_TAG)."
|
echo "Git tag ($TAG_NAME) matches CMakeLists.txt version ($CMAKE_TAG)."
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -140,3 +140,6 @@ ios-ne-build.sh
|
|||||||
macos-ne-build.sh
|
macos-ne-build.sh
|
||||||
macos-signed-build.sh
|
macos-signed-build.sh
|
||||||
macos-with-sign-build.sh
|
macos-with-sign-build.sh
|
||||||
|
DeveloperIdApplicationCertificate.p12
|
||||||
|
DeveloperIdInstallerCertificate.p12
|
||||||
|
|
||||||
|
|||||||
@@ -14,3 +14,7 @@
|
|||||||
[submodule "client/3rd/QSimpleCrypto"]
|
[submodule "client/3rd/QSimpleCrypto"]
|
||||||
path = client/3rd/QSimpleCrypto
|
path = client/3rd/QSimpleCrypto
|
||||||
url = https://github.com/amnezia-vpn/QSimpleCrypto.git
|
url = https://github.com/amnezia-vpn/QSimpleCrypto.git
|
||||||
|
[submodule "client/3rd/qtgamepad"]
|
||||||
|
path = client/3rd/qtgamepad
|
||||||
|
url = https://github.com/amnezia-vpn/qtgamepad.git
|
||||||
|
branch = 6.6
|
||||||
|
|||||||
+15
-37
@@ -1,7 +1,14 @@
|
|||||||
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||||
|
|
||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
set(AMNEZIAVPN_VERSION 4.8.12.9)
|
set(AMNEZIAVPN_VERSION 4.8.13.1)
|
||||||
|
|
||||||
|
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"
|
||||||
@@ -12,7 +19,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
|||||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||||
|
|
||||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||||
set(APP_ANDROID_VERSION_CODE 2105)
|
set(APP_ANDROID_VERSION_CODE 2107)
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(MZ_PLATFORM_NAME "linux")
|
set(MZ_PLATFORM_NAME "linux")
|
||||||
@@ -24,6 +31,8 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Android")
|
|||||||
set(MZ_PLATFORM_NAME "android")
|
set(MZ_PLATFORM_NAME "android")
|
||||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "iOS")
|
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "iOS")
|
||||||
set(MZ_PLATFORM_NAME "ios")
|
set(MZ_PLATFORM_NAME "ios")
|
||||||
|
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "tvOS")
|
||||||
|
set(MZ_PLATFORM_NAME "ios")
|
||||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
|
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
|
||||||
set(MZ_PLATFORM_NAME "wasm")
|
set(MZ_PLATFORM_NAME "wasm")
|
||||||
endif()
|
endif()
|
||||||
@@ -33,7 +42,7 @@ set(CMAKE_CXX_STANDARD 17)
|
|||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
if(IOS)
|
if(IOS OR CMAKE_SYSTEM_NAME STREQUAL "tvOS")
|
||||||
set(CMAKE_OSX_ARCHITECTURES "arm64")
|
set(CMAKE_OSX_ARCHITECTURES "arm64")
|
||||||
elseif(MACOS_NE)
|
elseif(MACOS_NE)
|
||||||
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64")
|
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64")
|
||||||
@@ -44,41 +53,10 @@ 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 AND NOT CMAKE_SYSTEM_NAME STREQUAL "tvOS")
|
||||||
add_subdirectory(service)
|
add_subdirectory(service)
|
||||||
|
|
||||||
include(${CMAKE_SOURCE_DIR}/deploy/installer/config.cmake)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(AMNEZIA_STAGE_DIR "${CMAKE_BINARY_DIR}/stage")
|
if ((LINUX AND NOT ANDROID) OR (APPLE AND NOT IOS AND NOT MACOS_NE AND NOT CMAKE_SYSTEM_NAME STREQUAL "tvOS") OR (WIN32))
|
||||||
|
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(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()
|
||||||
|
|||||||
@@ -0,0 +1,340 @@
|
|||||||
|
# AmneziaVPN Apple TV Build
|
||||||
|
|
||||||
|
This document describes how to build the current branch for Apple TV from the repository root.
|
||||||
|
|
||||||
|
The pipeline is:
|
||||||
|
|
||||||
|
1. Use a separately built static Qt 6.9.2 for `tvOS`.
|
||||||
|
2. Let Conan build/provide C/C++ dependencies.
|
||||||
|
3. Generate an Xcode project with `qt-cmake`.
|
||||||
|
4. Build the `.app` and embedded Network Extension with `xcodebuild`.
|
||||||
|
|
||||||
|
Important:
|
||||||
|
|
||||||
|
- Run the project commands from the repository root.
|
||||||
|
- This is a device build for `appletvos`, not a simulator build.
|
||||||
|
- `xcodebuild build` produces `.app`.
|
||||||
|
- `.ipa` is produced later via `archive` and `-exportArchive`.
|
||||||
|
- The current tvOS Network Extension scope is WireGuard-only.
|
||||||
|
- The temporary tvOS WireGuard bridge prebuilt is opt-in. The Conan recipe does not contain machine-specific fallback paths.
|
||||||
|
- Do not initialize or update submodules just for this build. If a clean checkout has empty `client/3rd` folders, pass `AMNEZIA_THIRDPARTY_ROOT` to an already initialized read-only `client/3rd` tree.
|
||||||
|
|
||||||
|
## 1. Environment
|
||||||
|
|
||||||
|
Set these paths for your machine:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export REPO_ROOT="$PWD"
|
||||||
|
export QT_DESKTOP_PREFIX="$HOME/Qt/6.9.2/macos"
|
||||||
|
export QT_TVOS_SRC="$HOME/Qt_tv/qt-6.9.2-tvos-src"
|
||||||
|
export QT_TVOS_PREFIX="$HOME/Qt_tv/6.9.2/tvos-device"
|
||||||
|
export BUILD_DIR="$REPO_ROOT/build-tvos-device-conan"
|
||||||
|
```
|
||||||
|
|
||||||
|
If this checkout does not have initialized `client/3rd` sources, point CMake at an initialized tree:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export AMNEZIA_THIRDPARTY_ROOT="/path/to/initialized/amnezia/client/3rd"
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are using a temporary prebuilt tvOS WireGuard bridge, point Conan at it explicitly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export AMNEZIA_TVOS_AWG_PREBUILT_DIR="/path/to/WireGuardKitGo-appletvos"
|
||||||
|
export AMNEZIA_TVOS_AWG_VERSION_HEADER_DIR="/path/to/directory/with/wireguard-go-version.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
`AMNEZIA_TVOS_AWG_PREBUILT_DIR` must contain `libwg-go.a`.
|
||||||
|
|
||||||
|
`AMNEZIA_TVOS_AWG_VERSION_HEADER_DIR` is optional when `wireguard-go-version.h` lives in the same directory as `libwg-go.a`.
|
||||||
|
|
||||||
|
If the env vars are not set, the recipe uses the normal source build path. Rebuilding and publishing the tvOS WireGuard bridge through the registry is a separate task.
|
||||||
|
|
||||||
|
## 2. Required Local Tools
|
||||||
|
|
||||||
|
Conan must be available:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv tool install conan
|
||||||
|
export PATH="$HOME/.local/bin:$PATH"
|
||||||
|
conan --version
|
||||||
|
```
|
||||||
|
|
||||||
|
Validated version:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Conan version 2.27.1
|
||||||
|
```
|
||||||
|
|
||||||
|
The build uses Xcode's AppleTVOS SDK:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
xcrun --sdk appletvos --show-sdk-path
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Prepare Qt Sources
|
||||||
|
|
||||||
|
Do not edit the installed Qt sources in place. Copy them into a separate tvOS fork:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p "$HOME/Qt_tv"
|
||||||
|
rsync -a "$HOME/Qt/6.9.2/Src/" "$QT_TVOS_SRC/"
|
||||||
|
```
|
||||||
|
|
||||||
|
Recommended for reproducibility:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd "$QT_TVOS_SRC"
|
||||||
|
git init
|
||||||
|
git add .
|
||||||
|
git commit -m "Qt 6.9.2 source snapshot"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Apply the Qt tvOS Patchset
|
||||||
|
|
||||||
|
Apply the local Qt tvOS patchset to `$QT_TVOS_SRC`.
|
||||||
|
|
||||||
|
If you need to recreate the patchset from a fresh copy, compare these files against `$HOME/Qt/6.9.2/Src` and reapply the same changes:
|
||||||
|
|
||||||
|
- `qtbase/cmake/QtBaseGlobalTargets.cmake`
|
||||||
|
- `qtbase/cmake/QtBaseHelpers.cmake`
|
||||||
|
- `qtbase/cmake/QtBuildPathsHelpers.cmake`
|
||||||
|
- `qtbase/cmake/QtMkspecHelpers.cmake`
|
||||||
|
- `qtbase/cmake/QtConfig.cmake.in`
|
||||||
|
- `qtbase/mkspecs/unsupported/macx-tvos-clang/qplatformdefs.h`
|
||||||
|
- `qtbase/src/corelib/CMakeLists.txt`
|
||||||
|
- `qtbase/src/corelib/platform/darwin/qdarwinpermissionplugin_location.mm`
|
||||||
|
- `qtbase/src/gui/CMakeLists.txt`
|
||||||
|
- `qtbase/src/widgets/CMakeLists.txt`
|
||||||
|
- `qtbase/src/network/kernel/qnetworkproxy_darwin.cpp`
|
||||||
|
- `qtbase/src/testlib/qtestcrashhandler.cpp`
|
||||||
|
- `qtbase/src/plugins/platforms/ios/qiosapplicationdelegate.mm`
|
||||||
|
- `qtbase/src/plugins/platforms/ios/qiosscreen.mm`
|
||||||
|
- `qtbase/src/plugins/platforms/ios/qiostheme.mm`
|
||||||
|
- `qtbase/src/plugins/platforms/ios/quiview.mm`
|
||||||
|
- `qtbase/src/plugins/platforms/ios/qiosclipboard.mm`
|
||||||
|
|
||||||
|
Recommended after patching:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git -C "$QT_TVOS_SRC" diff > "$HOME/Qt_tv/qt-6.9.2-tvos.patch"
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not use `QT_APPLE_SDK=appletvos`. The working path is `CMAKE_SYSTEM_NAME=tvOS` with `CMAKE_OSX_SYSROOT=appletvos`.
|
||||||
|
|
||||||
|
## 5. Build Qt 6.9.2 for Apple TV
|
||||||
|
|
||||||
|
Only the modules required by this project are built.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p /private/tmp/qt6.9.2-tvos-device-build
|
||||||
|
cd /private/tmp/qt6.9.2-tvos-device-build
|
||||||
|
|
||||||
|
"$QT_TVOS_SRC/configure" \
|
||||||
|
-release -static -appstore-compliant \
|
||||||
|
-nomake tests -nomake examples \
|
||||||
|
-submodules qtbase,qtdeclarative,qtshadertools,qtremoteobjects,qtsvg,qt5compat,qttools \
|
||||||
|
-qt-host-path "$QT_DESKTOP_PREFIX" \
|
||||||
|
-prefix "$QT_TVOS_PREFIX" \
|
||||||
|
-- \
|
||||||
|
-G Ninja \
|
||||||
|
-DQT_QMAKE_TARGET_MKSPEC=macx-tvos-clang \
|
||||||
|
-DCMAKE_SYSTEM_NAME=tvOS \
|
||||||
|
-DCMAKE_OSX_SYSROOT=appletvos \
|
||||||
|
-DCMAKE_OSX_ARCHITECTURES=arm64 \
|
||||||
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=17.0 \
|
||||||
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
|
-DQT_NO_APPLE_SDK_MAX_VERSION_CHECK=ON
|
||||||
|
|
||||||
|
cmake --build . --parallel 8
|
||||||
|
cmake --install .
|
||||||
|
```
|
||||||
|
|
||||||
|
Sanity checks:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
"$QT_TVOS_PREFIX/bin/qt-cmake" --version
|
||||||
|
"$QT_TVOS_PREFIX/bin/qmake" -query QMAKE_XSPEC
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected `QMAKE_XSPEC`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
macx-tvos-clang
|
||||||
|
```
|
||||||
|
|
||||||
|
Return to the repository root after building Qt:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd "$REPO_ROOT"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. Conan Dependency Behavior
|
||||||
|
|
||||||
|
For `CMAKE_SYSTEM_NAME=tvOS`, the project-level Conan graph is intentionally reduced:
|
||||||
|
|
||||||
|
- included: `awg-apple/2.0.1`
|
||||||
|
- included: `libssh/0.11.3@amnezia`
|
||||||
|
- included: `openssl/3.6.1` with `no_apps=True`
|
||||||
|
- excluded: `openvpnadapter`
|
||||||
|
- excluded: `hev-socks5-tunnel`
|
||||||
|
|
||||||
|
This keeps the current Apple TV target in the same practical scope as before: app plus WireGuard-only Network Extension.
|
||||||
|
|
||||||
|
`libssh` is built with `WITH_EXEC=OFF` on tvOS because tvOS does not provide `fork()` or `execv()`.
|
||||||
|
|
||||||
|
## 7. Configure the Project
|
||||||
|
|
||||||
|
From the repository root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd "$REPO_ROOT"
|
||||||
|
|
||||||
|
"$QT_TVOS_PREFIX/bin/qt-cmake" \
|
||||||
|
-B"$BUILD_DIR" \
|
||||||
|
-GXcode \
|
||||||
|
-DQT_HOST_PATH="$QT_DESKTOP_PREFIX" \
|
||||||
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
|
-DCMAKE_SYSTEM_NAME=tvOS \
|
||||||
|
-DCMAKE_OSX_SYSROOT=appletvos
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to provide an external initialized `client/3rd` tree:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
"$QT_TVOS_PREFIX/bin/qt-cmake" \
|
||||||
|
-B"$BUILD_DIR" \
|
||||||
|
-GXcode \
|
||||||
|
-DQT_HOST_PATH="$QT_DESKTOP_PREFIX" \
|
||||||
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
|
-DCMAKE_SYSTEM_NAME=tvOS \
|
||||||
|
-DCMAKE_OSX_SYSROOT=appletvos \
|
||||||
|
-DAMNEZIA_THIRDPARTY_ROOT="$AMNEZIA_THIRDPARTY_ROOT"
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected non-fatal configure warnings:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Warning: plug-in QIOSIntegrationPlugin is not known to the current Qt installation.
|
||||||
|
Warning: plug-in QJpegPlugin is not known to the current Qt installation.
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
In this repo those warnings are tolerated because `client/cmake/ios.cmake` also links the static plugin targets explicitly when available.
|
||||||
|
|
||||||
|
## 8. Build the Apple TV App
|
||||||
|
|
||||||
|
```bash
|
||||||
|
xcodebuild -quiet \
|
||||||
|
-project "$BUILD_DIR/AmneziaVPN.xcodeproj" \
|
||||||
|
-scheme AmneziaVPN \
|
||||||
|
-configuration RelWithDebInfo \
|
||||||
|
-sdk appletvos \
|
||||||
|
CODE_SIGNING_ALLOWED=NO \
|
||||||
|
build
|
||||||
|
```
|
||||||
|
|
||||||
|
Outputs:
|
||||||
|
|
||||||
|
- `$BUILD_DIR/client/RelWithDebInfo-appletvos/AmneziaVPN.app`
|
||||||
|
- `$BUILD_DIR/client/RelWithDebInfo-appletvos/AmneziaVPN.app/PlugIns/AmneziaVPNNetworkExtension.appex`
|
||||||
|
|
||||||
|
Verification:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
file "$BUILD_DIR/client/RelWithDebInfo-appletvos/AmneziaVPN.app/AmneziaVPN"
|
||||||
|
file "$BUILD_DIR/client/RelWithDebInfo-appletvos/AmneziaVPN.app/PlugIns/AmneziaVPNNetworkExtension.appex/AmneziaVPNNetworkExtension"
|
||||||
|
lipo -info "$BUILD_DIR/client/RelWithDebInfo-appletvos/AmneziaVPN.app/AmneziaVPN"
|
||||||
|
lipo -info "$BUILD_DIR/client/RelWithDebInfo-appletvos/AmneziaVPN.app/PlugIns/AmneziaVPNNetworkExtension.appex/AmneziaVPNNetworkExtension"
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Mach-O 64-bit executable arm64
|
||||||
|
Non-fat file: ... is architecture: arm64
|
||||||
|
```
|
||||||
|
|
||||||
|
Useful plist checks:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
plutil -p "$BUILD_DIR/client/RelWithDebInfo-appletvos/AmneziaVPN.app/Info.plist" | rg 'CFBundleIdentifier|DTPlatformName|UIDeviceFamily|MinimumOSVersion' -C 1
|
||||||
|
plutil -p "$BUILD_DIR/client/RelWithDebInfo-appletvos/AmneziaVPN.app/PlugIns/AmneziaVPNNetworkExtension.appex/Info.plist" | rg 'CFBundleIdentifier|NSExtension|DTPlatformName|MinimumOSVersion' -C 1
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected:
|
||||||
|
|
||||||
|
- `DTPlatformName => appletvos`
|
||||||
|
- `UIDeviceFamily => 3`
|
||||||
|
- `MinimumOSVersion => 17.0`
|
||||||
|
- extension point `com.apple.networkextension.packet-tunnel`
|
||||||
|
|
||||||
|
## 9. `.app` vs `.ipa`
|
||||||
|
|
||||||
|
This is the normal sequence:
|
||||||
|
|
||||||
|
1. `xcodebuild build` -> `.app`
|
||||||
|
2. `xcodebuild archive` -> `.xcarchive`
|
||||||
|
3. `xcodebuild -exportArchive` -> `.ipa`
|
||||||
|
|
||||||
|
So seeing `.app` after a successful `build` is correct.
|
||||||
|
|
||||||
|
## 10. Optional Archive and Export
|
||||||
|
|
||||||
|
The commands below are the next step for packaging, but signing and provisioning must be configured first.
|
||||||
|
|
||||||
|
Archive:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
xcodebuild \
|
||||||
|
-project "$BUILD_DIR/AmneziaVPN.xcodeproj" \
|
||||||
|
-scheme AmneziaVPN \
|
||||||
|
-configuration RelWithDebInfo \
|
||||||
|
-sdk appletvos \
|
||||||
|
-archivePath "$BUILD_DIR/AmneziaVPN-tvos.xcarchive" \
|
||||||
|
archive
|
||||||
|
```
|
||||||
|
|
||||||
|
Export:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
xcodebuild -exportArchive \
|
||||||
|
-archivePath "$BUILD_DIR/AmneziaVPN-tvos.xcarchive" \
|
||||||
|
-exportPath "$BUILD_DIR/export-tvos" \
|
||||||
|
-exportOptionsPlist /absolute/path/to/ExportOptions.plist
|
||||||
|
```
|
||||||
|
|
||||||
|
The resulting `.ipa` should appear under:
|
||||||
|
|
||||||
|
```text
|
||||||
|
$BUILD_DIR/export-tvos
|
||||||
|
```
|
||||||
|
|
||||||
|
## 11. Known Non-Fatal Warnings
|
||||||
|
|
||||||
|
The validated `xcodebuild` still prints warnings that do not break the build:
|
||||||
|
|
||||||
|
- missing Swift search path under the active Xcode Metal toolchain
|
||||||
|
- `SDKROOT[sdk=...]` target-level warnings generated by Xcode project export
|
||||||
|
- Swift conditional compilation flag warnings such as `GROUP_ID="..."`
|
||||||
|
- asset catalog warnings because the current icon set is still iOS-shaped, not a full tvOS Top Shelf asset set
|
||||||
|
- Go/WireGuard umbrella-header warnings from the temporary local `libwg-go.a` bridge
|
||||||
|
- deprecated libssh SCP API warnings in existing app code
|
||||||
|
- `qt_import_plugins()` warnings shown during configure
|
||||||
|
|
||||||
|
If the static platform plugin is not linked correctly, the typical failure is:
|
||||||
|
|
||||||
|
- `_OBJC_CLASS_$_QIOSApplicationDelegate`
|
||||||
|
- `_qt_main_wrapper`
|
||||||
|
|
||||||
|
Those are cleanup tasks, not blockers for the current build proof.
|
||||||
|
|
||||||
|
## 12. Fast Rebuild Checklist
|
||||||
|
|
||||||
|
If everything is already built once:
|
||||||
|
|
||||||
|
1. Reuse `$QT_TVOS_PREFIX`
|
||||||
|
2. Reuse Conan cache under `$HOME/.conan2`
|
||||||
|
3. Reuse or pass an initialized `AMNEZIA_THIRDPARTY_ROOT`
|
||||||
|
4. Re-run `qt-cmake` into `$BUILD_DIR`
|
||||||
|
5. Re-run `xcodebuild -quiet ... build`
|
||||||
+1
-1
Submodule client/3rd-prebuilt updated: 579673b2ed...b8c229288d
+1
Submodule client/3rd/qtgamepad added at f72b3e0c62
+46
-37
@@ -3,6 +3,9 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
|||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
project(${PROJECT})
|
project(${PROJECT})
|
||||||
|
|
||||||
|
set(AMNEZIA_THIRDPARTY_ROOT "${CMAKE_CURRENT_LIST_DIR}/3rd" CACHE PATH "Path to Amnezia client/3rd sources")
|
||||||
|
get_filename_component(AMNEZIA_THIRDPARTY_CLIENT_ROOT "${AMNEZIA_THIRDPARTY_ROOT}/.." ABSOLUTE)
|
||||||
|
|
||||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||||
set_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER "Autogen")
|
set_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER "Autogen")
|
||||||
set_property(GLOBAL PROPERTY AUTOMOC_TARGETS_FOLDER "Autogen")
|
set_property(GLOBAL PROPERTY AUTOMOC_TARGETS_FOLDER "Autogen")
|
||||||
@@ -33,7 +36,7 @@ add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}")
|
|||||||
add_definitions(-DFREE_V2_ENDPOINT="$ENV{FREE_V2_ENDPOINT}")
|
add_definitions(-DFREE_V2_ENDPOINT="$ENV{FREE_V2_ENDPOINT}")
|
||||||
add_definitions(-DPREM_V1_ENDPOINT="$ENV{PREM_V1_ENDPOINT}")
|
add_definitions(-DPREM_V1_ENDPOINT="$ENV{PREM_V1_ENDPOINT}")
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
if(WIN32 OR (APPLE AND NOT IOS AND NOT CMAKE_SYSTEM_NAME STREQUAL "tvOS") OR (LINUX AND NOT ANDROID))
|
||||||
set(PACKAGES ${PACKAGES} Widgets)
|
set(PACKAGES ${PACKAGES} Widgets)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -46,7 +49,7 @@ set(LIBS ${LIBS}
|
|||||||
Qt6::Core5Compat Qt6::Concurrent
|
Qt6::Core5Compat Qt6::Concurrent
|
||||||
)
|
)
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
if(WIN32 OR (APPLE AND NOT IOS AND NOT CMAKE_SYSTEM_NAME STREQUAL "tvOS") OR (LINUX AND NOT ANDROID))
|
||||||
set(LIBS ${LIBS} Qt6::Widgets)
|
set(LIBS ${LIBS} Qt6::Widgets)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -56,10 +59,9 @@ target_include_directories(${PROJECT} PUBLIC
|
|||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
|
||||||
)
|
)
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE AND NOT CMAKE_SYSTEM_NAME STREQUAL "tvOS") OR (LINUX AND NOT ANDROID))
|
||||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
|
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
|
||||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep)
|
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep)
|
||||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_tun2socks.rep)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc)
|
qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc)
|
||||||
@@ -109,6 +111,7 @@ include_directories(
|
|||||||
${CMAKE_CURRENT_LIST_DIR}/../ipc
|
${CMAKE_CURRENT_LIST_DIR}/../ipc
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../common/logger
|
${CMAKE_CURRENT_LIST_DIR}/../common/logger
|
||||||
${CMAKE_CURRENT_LIST_DIR}
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
|
${AMNEZIA_THIRDPARTY_CLIENT_ROOT}
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -176,7 +179,7 @@ if(LINUX AND NOT ANDROID)
|
|||||||
link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/linux)
|
link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/linux)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE AND NOT CMAKE_SYSTEM_NAME STREQUAL "tvOS") OR (LINUX AND NOT ANDROID))
|
||||||
add_compile_definitions(AMNEZIA_DESKTOP)
|
add_compile_definitions(AMNEZIA_DESKTOP)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -184,7 +187,8 @@ if(ANDROID)
|
|||||||
include(cmake/android.cmake)
|
include(cmake/android.cmake)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(IOS)
|
if(IOS OR CMAKE_SYSTEM_NAME STREQUAL "tvOS")
|
||||||
|
option(AMNEZIA_IOS_ENABLE_APPLETV_TARGET "Enable Apple TV target settings for iOS/Xcode projects" OFF)
|
||||||
include(cmake/ios.cmake)
|
include(cmake/ios.cmake)
|
||||||
include(cmake/ios-arch-fixup.cmake)
|
include(cmake/ios-arch-fixup.cmake)
|
||||||
elseif(APPLE AND MACOS_NE)
|
elseif(APPLE AND MACOS_NE)
|
||||||
@@ -197,35 +201,40 @@ endif()
|
|||||||
target_link_libraries(${PROJECT} PRIVATE ${LIBS})
|
target_link_libraries(${PROJECT} PRIVATE ${LIBS})
|
||||||
target_compile_definitions(${PROJECT} PRIVATE "MZ_$<UPPER_CASE:${MZ_PLATFORM_NAME}>")
|
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()
|
|
||||||
|
|
||||||
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
||||||
qt_finalize_target(${PROJECT})
|
|
||||||
|
# Finalize the executable so Qt can gather/deploy QML modules and plugins correctly (Android needs this).
|
||||||
|
if(COMMAND qt_import_qml_plugins)
|
||||||
|
qt_import_qml_plugins(${PROJECT})
|
||||||
|
endif()
|
||||||
|
if(COMMAND qt_finalize_executable)
|
||||||
|
qt_finalize_executable(${PROJECT})
|
||||||
|
else()
|
||||||
|
qt_finalize_target(${PROJECT})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT IOS AND NOT CMAKE_SYSTEM_NAME STREQUAL "tvOS")
|
||||||
|
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
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ bool AmneziaApplication::parseCommands()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(Q_OS_TVOS) && !defined(MACOS_NE)
|
||||||
void AmneziaApplication::startLocalServer() {
|
void AmneziaApplication::startLocalServer() {
|
||||||
const QString serverName("AmneziaVPNInstance");
|
const QString serverName("AmneziaVPNInstance");
|
||||||
QLocalServer::removeServer(serverName);
|
QLocalServer::removeServer(serverName);
|
||||||
@@ -271,7 +271,7 @@ void AmneziaApplication::startLocalServer() {
|
|||||||
bool AmneziaApplication::eventFilter(QObject *watched, QEvent *event)
|
bool AmneziaApplication::eventFilter(QObject *watched, QEvent *event)
|
||||||
{
|
{
|
||||||
if (event->type() == QEvent::Close) {
|
if (event->type() == QEvent::Close) {
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_TVOS)
|
||||||
quit();
|
quit();
|
||||||
#else
|
#else
|
||||||
if (m_forceQuit) {
|
if (m_forceQuit) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
#include <QQmlContext>
|
#include <QQmlContext>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_TVOS)
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#else
|
#else
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
|
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_TVOS)
|
||||||
#define AMNEZIA_BASE_CLASS QGuiApplication
|
#define AMNEZIA_BASE_CLASS QGuiApplication
|
||||||
#else
|
#else
|
||||||
#define AMNEZIA_BASE_CLASS QApplication
|
#define AMNEZIA_BASE_CLASS QApplication
|
||||||
@@ -37,7 +37,7 @@ public:
|
|||||||
void loadFonts();
|
void loadFonts();
|
||||||
bool parseCommands();
|
bool parseCommands();
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(Q_OS_TVOS) && !defined(MACOS_NE)
|
||||||
void startLocalServer();
|
void startLocalServer();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "8.5.2"
|
agp = "8.6.1"
|
||||||
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.3.4"
|
androidx-camera = "1.5.3"
|
||||||
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,8 @@ import android.os.ParcelFileDescriptor
|
|||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import android.provider.OpenableColumns
|
import android.provider.OpenableColumns
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
|
import android.view.InputDevice
|
||||||
|
import android.view.KeyEvent
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@@ -89,6 +91,10 @@ class AmneziaActivity : QtActivity() {
|
|||||||
private val actionResultHandlers = mutableMapOf<Int, ActivityResultHandler>()
|
private val actionResultHandlers = mutableMapOf<Int, ActivityResultHandler>()
|
||||||
private val permissionRequestHandlers = mutableMapOf<Int, PermissionRequestHandler>()
|
private val permissionRequestHandlers = mutableMapOf<Int, PermissionRequestHandler>()
|
||||||
|
|
||||||
|
private var isActivityResumed = false
|
||||||
|
private var hasWindowFocus = false
|
||||||
|
private val resumeHandler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
private val vpnServiceEventHandler: Handler by lazy(NONE) {
|
private val vpnServiceEventHandler: Handler by lazy(NONE) {
|
||||||
object : Handler(Looper.getMainLooper()) {
|
object : Handler(Looper.getMainLooper()) {
|
||||||
override fun handleMessage(msg: Message) {
|
override fun handleMessage(msg: Message) {
|
||||||
@@ -197,10 +203,7 @@ 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)
|
||||||
}
|
}
|
||||||
@@ -260,6 +263,10 @@ class AmneziaActivity : QtActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
|
isActivityResumed = false
|
||||||
|
hasWindowFocus = false
|
||||||
|
// Cancel all pending operations when activity stops
|
||||||
|
resumeHandler.removeCallbacksAndMessages(null)
|
||||||
Log.d(TAG, "Stop Amnezia activity")
|
Log.d(TAG, "Stop Amnezia activity")
|
||||||
doUnbindService()
|
doUnbindService()
|
||||||
mainScope.launch {
|
mainScope.launch {
|
||||||
@@ -271,35 +278,91 @@ class AmneziaActivity : QtActivity() {
|
|||||||
|
|
||||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||||
super.onWindowFocusChanged(hasFocus)
|
super.onWindowFocusChanged(hasFocus)
|
||||||
|
hasWindowFocus = hasFocus
|
||||||
Log.d(TAG, "Window focus changed: hasFocus=$hasFocus")
|
Log.d(TAG, "Window focus changed: hasFocus=$hasFocus")
|
||||||
|
|
||||||
|
// Cancel pending operations if window loses focus
|
||||||
|
if (!hasFocus) {
|
||||||
|
resumeHandler.removeCallbacksAndMessages(null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||||
|
val deviceId = event.deviceId
|
||||||
|
val keyCode = event.keyCode
|
||||||
|
val pressed = event.action == KeyEvent.ACTION_DOWN
|
||||||
|
val source = event.source
|
||||||
|
|
||||||
|
if (deviceId < 0 && pressed) {
|
||||||
|
when (keyCode) {
|
||||||
|
KeyEvent.KEYCODE_BUTTON_A,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_B,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_X,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_Y,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_START,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_SELECT,
|
||||||
|
KeyEvent.KEYCODE_DPAD_CENTER -> {
|
||||||
|
nativeGamepadKeyEvent(0, keyCode, true)
|
||||||
|
nativeGamepadKeyEvent(0, keyCode, false)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real gamepad events (deviceId >= 0)
|
||||||
|
if (deviceId >= 0) {
|
||||||
|
val isGamepad = (source and InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD
|
||||||
|
val isJoystick = (source and InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK
|
||||||
|
val isDpad = (source and InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
|
||||||
|
if (isGamepad || isJoystick || isDpad) {
|
||||||
|
nativeGamepadKeyEvent(deviceId, keyCode, pressed)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.dispatchKeyEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
private external fun nativeGamepadKeyEvent(deviceId: Int, keyCode: Int, pressed: Boolean)
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
|
isActivityResumed = false
|
||||||
|
// Cancel all pending operations when activity pauses
|
||||||
|
resumeHandler.removeCallbacksAndMessages(null)
|
||||||
Log.d(TAG, "Pause Amnezia activity")
|
Log.d(TAG, "Pause Amnezia activity")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
/* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
isActivityResumed = true
|
||||||
|
Log.d(TAG, "Resume Amnezia activity")
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
window.decorView.apply {
|
window.decorView.apply {
|
||||||
invalidate()
|
invalidate()
|
||||||
|
|
||||||
postDelayed({
|
resumeHandler.postDelayed({
|
||||||
sendTouch(1f, 1f)
|
// Check if activity is still resumed and has focus before executing
|
||||||
|
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
|
||||||
|
sendTouch(1f, 1f)
|
||||||
|
}
|
||||||
}, 100)
|
}, 100)
|
||||||
|
|
||||||
postDelayed({
|
resumeHandler.postDelayed({
|
||||||
sendTouch(2f, 2f)
|
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
|
||||||
|
sendTouch(2f, 2f)
|
||||||
|
}
|
||||||
}, 200)
|
}, 200)
|
||||||
|
|
||||||
postDelayed({
|
resumeHandler.postDelayed({
|
||||||
requestLayout()
|
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
|
||||||
invalidate()
|
requestLayout()
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
}, 250)
|
}, 250)
|
||||||
}
|
}
|
||||||
} */
|
}
|
||||||
Log.d(TAG, "Resume Amnezia activity")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun configureWindowForEdgeToEdge() {
|
private fun configureWindowForEdgeToEdge() {
|
||||||
@@ -362,6 +425,10 @@ class AmneziaActivity : QtActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
isActivityResumed = false
|
||||||
|
hasWindowFocus = false
|
||||||
|
// Cancel all pending operations when activity is destroyed
|
||||||
|
resumeHandler.removeCallbacksAndMessages(null)
|
||||||
Log.d(TAG, "Destroy Amnezia activity")
|
Log.d(TAG, "Destroy Amnezia activity")
|
||||||
unregisterBroadcastReceiver(notificationStateReceiver)
|
unregisterBroadcastReceiver(notificationStateReceiver)
|
||||||
notificationStateReceiver = null
|
notificationStateReceiver = null
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package org.amnezia.vpn
|
package org.amnezia.vpn
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
@@ -11,7 +14,25 @@ private const val TAG = "TvFilePicker"
|
|||||||
|
|
||||||
class TvFilePicker : ComponentActivity() {
|
class TvFilePicker : ComponentActivity() {
|
||||||
|
|
||||||
private val fileChooseResultLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) {
|
private val fileChooseResultLauncher = registerForActivityResult(object : ActivityResultContracts.OpenDocument() {
|
||||||
|
override fun createIntent(context: Context, input: Array<String>): Intent {
|
||||||
|
val intent = super.createIntent(context, input)
|
||||||
|
|
||||||
|
val activitiesToResolveIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
context.packageManager.queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong()))
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
context.packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
|
||||||
|
}
|
||||||
|
if (activitiesToResolveIntent.all {
|
||||||
|
val name = it.activityInfo.packageName
|
||||||
|
name.startsWith("com.google.android.tv.frameworkpackagestubs") || name.startsWith("com.android.tv.frameworkpackagestubs")
|
||||||
|
}) {
|
||||||
|
throw ActivityNotFoundException()
|
||||||
|
}
|
||||||
|
return intent
|
||||||
|
}
|
||||||
|
}) {
|
||||||
setResult(RESULT_OK, Intent().apply { data = it })
|
setResult(RESULT_OK, Intent().apply { data = it })
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
@@ -31,7 +52,7 @@ class TvFilePicker : ComponentActivity() {
|
|||||||
private fun getFile() {
|
private fun getFile() {
|
||||||
try {
|
try {
|
||||||
Log.v(TAG, "getFile")
|
Log.v(TAG, "getFile")
|
||||||
fileChooseResultLauncher.launch("*/*")
|
fileChooseResultLauncher.launch(arrayOf("*/*"))
|
||||||
} catch (_: ActivityNotFoundException) {
|
} catch (_: ActivityNotFoundException) {
|
||||||
Log.w(TAG, "Activity not found")
|
Log.w(TAG, "Activity not found")
|
||||||
setResult(RESULT_CANCELED, Intent().apply { putExtra("activityNotFound", true) })
|
setResult(RESULT_CANCELED, Intent().apply { putExtra("activityNotFound", true) })
|
||||||
|
|||||||
+32
-80
@@ -1,97 +1,49 @@
|
|||||||
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
|
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
|
||||||
|
set(AMNEZIA_THIRDPARTY_ROOT "${CLIENT_ROOT_DIR}/3rd" CACHE PATH "Path to Amnezia client/3rd sources")
|
||||||
|
|
||||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/Modules;${CMAKE_MODULE_PATH}")
|
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/Modules;${CMAKE_MODULE_PATH}")
|
||||||
|
|
||||||
add_subdirectory(${CLIENT_ROOT_DIR}/3rd/SortFilterProxyModel)
|
add_subdirectory(${AMNEZIA_THIRDPARTY_ROOT}/SortFilterProxyModel ${CMAKE_CURRENT_BINARY_DIR}/3rd/SortFilterProxyModel)
|
||||||
set(LIBS ${LIBS} SortFilterProxyModel)
|
set(LIBS ${LIBS} SortFilterProxyModel)
|
||||||
include(${CLIENT_ROOT_DIR}/cmake/QSimpleCrypto.cmake)
|
include(${CLIENT_ROOT_DIR}/cmake/QSimpleCrypto.cmake)
|
||||||
|
|
||||||
include(${CLIENT_ROOT_DIR}/3rd/qrcodegen/qrcodegen.cmake)
|
include(${AMNEZIA_THIRDPARTY_ROOT}/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)
|
add_subdirectory(${AMNEZIA_THIRDPARTY_ROOT}/qtkeychain ${CMAKE_CURRENT_BINARY_DIR}/3rd/qtkeychain EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
|
if(ANDROID)
|
||||||
|
# Use qtgamepad from amnezia-vpn/qtgamepad repository
|
||||||
|
# Only if Qt6CorePrivate is available (required by qtgamepad)
|
||||||
|
find_package(Qt6CorePrivate CONFIG QUIET)
|
||||||
|
if(Qt6CorePrivate_FOUND)
|
||||||
|
add_subdirectory(${CLIENT_ROOT_DIR}/3rd/qtgamepad)
|
||||||
|
# Link both the C++ module and QML plugin
|
||||||
|
if(TARGET GamepadLegacy)
|
||||||
|
target_link_libraries(${PROJECT} PRIVATE GamepadLegacy)
|
||||||
|
endif()
|
||||||
|
if(TARGET GamepadLegacyQuickPrivate)
|
||||||
|
target_link_libraries(${PROJECT} PRIVATE GamepadLegacyQuickPrivate)
|
||||||
|
endif()
|
||||||
|
message(STATUS "Gamepad support enabled for Android")
|
||||||
|
else()
|
||||||
|
message(STATUS "Qt6CorePrivate not found. Gamepad support disabled for Android.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
set(LIBS ${LIBS} qt6keychain)
|
set(LIBS ${LIBS} qt6keychain)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${OPENSSL_INCLUDE_DIR}
|
${AMNEZIA_THIRDPARTY_ROOT}/QSimpleCrypto/src/include
|
||||||
${LIBSSH_INCLUDE_DIR}/include
|
${AMNEZIA_THIRDPARTY_ROOT}/qtkeychain/qtkeychain
|
||||||
${LIBSSH_ROOT_DIR}/include
|
|
||||||
${CLIENT_ROOT_DIR}/3rd/libssh/include
|
|
||||||
${CLIENT_ROOT_DIR}/3rd/QSimpleCrypto/src/include
|
|
||||||
${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)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
|
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
|
||||||
set(QSIMPLECRYPTO_DIR ${CLIENT_ROOT_DIR}/3rd/QSimpleCrypto/src)
|
set(AMNEZIA_THIRDPARTY_ROOT "${CLIENT_ROOT_DIR}/3rd" CACHE PATH "Path to Amnezia client/3rd sources")
|
||||||
|
set(QSIMPLECRYPTO_DIR ${AMNEZIA_THIRDPARTY_ROOT}/QSimpleCrypto/src)
|
||||||
|
|
||||||
include_directories(${QSIMPLECRYPTO_DIR})
|
include_directories(${QSIMPLECRYPTO_DIR})
|
||||||
|
|
||||||
|
|||||||
+10
-14
@@ -42,18 +42,14 @@ set(SOURCES ${SOURCES}
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/core/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()
|
|
||||||
|
|
||||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/xray/android/libxray.aar
|
find_package(awg-android REQUIRED)
|
||||||
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/android/xray/libXray)
|
set(LIBS ${LIBS} amnezia::awg-android)
|
||||||
|
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})
|
||||||
|
|||||||
@@ -39,5 +39,7 @@ while(IOS_TARGETS)
|
|||||||
set_target_properties(${TARGET_NAME} PROPERTIES
|
set_target_properties(${TARGET_NAME} PROPERTIES
|
||||||
XCODE_ATTRIBUTE_ARCHS[sdk=iphoneos*] "arm64"
|
XCODE_ATTRIBUTE_ARCHS[sdk=iphoneos*] "arm64"
|
||||||
XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*] "x86_64"
|
XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*] "x86_64"
|
||||||
|
XCODE_ATTRIBUTE_ARCHS[sdk=appletvos*] "arm64"
|
||||||
|
XCODE_ATTRIBUTE_ARCHS[sdk=appletvsimulator*] "arm64"
|
||||||
)
|
)
|
||||||
endwhile()
|
endwhile()
|
||||||
+136
-31
@@ -1,7 +1,19 @@
|
|||||||
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})
|
||||||
|
set(AMNEZIA_IOS_APPLETV ${AMNEZIA_IOS_ENABLE_APPLETV_TARGET})
|
||||||
|
|
||||||
|
if(AMNEZIA_IOS_APPLETV)
|
||||||
|
message("Apple TV target mode is ON")
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 17.0)
|
||||||
|
set(QT_NO_SET_DEFAULT_IOS_LAUNCH_SCREEN TRUE)
|
||||||
|
set(QT_NO_ADD_IOS_LAUNCH_SCREEN_TO_BUNDLE TRUE)
|
||||||
|
set(IOS_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Info-tvOS.plist.in)
|
||||||
|
set(IOS_LAUNCHSCREEN_STORYBOARD ${CMAKE_CURRENT_SOURCE_DIR}/ios/app/tvOS/AmneziaVPNLaunchScreen.storyboard)
|
||||||
|
else()
|
||||||
|
message("Apple TV target mode is OFF")
|
||||||
|
set(IOS_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Info.plist.in)
|
||||||
|
set(IOS_LAUNCHSCREEN_STORYBOARD ${CMAKE_CURRENT_SOURCE_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard)
|
||||||
|
endif()
|
||||||
|
|
||||||
enable_language(OBJC)
|
enable_language(OBJC)
|
||||||
enable_language(OBJCXX)
|
enable_language(OBJCXX)
|
||||||
@@ -10,13 +22,23 @@ enable_language(Swift)
|
|||||||
find_package(Qt6 REQUIRED COMPONENTS ShaderTools)
|
find_package(Qt6 REQUIRED COMPONENTS ShaderTools)
|
||||||
set(LIBS ${LIBS} Qt6::ShaderTools)
|
set(LIBS ${LIBS} Qt6::ShaderTools)
|
||||||
|
|
||||||
find_library(FW_AUTHENTICATIONSERVICES AuthenticationServices)
|
if(AMNEZIA_IOS_APPLETV)
|
||||||
find_library(FW_UIKIT UIKit)
|
# Use framework linker flags directly for tvOS to avoid iPhoneOS SDK absolute paths.
|
||||||
find_library(FW_AVFOUNDATION AVFoundation)
|
set(FW_AUTHENTICATIONSERVICES "-framework AuthenticationServices")
|
||||||
find_library(FW_FOUNDATION Foundation)
|
set(FW_UIKIT "-framework UIKit")
|
||||||
find_library(FW_STOREKIT StoreKit)
|
set(FW_AVFOUNDATION "-framework AVFoundation")
|
||||||
find_library(FW_USERNOTIFICATIONS UserNotifications)
|
set(FW_FOUNDATION "-framework Foundation")
|
||||||
find_library(FW_NETWORKEXTENSION NetworkExtension)
|
set(FW_STOREKIT "-framework StoreKit")
|
||||||
|
set(FW_USERNOTIFICATIONS "-framework UserNotifications")
|
||||||
|
else()
|
||||||
|
find_library(FW_AUTHENTICATIONSERVICES AuthenticationServices)
|
||||||
|
find_library(FW_UIKIT UIKit)
|
||||||
|
find_library(FW_AVFOUNDATION AVFoundation)
|
||||||
|
find_library(FW_FOUNDATION Foundation)
|
||||||
|
find_library(FW_STOREKIT StoreKit)
|
||||||
|
find_library(FW_USERNOTIFICATIONS UserNotifications)
|
||||||
|
find_library(FW_NETWORKEXTENSION NetworkExtension)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(LIBS ${LIBS}
|
set(LIBS ${LIBS}
|
||||||
${FW_AUTHENTICATIONSERVICES}
|
${FW_AUTHENTICATIONSERVICES}
|
||||||
@@ -25,9 +47,12 @@ set(LIBS ${LIBS}
|
|||||||
${FW_FOUNDATION}
|
${FW_FOUNDATION}
|
||||||
${FW_STOREKIT}
|
${FW_STOREKIT}
|
||||||
${FW_USERNOTIFICATIONS}
|
${FW_USERNOTIFICATIONS}
|
||||||
${FW_NETWORKEXTENSION}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(NOT AMNEZIA_IOS_APPLETV)
|
||||||
|
set(LIBS ${LIBS} ${FW_NETWORKEXTENSION})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h
|
||||||
@@ -57,7 +82,7 @@ target_include_directories(${PROJECT} PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
|
|||||||
|
|
||||||
set_target_properties(${PROJECT} PROPERTIES
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Info.plist.in
|
MACOSX_BUNDLE_INFO_PLIST ${IOS_INFO_PLIST}
|
||||||
MACOSX_BUNDLE_ICON_FILE "AppIcon"
|
MACOSX_BUNDLE_ICON_FILE "AppIcon"
|
||||||
MACOSX_BUNDLE_INFO_STRING "AmneziaVPN"
|
MACOSX_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||||
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPN"
|
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPN"
|
||||||
@@ -66,7 +91,6 @@ set_target_properties(${PROJECT} PROPERTIES
|
|||||||
MACOSX_BUNDLE_LONG_VERSION_STRING "${APPLE_PROJECT_VERSION}-${CMAKE_PROJECT_VERSION_TWEAK}"
|
MACOSX_BUNDLE_LONG_VERSION_STRING "${APPLE_PROJECT_VERSION}-${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
|
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
|
||||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
|
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/ios/app/main.entitlements"
|
|
||||||
XCODE_ATTRIBUTE_MARKETING_VERSION "${APPLE_PROJECT_VERSION}"
|
XCODE_ATTRIBUTE_MARKETING_VERSION "${APPLE_PROJECT_VERSION}"
|
||||||
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||||
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPN"
|
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPN"
|
||||||
@@ -74,13 +98,36 @@ set_target_properties(${PROJECT} PROPERTIES
|
|||||||
XCODE_GENERATE_SCHEME TRUE
|
XCODE_GENERATE_SCHEME TRUE
|
||||||
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
|
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
|
||||||
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
|
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
|
||||||
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
|
||||||
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON
|
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON
|
||||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
|
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
|
||||||
XCODE_EMBED_APP_EXTENSIONS networkextension
|
XCODE_EMBED_APP_EXTENSIONS networkextension
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(AMNEZIA_IOS_APPLETV)
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS "appletvos appletvsimulator"
|
||||||
|
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "3"
|
||||||
|
XCODE_ATTRIBUTE_TVOS_DEPLOYMENT_TARGET "${CMAKE_OSX_DEPLOYMENT_TARGET}"
|
||||||
|
XCODE_ATTRIBUTE_SDKROOT "appletvos"
|
||||||
|
XCODE_ATTRIBUTE_SDKROOT[sdk=appletvos*] "appletvos"
|
||||||
|
XCODE_ATTRIBUTE_SDKROOT[sdk=appletvsimulator*] "appletvsimulator"
|
||||||
|
XCODE_ATTRIBUTE_LIBRARY_SEARCH_PATHS "$(inherited) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)"
|
||||||
|
XCODE_ATTRIBUTE_LIBRARY_SEARCH_PATHS[sdk=appletvos*] "$(inherited) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)"
|
||||||
|
XCODE_ATTRIBUTE_LIBRARY_SEARCH_PATHS[sdk=appletvsimulator*] "$(inherited) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)"
|
||||||
|
XCODE_ATTRIBUTE_EXCLUDED_LIBRARY_SEARCH_PATHS "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS*.sdk/usr/lib/swift"
|
||||||
|
XCODE_ATTRIBUTE_EXCLUDED_FRAMEWORK_SEARCH_PATHS "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS*.sdk/System/Library/Frameworks"
|
||||||
|
)
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
QT_IOS_PERMISSIONS ""
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/ios/app/main.entitlements"
|
||||||
|
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(DEFINED DEPLOY)
|
if(DEFINED DEPLOY)
|
||||||
set_target_properties(${PROJECT} PROPERTIES
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||||
@@ -111,7 +158,61 @@ target_compile_options(${PROJECT} PRIVATE
|
|||||||
-DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\"
|
-DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\"
|
||||||
)
|
)
|
||||||
|
|
||||||
set(WG_APPLE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rd/amneziawg-apple/Sources)
|
if(AMNEZIA_IOS_APPLETV)
|
||||||
|
# qscnetworkreachability plugin links IOKit, which is unavailable on tvOS.
|
||||||
|
qt_import_plugins(${PROJECT}
|
||||||
|
NO_DEFAULT
|
||||||
|
INCLUDE
|
||||||
|
QIOSIntegrationPlugin
|
||||||
|
QJpegPlugin
|
||||||
|
QSvgPlugin
|
||||||
|
QGifPlugin
|
||||||
|
QICOPlugin
|
||||||
|
QSvgIconPlugin
|
||||||
|
QSecureTransportBackendPlugin
|
||||||
|
EXCLUDE
|
||||||
|
QSCNetworkReachabilityNetworkInformationPlugin
|
||||||
|
QDarwinCameraPermissionPlugin
|
||||||
|
)
|
||||||
|
|
||||||
|
# Static tvOS Qt build doesn't auto-link these plugin archives into the
|
||||||
|
# Xcode target, but the app entry point lives in QIOSIntegrationPlugin.
|
||||||
|
set(_amnezia_tvos_static_plugins
|
||||||
|
Qt6::QIOSIntegrationPlugin
|
||||||
|
Qt6::QIOSIntegrationPlugin_init
|
||||||
|
Qt6::QJpegPlugin
|
||||||
|
Qt6::QJpegPlugin_init
|
||||||
|
Qt6::QSvgPlugin
|
||||||
|
Qt6::QSvgPlugin_init
|
||||||
|
Qt6::QGifPlugin
|
||||||
|
Qt6::QGifPlugin_init
|
||||||
|
Qt6::QICOPlugin
|
||||||
|
Qt6::QICOPlugin_init
|
||||||
|
Qt6::QSvgIconPlugin
|
||||||
|
Qt6::QSvgIconPlugin_init
|
||||||
|
Qt6::QSecureTransportBackendPlugin
|
||||||
|
Qt6::QSecureTransportBackendPlugin_init
|
||||||
|
)
|
||||||
|
foreach(_amnezia_tvos_static_plugin IN LISTS _amnezia_tvos_static_plugins)
|
||||||
|
if(TARGET ${_amnezia_tvos_static_plugin})
|
||||||
|
target_link_libraries(${PROJECT} PRIVATE ${_amnezia_tvos_static_plugin})
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
unset(_amnezia_tvos_static_plugin)
|
||||||
|
unset(_amnezia_tvos_static_plugins)
|
||||||
|
|
||||||
|
# Qt 6.9.2 iOS package links IOKit via Qt6::Core interface, but tvOS SDK
|
||||||
|
# does not provide IOKit. Strip this single framework for Apple TV builds.
|
||||||
|
get_target_property(_qtcore_iface_libs Qt6::Core INTERFACE_LINK_LIBRARIES)
|
||||||
|
if(_qtcore_iface_libs)
|
||||||
|
string(REPLACE "-framework IOKit;" "" _qtcore_iface_libs "${_qtcore_iface_libs}")
|
||||||
|
string(REPLACE ";-framework IOKit" "" _qtcore_iface_libs "${_qtcore_iface_libs}")
|
||||||
|
set_property(TARGET Qt6::Core PROPERTY INTERFACE_LINK_LIBRARIES "${_qtcore_iface_libs}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(AMNEZIA_THIRDPARTY_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/3rd" CACHE PATH "Path to Amnezia client/3rd sources")
|
||||||
|
set(WG_APPLE_SOURCE_DIR ${AMNEZIA_THIRDPARTY_ROOT}/amneziawg-apple/Sources)
|
||||||
|
|
||||||
target_sources(${PROJECT} PRIVATE
|
target_sources(${PROJECT} PRIVATE
|
||||||
# ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosvpnprotocol.swift
|
# ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosvpnprotocol.swift
|
||||||
@@ -123,25 +224,29 @@ target_sources(${PROJECT} PRIVATE
|
|||||||
${CLIENT_ROOT_DIR}/platforms/ios/VPNCController.swift
|
${CLIENT_ROOT_DIR}/platforms/ios/VPNCController.swift
|
||||||
)
|
)
|
||||||
|
|
||||||
target_sources(${PROJECT} PRIVATE
|
if(IOS_LAUNCHSCREEN_STORYBOARD)
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard
|
target_sources(${PROJECT} PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Media.xcassets
|
${IOS_LAUNCHSCREEN_STORYBOARD}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Media.xcassets
|
||||||
)
|
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
||||||
|
)
|
||||||
|
|
||||||
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard
|
${IOS_LAUNCHSCREEN_STORYBOARD}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Media.xcassets
|
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Media.xcassets
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
||||||
)
|
)
|
||||||
|
else()
|
||||||
|
target_sources(${PROJECT} PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Media.xcassets
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Media.xcassets
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
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,9 +23,6 @@ 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/macos_util.h
|
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.h
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
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})
|
||||||
|
|
||||||
@@ -152,19 +151,6 @@ 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)
|
||||||
|
|
||||||
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
|
||||||
"${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
|
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"
|
|
||||||
"$<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}
|
COMMAND ${QT_BIN_DIR_DETECTED}/macdeployqt $<TARGET_BUNDLE_DIR:AmneziaVPN> -appstore-compliant -qmldir=${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
COMMENT "Signing OpenVPNAdapter framework"
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ set(HEADERS ${HEADERS}
|
|||||||
${CLIENT_ROOT_DIR}/mozilla/controllerimpl.h
|
${CLIENT_ROOT_DIR}/mozilla/controllerimpl.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT IOS AND NOT MACOS_NE)
|
if(NOT IOS AND NOT MACOS_NE AND NOT CMAKE_SYSTEM_NAME STREQUAL "tvOS")
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.h
|
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.h
|
||||||
)
|
)
|
||||||
@@ -89,14 +89,14 @@ set(SOURCES ${SOURCES}
|
|||||||
${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.cpp
|
${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT IOS AND NOT MACOS_NE)
|
if(NOT IOS AND NOT MACOS_NE AND NOT CMAKE_SYSTEM_NAME STREQUAL "tvOS")
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.cpp
|
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Include native macOS platform helpers (dock/status-item)
|
# Include native macOS platform helpers (dock/status-item)
|
||||||
if(APPLE AND NOT IOS)
|
if(APPLE AND NOT IOS AND NOT CMAKE_SYSTEM_NAME STREQUAL "tvOS")
|
||||||
list(APPEND HEADERS
|
list(APPEND HEADERS
|
||||||
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.h
|
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.h
|
||||||
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.h
|
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.h
|
||||||
@@ -175,13 +175,12 @@ if(WIN32)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE AND NOT CMAKE_SYSTEM_NAME STREQUAL "tvOS") OR (LINUX AND NOT ANDROID))
|
||||||
message("Client desktop build")
|
message("Client desktop build")
|
||||||
add_compile_definitions(AMNEZIA_DESKTOP)
|
add_compile_definitions(AMNEZIA_DESKTOP)
|
||||||
|
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CLIENT_ROOT_DIR}/core/ipcclient.h
|
${CLIENT_ROOT_DIR}/core/ipcclient.h
|
||||||
${CLIENT_ROOT_DIR}/core/privileged_process.h
|
|
||||||
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.h
|
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.h
|
||||||
${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.h
|
${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.h
|
||||||
${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.h
|
${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.h
|
||||||
@@ -194,7 +193,6 @@ if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
|||||||
|
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CLIENT_ROOT_DIR}/core/ipcclient.cpp
|
${CLIENT_ROOT_DIR}/core/ipcclient.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/privileged_process.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
|
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
|
||||||
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.cpp
|
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.cpp
|
||||||
${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.cpp
|
${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.cpp
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
#include "openvpn_configurator.h"
|
#include "openvpn_configurator.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QCoreApplication>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QTemporaryDir>
|
#include <QTemporaryDir>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
|
||||||
#include <QGuiApplication>
|
|
||||||
#else
|
|
||||||
#include <QApplication>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "core/networkUtilities.h"
|
#include "core/networkUtilities.h"
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
@@ -165,7 +161,7 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
|
|||||||
QString dnsConf = QString("\nscript-security 2\n"
|
QString dnsConf = QString("\nscript-security 2\n"
|
||||||
"up %1/update-resolv-conf.sh\n"
|
"up %1/update-resolv-conf.sh\n"
|
||||||
"down %1/update-resolv-conf.sh\n")
|
"down %1/update-resolv-conf.sh\n")
|
||||||
.arg(qApp->applicationDirPath());
|
.arg(QCoreApplication::applicationDirPath());
|
||||||
|
|
||||||
config.append(dnsConf);
|
config.append(dnsConf);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "ssh_configurator.h"
|
#include "ssh_configurator.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QCoreApplication>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
@@ -8,11 +9,6 @@
|
|||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <qtimer.h>
|
#include <qtimer.h>
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
|
||||||
#include <QGuiApplication>
|
|
||||||
#else
|
|
||||||
#include <QApplication>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "core/server_defs.h"
|
#include "core/server_defs.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
@@ -24,7 +20,7 @@ SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, const QShar
|
|||||||
|
|
||||||
QString SshConfigurator::convertOpenSShKey(const QString &key)
|
QString SshConfigurator::convertOpenSShKey(const QString &key)
|
||||||
{
|
{
|
||||||
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
#if !defined(Q_OS_IOS) && !defined(Q_OS_TVOS) && !defined(MACOS_NE)
|
||||||
QProcess p;
|
QProcess p;
|
||||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||||
|
|
||||||
@@ -70,13 +66,13 @@ QString SshConfigurator::convertOpenSShKey(const QString &key)
|
|||||||
// DEAD CODE.
|
// DEAD CODE.
|
||||||
void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
|
void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
|
||||||
{
|
{
|
||||||
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
#if !defined(Q_OS_IOS) && !defined(Q_OS_TVOS) && !defined(MACOS_NE)
|
||||||
QProcess *p = new QProcess();
|
QProcess *p = new QProcess();
|
||||||
p->setProcessChannelMode(QProcess::SeparateChannels);
|
p->setProcessChannelMode(QProcess::SeparateChannels);
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
p->setProcessEnvironment(prepareEnv());
|
p->setProcessEnvironment(prepareEnv());
|
||||||
p->setProgram(qApp->applicationDirPath() + "\\cygwin\\putty.exe");
|
p->setProgram(QCoreApplication::applicationDirPath() + "\\cygwin\\putty.exe");
|
||||||
|
|
||||||
if (credentials.secretData.contains("PRIVATE KEY")) {
|
if (credentials.secretData.contains("PRIVATE KEY")) {
|
||||||
// todo: connect by key
|
// todo: connect by key
|
||||||
@@ -100,10 +96,10 @@ QProcessEnvironment SshConfigurator::prepareEnv()
|
|||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
pathEnvVar.clear();
|
pathEnvVar.clear();
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
|
pathEnvVar.prepend(QDir::toNativeSeparators(QCoreApplication::applicationDirPath()) + "\\cygwin;");
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;");
|
pathEnvVar.prepend(QDir::toNativeSeparators(QCoreApplication::applicationDirPath()) + "\\openvpn;");
|
||||||
#elif defined(Q_OS_MACX) && !defined(MACOS_NE)
|
#elif defined(Q_OS_MACX) && !defined(MACOS_NE)
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
|
pathEnvVar.prepend(QDir::toNativeSeparators(QCoreApplication::applicationDirPath()) + "/Contents/MacOS");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
env.insert("PATH", pathEnvVar);
|
env.insert("PATH", pathEnvVar);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include "platforms/android/android_controller.h"
|
#include "platforms/android/android_controller.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_IOS)
|
#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
|
||||||
#include "platforms/ios/ios_controller.h"
|
#include "platforms/ios/ios_controller.h"
|
||||||
#include <AmneziaVPN-Swift.h>
|
#include <AmneziaVPN-Swift.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -196,7 +196,7 @@ void CoreController::initAndroidController()
|
|||||||
|
|
||||||
void CoreController::initAppleController()
|
void CoreController::initAppleController()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
|
||||||
IosController::Instance()->initialize();
|
IosController::Instance()->initialize();
|
||||||
connect(IosController::Instance(), &IosController::importConfigFromOutside, this, [this](QString data) {
|
connect(IosController::Instance(), &IosController::importConfigFromOutside, this, [this](QString data) {
|
||||||
emit m_pageController->goToPageHome();
|
emit m_pageController->goToPageHome();
|
||||||
@@ -233,7 +233,7 @@ void CoreController::initSignalHandlers()
|
|||||||
|
|
||||||
void CoreController::initNotificationHandler()
|
void CoreController::initNotificationHandler()
|
||||||
{
|
{
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(Q_OS_TVOS)
|
||||||
m_notificationHandler.reset(NotificationHandler::create(nullptr));
|
m_notificationHandler.reset(NotificationHandler::create(nullptr));
|
||||||
|
|
||||||
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
|
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <QQmlContext>
|
#include <QQmlContext>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(Q_OS_TVOS)
|
||||||
#include "ui/systemtray_notificationhandler.h"
|
#include "ui/systemtray_notificationhandler.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
#include "ui/models/sites_model.h"
|
#include "ui/models/sites_model.h"
|
||||||
#include "ui/models/newsModel.h"
|
#include "ui/models/newsModel.h"
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(Q_OS_TVOS)
|
||||||
#include "ui/notificationhandler.h"
|
#include "ui/notificationhandler.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ private:
|
|||||||
QSharedPointer<VpnConnection> m_vpnConnection;
|
QSharedPointer<VpnConnection> m_vpnConnection;
|
||||||
QSharedPointer<QTranslator> m_translator;
|
QSharedPointer<QTranslator> m_translator;
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(Q_OS_TVOS)
|
||||||
QScopedPointer<NotificationHandler> m_notificationHandler;
|
QScopedPointer<NotificationHandler> m_notificationHandler;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -419,6 +419,18 @@ ErrorCode ServerController::installDockerWorker(const ServerCredentials &credent
|
|||||||
cbReadStdOut, cbReadStdErr);
|
cbReadStdOut, cbReadStdErr);
|
||||||
|
|
||||||
qDebug().noquote() << "ServerController::installDockerWorker" << stdOut;
|
qDebug().noquote() << "ServerController::installDockerWorker" << stdOut;
|
||||||
|
if (container == DockerContainer::Awg2) {
|
||||||
|
QRegularExpression regex(R"(Linux\s+(\d+)\.(\d+)[^\d]*)");
|
||||||
|
QRegularExpressionMatch match = regex.match(stdOut);
|
||||||
|
if (match.hasMatch()) {
|
||||||
|
int majorVersion = match.captured(1).toInt();
|
||||||
|
int minorVersion = match.captured(2).toInt();
|
||||||
|
|
||||||
|
if (majorVersion < 4 || (majorVersion == 4 && minorVersion < 14)) {
|
||||||
|
return ErrorCode::ServerLinuxKernelTooOld;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (stdOut.contains("lock"))
|
if (stdOut.contains("lock"))
|
||||||
return ErrorCode::ServerPacketManagerError;
|
return ErrorCode::ServerPacketManagerError;
|
||||||
if (stdOut.contains("command not found"))
|
if (stdOut.contains("command not found"))
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ namespace amnezia
|
|||||||
ServerDockerOnCgroupsV2 = 211,
|
ServerDockerOnCgroupsV2 = 211,
|
||||||
ServerCgroupMountpoint = 212,
|
ServerCgroupMountpoint = 212,
|
||||||
DockerPullRateLimit = 213,
|
DockerPullRateLimit = 213,
|
||||||
|
ServerLinuxKernelTooOld = 214,
|
||||||
|
|
||||||
// Ssh connection errors
|
// Ssh connection errors
|
||||||
SshRequestDeniedError = 300,
|
SshRequestDeniedError = 300,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ QString errorString(ErrorCode code) {
|
|||||||
case(ErrorCode::ServerDockerOnCgroupsV2): errorMessage = QObject::tr("Docker error: runc doesn't work on cgroups v2"); break;
|
case(ErrorCode::ServerDockerOnCgroupsV2): errorMessage = QObject::tr("Docker error: runc doesn't work on cgroups v2"); break;
|
||||||
case(ErrorCode::ServerCgroupMountpoint): errorMessage = QObject::tr("Server error: cgroup mountpoint does not exist"); break;
|
case(ErrorCode::ServerCgroupMountpoint): errorMessage = QObject::tr("Server error: cgroup mountpoint does not exist"); break;
|
||||||
case(ErrorCode::DockerPullRateLimit): errorMessage = QObject::tr("Docker error: The pull rate limit has been reached"); break;
|
case(ErrorCode::DockerPullRateLimit): errorMessage = QObject::tr("Docker error: The pull rate limit has been reached"); break;
|
||||||
|
case(ErrorCode::ServerLinuxKernelTooOld): errorMessage = QObject::tr("Server error: Linux kernel is too old"); break;
|
||||||
|
|
||||||
// Libssh errors
|
// Libssh errors
|
||||||
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
|
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
|
||||||
|
|||||||
+35
-61
@@ -7,7 +7,6 @@ IpcClient::IpcClient(QObject *parent) : QObject(parent)
|
|||||||
{
|
{
|
||||||
m_node.connectToNode(QUrl("local:" + amnezia::getIpcServiceUrl()));
|
m_node.connectToNode(QUrl("local:" + amnezia::getIpcServiceUrl()));
|
||||||
m_interface.reset(m_node.acquire<IpcInterfaceReplica>());
|
m_interface.reset(m_node.acquire<IpcInterfaceReplica>());
|
||||||
m_tun2socks.reset(m_node.acquire<IpcProcessTun2SocksReplica>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IpcClient& IpcClient::Instance()
|
IpcClient& IpcClient::Instance()
|
||||||
@@ -33,68 +32,43 @@ QSharedPointer<IpcInterfaceReplica> IpcClient::Interface()
|
|||||||
return rep;
|
return rep;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<IpcProcessTun2SocksReplica> IpcClient::InterfaceTun2Socks()
|
QSharedPointer<IpcProcessInterfaceReplica> IpcClient::CreatePrivilegedProcess()
|
||||||
{
|
{
|
||||||
QSharedPointer<IpcProcessTun2SocksReplica> rep = Instance().m_tun2socks;
|
return withInterface([](QSharedPointer<IpcInterfaceReplica> &iface) -> QSharedPointer<IpcProcessInterfaceReplica> {
|
||||||
if (rep.isNull()) {
|
auto createPrivilegedProcess = iface->createPrivilegedProcess();
|
||||||
qCritical() << "IpcClient::InterfaceTun2Socks: Replica is undefined";
|
if (!createPrivilegedProcess.waitForFinished()) {
|
||||||
return nullptr;
|
qCritical() << "Failed to create privileged process";
|
||||||
}
|
return nullptr;
|
||||||
if (!rep->waitForSource(1000)) {
|
|
||||||
qCritical() << "IpcClient::InterfaceTun2Socks: Failed to initialize replica";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (!rep->isReplicaValid()) {
|
|
||||||
qWarning() << "IpcClient::InterfaceTun2Socks(): Replica is invalid";
|
|
||||||
}
|
|
||||||
return rep;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSharedPointer<PrivilegedProcess> IpcClient::CreatePrivilegedProcess()
|
|
||||||
{
|
|
||||||
QSharedPointer<IpcInterfaceReplica> rep = Interface();
|
|
||||||
if (!rep) {
|
|
||||||
qCritical() << "IpcClient::createPrivilegedProcess: Replica is invalid";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
QRemoteObjectPendingReply<int> pidReply = rep->createPrivilegedProcess();
|
|
||||||
if (!pidReply.waitForFinished(5000)){
|
|
||||||
qCritical() << "IpcClient::createPrivilegedProcess: Failed to execute RO createPrivilegedProcess call";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int pid = pidReply.returnValue();
|
|
||||||
QSharedPointer<ProcessDescriptor> pd(new ProcessDescriptor());
|
|
||||||
|
|
||||||
pd->localSocket.reset(new QLocalSocket(pd->replicaNode.data()));
|
|
||||||
|
|
||||||
connect(pd->localSocket.data(), &QLocalSocket::connected, pd->replicaNode.data(), [pd]() {
|
|
||||||
pd->replicaNode->addClientSideConnection(pd->localSocket.data());
|
|
||||||
|
|
||||||
IpcProcessInterfaceReplica *repl = pd->replicaNode->acquire<IpcProcessInterfaceReplica>();
|
|
||||||
// TODO: rework the unsafe cast below
|
|
||||||
PrivilegedProcess *priv = static_cast<PrivilegedProcess *>(repl);
|
|
||||||
pd->ipcProcess.reset(priv);
|
|
||||||
if (!pd->ipcProcess) {
|
|
||||||
qWarning() << "Acquire PrivilegedProcess failed";
|
|
||||||
} else {
|
|
||||||
pd->ipcProcess->waitForSource(1000);
|
|
||||||
if (!pd->ipcProcess->isReplicaValid()) {
|
|
||||||
qWarning() << "PrivilegedProcess replica is not connected!";
|
|
||||||
}
|
|
||||||
|
|
||||||
QObject::connect(pd->ipcProcess.data(), &PrivilegedProcess::destroyed, pd->ipcProcess.data(),
|
|
||||||
[pd]() { pd->replicaNode->deleteLater(); });
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
pd->localSocket->connectToServer(amnezia::getIpcProcessUrl(pid));
|
const int pid = createPrivilegedProcess.returnValue();
|
||||||
if (!pd->localSocket->waitForConnected()) {
|
|
||||||
qCritical() << "IpcClient::createPrivilegedProcess: Failed to connect to process' socket";
|
auto* node = new QRemoteObjectNode();
|
||||||
|
node->connectToNode(QUrl(QString("local:%1").arg(amnezia::getIpcProcessUrl(pid))));
|
||||||
|
|
||||||
|
QSharedPointer<IpcProcessInterfaceReplica> rep(
|
||||||
|
node->acquire<IpcProcessInterfaceReplica>(),
|
||||||
|
[node] (IpcProcessInterfaceReplica *ptr) {
|
||||||
|
delete ptr;
|
||||||
|
node->deleteLater();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (rep.isNull()) {
|
||||||
|
qCritical() << "IpcClient::CreatePrivilegedProcess(): Failed to acquire replica";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (!rep->waitForSource()) {
|
||||||
|
qCritical() << "IpcClient::CreatePrivilegedProcess(): Failed to initialize replica";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (!rep->isReplicaValid()) {
|
||||||
|
qCritical() << "IpcClient::CreatePrivilegedProcess(): Replica is invalid";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rep;
|
||||||
|
},
|
||||||
|
[]() -> QSharedPointer<IpcProcessInterfaceReplica> {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
});
|
||||||
|
|
||||||
auto processReplica = QSharedPointer<PrivilegedProcess>(pd->ipcProcess);
|
|
||||||
return processReplica;
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-17
@@ -5,9 +5,7 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "rep_ipc_interface_replica.h"
|
#include "rep_ipc_interface_replica.h"
|
||||||
#include "rep_ipc_process_tun2socks_replica.h"
|
#include "rep_ipc_process_interface_replica.h"
|
||||||
|
|
||||||
#include "privileged_process.h"
|
|
||||||
|
|
||||||
class IpcClient : public QObject
|
class IpcClient : public QObject
|
||||||
{
|
{
|
||||||
@@ -18,8 +16,7 @@ public:
|
|||||||
static IpcClient& Instance();
|
static IpcClient& Instance();
|
||||||
|
|
||||||
static QSharedPointer<IpcInterfaceReplica> Interface();
|
static QSharedPointer<IpcInterfaceReplica> Interface();
|
||||||
static QSharedPointer<IpcProcessTun2SocksReplica> InterfaceTun2Socks();
|
static QSharedPointer<IpcProcessInterfaceReplica> CreatePrivilegedProcess();
|
||||||
static QSharedPointer<PrivilegedProcess> CreatePrivilegedProcess();
|
|
||||||
|
|
||||||
template <typename Func>
|
template <typename Func>
|
||||||
static auto withInterface(Func func)
|
static auto withInterface(Func func)
|
||||||
@@ -54,18 +51,6 @@ signals:
|
|||||||
private:
|
private:
|
||||||
QRemoteObjectNode m_node;
|
QRemoteObjectNode m_node;
|
||||||
QSharedPointer<IpcInterfaceReplica> m_interface;
|
QSharedPointer<IpcInterfaceReplica> m_interface;
|
||||||
QSharedPointer<IpcProcessTun2SocksReplica> m_tun2socks;
|
|
||||||
|
|
||||||
struct ProcessDescriptor {
|
|
||||||
ProcessDescriptor () {
|
|
||||||
replicaNode = QSharedPointer<QRemoteObjectNode>(new QRemoteObjectNode());
|
|
||||||
ipcProcess = QSharedPointer<PrivilegedProcess>();
|
|
||||||
localSocket = QSharedPointer<QLocalSocket>();
|
|
||||||
}
|
|
||||||
QSharedPointer<PrivilegedProcess> ipcProcess;
|
|
||||||
QSharedPointer<QRemoteObjectNode> replicaNode;
|
|
||||||
QSharedPointer<QLocalSocket> localSocket;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // IPCCLIENT_H
|
#endif // IPCCLIENT_H
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(Q_OS_TVOS) && !defined(MACOS_NE)
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
@@ -404,7 +404,7 @@ QPair<QString, QNetworkInterface> NetworkUtilities::getGatewayAndIface()
|
|||||||
close(sock);
|
close(sock);
|
||||||
return { gateway_address, QNetworkInterface::interfaceFromName(interface) };
|
return { gateway_address, QNetworkInterface::interfaceFromName(interface) };
|
||||||
#endif
|
#endif
|
||||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(Q_OS_TVOS) && !defined(MACOS_NE)
|
||||||
QString gateway;
|
QString gateway;
|
||||||
int index = -1;
|
int index = -1;
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
#include "privileged_process.h"
|
|
||||||
|
|
||||||
PrivilegedProcess::PrivilegedProcess() :
|
|
||||||
IpcProcessInterfaceReplica()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
PrivilegedProcess::~PrivilegedProcess()
|
|
||||||
{
|
|
||||||
qDebug() << "PrivilegedProcess::~PrivilegedProcess()";
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrivilegedProcess::waitForFinished(int msecs)
|
|
||||||
{
|
|
||||||
QSharedPointer<QEventLoop> loop(new QEventLoop);
|
|
||||||
connect(this, &PrivilegedProcess::finished, this, [this, loop](int exitCode, QProcess::ExitStatus exitStatus) mutable{
|
|
||||||
loop->quit();
|
|
||||||
loop.clear();
|
|
||||||
});
|
|
||||||
|
|
||||||
QTimer::singleShot(msecs, this, [this, loop]() mutable {
|
|
||||||
loop->quit();
|
|
||||||
loop.clear();
|
|
||||||
});
|
|
||||||
|
|
||||||
loop->exec();
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
#ifndef PRIVILEGED_PROCESS_H
|
|
||||||
#define PRIVILEGED_PROCESS_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include "rep_ipc_process_interface_replica.h"
|
|
||||||
// This class is dangerous - instance of this class casted from base class,
|
|
||||||
// so it support only functions
|
|
||||||
// Do not add any members into it
|
|
||||||
//
|
|
||||||
class PrivilegedProcess : public IpcProcessInterfaceReplica
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
PrivilegedProcess();
|
|
||||||
~PrivilegedProcess() override;
|
|
||||||
|
|
||||||
void waitForFinished(int msecs);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // PRIVILEGED_PROCESS_H
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleAllowMixedLocalizations</key>
|
||||||
|
<true/>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>${QT_INTERNAL_DOLLAR_VAR}{PRODUCT_NAME}</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||||
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
|
<false/>
|
||||||
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
|
<array/>
|
||||||
|
<key>UIRequiresFullScreen</key>
|
||||||
|
<true/>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>UILaunchStoryboardName</key>
|
||||||
|
<string>AmneziaVPNLaunchScreen</string>
|
||||||
|
<key>UIUserInterfaceStyle</key>
|
||||||
|
<string>Light</string>
|
||||||
|
<key>com.wireguard.ios.app_group_id</key>
|
||||||
|
<string>group.org.amnezia.AmneziaVPN</string>
|
||||||
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSAllowsLocalNetworking</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder.AppleTV.Storyboard" version="3.0" toolsVersion="13122.16" targetRuntime="AppleTV" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||||
|
<dependencies>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<scene sceneID="tne-QT-ifu">
|
||||||
|
<objects>
|
||||||
|
<viewController id="BYZ-38-t0r" sceneMemberID="viewController">
|
||||||
|
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="1920" height="1080"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<viewLayoutGuide key="safeArea" id="wu6-TO-1qx"/>
|
||||||
|
</view>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
enable_language(Swift)
|
enable_language(Swift)
|
||||||
|
|
||||||
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
|
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
|
||||||
|
set(AMNEZIA_THIRDPARTY_ROOT "${CLIENT_ROOT_DIR}/3rd" CACHE PATH "Path to Amnezia client/3rd sources")
|
||||||
|
set(AMNEZIA_IOS_APPLETV ${AMNEZIA_IOS_ENABLE_APPLETV_TARGET})
|
||||||
|
|
||||||
|
if(AMNEZIA_IOS_APPLETV)
|
||||||
|
message("Network Extension tvOS mode is ON")
|
||||||
|
else()
|
||||||
|
message("Network Extension tvOS mode is OFF")
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(networkextension)
|
add_executable(networkextension)
|
||||||
set_target_properties(networkextension PROPERTIES
|
set_target_properties(networkextension PROPERTIES
|
||||||
@@ -28,6 +36,23 @@ set_target_properties(networkextension PROPERTIES
|
|||||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks"
|
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(AMNEZIA_IOS_APPLETV)
|
||||||
|
set_target_properties(networkextension PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS "appletvos appletvsimulator"
|
||||||
|
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "3"
|
||||||
|
XCODE_ATTRIBUTE_TVOS_DEPLOYMENT_TARGET "${CMAKE_OSX_DEPLOYMENT_TARGET}"
|
||||||
|
XCODE_ATTRIBUTE_SDKROOT "appletvos"
|
||||||
|
XCODE_ATTRIBUTE_SDKROOT[sdk=appletvos*] "appletvos"
|
||||||
|
XCODE_ATTRIBUTE_SDKROOT[sdk=appletvsimulator*] "appletvsimulator"
|
||||||
|
XCODE_ATTRIBUTE_LIBRARY_SEARCH_PATHS "$(inherited) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)"
|
||||||
|
XCODE_ATTRIBUTE_LIBRARY_SEARCH_PATHS[sdk=appletvos*] "$(inherited) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)"
|
||||||
|
XCODE_ATTRIBUTE_LIBRARY_SEARCH_PATHS[sdk=appletvsimulator*] "$(inherited) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)"
|
||||||
|
XCODE_ATTRIBUTE_EXCLUDED_LIBRARY_SEARCH_PATHS "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS*.sdk/usr/lib/swift"
|
||||||
|
XCODE_ATTRIBUTE_EXCLUDED_FRAMEWORK_SEARCH_PATHS "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS*.sdk/System/Library/Frameworks"
|
||||||
|
LINKER_LANGUAGE Swift
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(DEPLOY)
|
if(DEPLOY)
|
||||||
set_target_properties(networkextension PROPERTIES
|
set_target_properties(networkextension PROPERTIES
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||||
@@ -45,38 +70,49 @@ endif()
|
|||||||
set_target_properties(networkextension PROPERTIES
|
set_target_properties(networkextension PROPERTIES
|
||||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||||
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||||
XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/WireGuardNetworkExtension-Bridging-Header.h"
|
|
||||||
XCODE_ATTRIBUTE_SWIFT_OPTIMIZATION_LEVEL "-Onone"
|
XCODE_ATTRIBUTE_SWIFT_OPTIMIZATION_LEVEL "-Onone"
|
||||||
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
|
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set_target_properties(networkextension PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/WireGuardNetworkExtension-Bridging-Header.h"
|
||||||
|
)
|
||||||
|
|
||||||
set_target_properties("networkextension" PROPERTIES
|
set_target_properties("networkextension" PROPERTIES
|
||||||
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
|
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
|
||||||
)
|
)
|
||||||
|
|
||||||
find_library(FW_ASSETS_LIBRARY AssetsLibrary)
|
|
||||||
find_library(FW_MOBILE_CORE MobileCoreServices)
|
|
||||||
find_library(FW_UI_KIT UIKit)
|
find_library(FW_UI_KIT UIKit)
|
||||||
find_library(FW_LIBRESOLV libresolv.9.tbd)
|
find_library(FW_LIBRESOLV libresolv.9.tbd)
|
||||||
|
|
||||||
target_link_libraries(networkextension PRIVATE ${FW_ASSETS_LIBRARY})
|
if(NOT AMNEZIA_IOS_APPLETV)
|
||||||
target_link_libraries(networkextension PRIVATE ${FW_MOBILE_CORE})
|
target_link_libraries(networkextension PRIVATE ${FW_UI_KIT})
|
||||||
target_link_libraries(networkextension PRIVATE ${FW_UI_KIT})
|
target_link_libraries(networkextension PRIVATE ${FW_LIBRESOLV})
|
||||||
target_link_libraries(networkextension PRIVATE ${FW_LIBRESOLV})
|
else()
|
||||||
|
target_link_libraries(networkextension PRIVATE -lresolv)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_compile_options(networkextension PRIVATE -DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\")
|
target_compile_options(networkextension PRIVATE -DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\")
|
||||||
target_compile_options(networkextension PRIVATE -DNETWORK_EXTENSION=1)
|
target_compile_options(networkextension PRIVATE -DNETWORK_EXTENSION=1)
|
||||||
|
|
||||||
set(WG_APPLE_SOURCE_DIR ${CLIENT_ROOT_DIR}/3rd/amneziawg-apple/Sources)
|
set(WG_APPLE_SOURCE_DIR ${AMNEZIA_THIRDPARTY_ROOT}/amneziawg-apple/Sources)
|
||||||
|
|
||||||
target_sources(networkextension PRIVATE
|
set(NE_COMMON_SOURCES
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/NELogController.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider.swift
|
||||||
|
)
|
||||||
|
|
||||||
|
set(NE_WIREGUARD_SOURCES
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/WireGuardAdapter.swift
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/WireGuardAdapter.swift
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PacketTunnelSettingsGenerator.swift
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PacketTunnelSettingsGenerator.swift
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/DNSResolver.swift
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/DNSResolver.swift
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardNetworkExtension/ErrorNotifier.swift
|
${WG_APPLE_SOURCE_DIR}/WireGuardNetworkExtension/ErrorNotifier.swift
|
||||||
${WG_APPLE_SOURCE_DIR}/Shared/Keychain.swift
|
${WG_APPLE_SOURCE_DIR}/Shared/Keychain.swift
|
||||||
${WG_APPLE_SOURCE_DIR}/Shared/Model/TunnelConfiguration+WgQuickConfig.swift
|
${WG_APPLE_SOURCE_DIR}/Shared/FileManager+Extension.swift
|
||||||
${WG_APPLE_SOURCE_DIR}/Shared/Model/NETunnelProviderProtocol+Extension.swift
|
${WG_APPLE_SOURCE_DIR}/Shared/Model/NETunnelProviderProtocol+Extension.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/Shared/Model/TunnelConfiguration+WgQuickConfig.swift
|
||||||
${WG_APPLE_SOURCE_DIR}/Shared/Model/String+ArrayConversion.swift
|
${WG_APPLE_SOURCE_DIR}/Shared/Model/String+ArrayConversion.swift
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/TunnelConfiguration.swift
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/TunnelConfiguration.swift
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/IPAddressRange.swift
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/IPAddressRange.swift
|
||||||
@@ -84,24 +120,50 @@ target_sources(networkextension PRIVATE
|
|||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/DNSServer.swift
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/DNSServer.swift
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/InterfaceConfiguration.swift
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/InterfaceConfiguration.swift
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PeerConfiguration.swift
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PeerConfiguration.swift
|
||||||
${WG_APPLE_SOURCE_DIR}/Shared/FileManager+Extension.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKitC/x25519.c
|
${WG_APPLE_SOURCE_DIR}/WireGuardKitC/x25519.c
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/Array+ConcurrentMap.swift
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/Array+ConcurrentMap.swift
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/IPAddress+AddrInfo.swift
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/IPAddress+AddrInfo.swift
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PrivateKey.swift
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PrivateKey.swift
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/HevSocksTunnel.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/NELogController.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+WireGuard.swift
|
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+WireGuard.swift
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+OpenVPN.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+Xray.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/WGConfig.swift
|
${CLIENT_ROOT_DIR}/platforms/ios/WGConfig.swift
|
||||||
|
)
|
||||||
|
|
||||||
|
set(NE_XRAY_SOURCES
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/HevSocksTunnel.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+Xray.swift
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/XrayConfig.swift
|
${CLIENT_ROOT_DIR}/platforms/ios/XrayConfig.swift
|
||||||
|
)
|
||||||
|
|
||||||
|
set(NE_OPENVPN_SOURCES
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+OpenVPN.swift
|
||||||
|
)
|
||||||
|
|
||||||
|
set(NE_APPLE_GLUE_SOURCES
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/iosglue.mm
|
${CLIENT_ROOT_DIR}/platforms/ios/iosglue.mm
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(AMNEZIA_IOS_APPLETV)
|
||||||
|
list(APPEND NE_APPLE_GLUE_SOURCES
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/tvos_cgo_stubs.c
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_sources(networkextension PRIVATE ${NE_COMMON_SOURCES})
|
||||||
|
|
||||||
|
if(NOT AMNEZIA_IOS_APPLETV)
|
||||||
|
target_sources(networkextension PRIVATE
|
||||||
|
${NE_WIREGUARD_SOURCES}
|
||||||
|
${NE_OPENVPN_SOURCES}
|
||||||
|
${NE_XRAY_SOURCES}
|
||||||
|
${NE_APPLE_GLUE_SOURCES}
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
target_sources(networkextension PRIVATE
|
||||||
|
${NE_WIREGUARD_SOURCES}
|
||||||
|
${NE_APPLE_GLUE_SOURCES}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_sources(networkextension PRIVATE
|
target_sources(networkextension PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
||||||
)
|
)
|
||||||
@@ -110,21 +172,16 @@ set_property(TARGET networkextension APPEND PROPERTY RESOURCE
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
||||||
)
|
)
|
||||||
|
|
||||||
## Build wireguard-go-version.h
|
|
||||||
execute_process(
|
|
||||||
COMMAND go list -m golang.zx2c4.com/wireguard
|
|
||||||
WORKING_DIRECTORY ${CLIENT_ROOT_DIR}/3rd/wireguard-apple/Sources/WireGuardKitGo
|
|
||||||
OUTPUT_VARIABLE WG_VERSION_FULL
|
|
||||||
)
|
|
||||||
string(REGEX REPLACE ".*v\([0-9.]*\).*" "\\1" WG_VERSION_STRING 1.1.1)
|
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/wireguard-go-version.h.in
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
|
|
||||||
target_sources(networkextension PRIVATE
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
|
|
||||||
|
|
||||||
target_include_directories(networkextension PRIVATE ${CLIENT_ROOT_DIR})
|
target_include_directories(networkextension PRIVATE ${CLIENT_ROOT_DIR})
|
||||||
target_include_directories(networkextension PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
target_include_directories(networkextension PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
target_link_libraries(networkextension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/wireguard/ios/arm64/libwg-go.a)
|
find_package(awg-apple REQUIRED)
|
||||||
|
target_link_libraries(networkextension PRIVATE amnezia::awg-apple)
|
||||||
|
|
||||||
target_link_libraries(networkextension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework)
|
if(NOT AMNEZIA_IOS_APPLETV)
|
||||||
|
find_package(openvpnadapter REQUIRED)
|
||||||
|
target_link_libraries(networkextension PRIVATE amnezia::openvpnadapter)
|
||||||
|
|
||||||
|
find_package(hev-socks5-tunnel REQUIRED)
|
||||||
|
target_link_libraries(networkextension PRIVATE heiher::hev-socks5-tunnel)
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
#ifndef WIREGUARD_GO_VERSION
|
|
||||||
#define WIREGUARD_GO_VERSION "@WG_VERSION_STRING@"
|
|
||||||
#endif // WIREGUARD_GO_VERSION
|
|
||||||
@@ -114,25 +114,14 @@ set_property(TARGET AmneziaVPNNetworkExtension APPEND PROPERTY RESOURCE
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
||||||
)
|
)
|
||||||
|
|
||||||
## Build wireguard-go-version.h
|
|
||||||
execute_process(
|
|
||||||
COMMAND go list -m golang.zx2c4.com/wireguard
|
|
||||||
WORKING_DIRECTORY ${CLIENT_ROOT_DIR}/3rd/wireguard-apple/Sources/WireGuardKitGo
|
|
||||||
OUTPUT_VARIABLE WG_VERSION_FULL
|
|
||||||
)
|
|
||||||
string(REGEX REPLACE ".*v\([0-9.]*\).*" "\\1" WG_VERSION_STRING 1.1.1)
|
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/wireguard-go-version.h.in
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
|
|
||||||
target_sources(AmneziaVPNNetworkExtension PRIVATE
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
|
|
||||||
|
|
||||||
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR})
|
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR})
|
||||||
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/wireguard/macos/universal2/libwg-go.a)
|
find_package(openvpnadapter REQUIRED)
|
||||||
|
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE amnezia::openvpnadapter)
|
||||||
|
|
||||||
message(${CLIENT_ROOT_DIR})
|
find_package(awg-apple REQUIRED)
|
||||||
message(${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/libhev-socks5-tunnel.a)
|
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE amnezia::awg-apple)
|
||||||
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/libhev-socks5-tunnel.a)
|
|
||||||
|
|
||||||
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/Headers)
|
find_package(hev-socks5-tunnel REQUIRED)
|
||||||
|
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE heiher::hev-socks5-tunnel)
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
#ifndef WIREGUARD_GO_VERSION
|
|
||||||
#define WIREGUARD_GO_VERSION "@WG_VERSION_STRING@"
|
|
||||||
#endif // WIREGUARD_GO_VERSION
|
|
||||||
+3
-4
@@ -12,11 +12,11 @@
|
|||||||
#include "Windows.h"
|
#include "Windows.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_IOS)
|
#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
|
||||||
#include "platforms/ios/QtAppDelegate-C-Interface.h"
|
#include "platforms/ios/QtAppDelegate-C-Interface.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(Q_OS_TVOS) && !defined(MACOS_NE)
|
||||||
bool isAnotherInstanceRunning()
|
bool isAnotherInstanceRunning()
|
||||||
{
|
{
|
||||||
QLocalSocket socket;
|
QLocalSocket socket;
|
||||||
@@ -47,7 +47,7 @@ int main(int argc, char *argv[])
|
|||||||
AmneziaApplication app(argc, argv);
|
AmneziaApplication app(argc, argv);
|
||||||
OsSignalHandler::setup();
|
OsSignalHandler::setup();
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(Q_OS_TVOS) && !defined(MACOS_NE)
|
||||||
if (isAnotherInstanceRunning()) {
|
if (isAnotherInstanceRunning()) {
|
||||||
QTimer::singleShot(1000, &app, [&]() { app.quit(); });
|
QTimer::singleShot(1000, &app, [&]() { app.quit(); });
|
||||||
return app.exec();
|
return app.exec();
|
||||||
@@ -75,7 +75,6 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
qInfo().noquote() << QString("Started %1 version %2 %3").arg(APPLICATION_NAME, APP_VERSION, GIT_COMMIT_HASH);
|
qInfo().noquote() << QString("Started %1 version %2 %3").arg(APPLICATION_NAME, APP_VERSION, GIT_COMMIT_HASH);
|
||||||
qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture());
|
qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture());
|
||||||
qInfo().noquote() << QString("SSL backend: %1").arg(QSslSocket::sslLibraryVersionString());
|
|
||||||
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -270,12 +270,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
|||||||
&& !wgConfig.value(amnezia::config_key::initPacketMagicHeader).isUndefined()
|
&& !wgConfig.value(amnezia::config_key::initPacketMagicHeader).isUndefined()
|
||||||
&& !wgConfig.value(amnezia::config_key::responsePacketMagicHeader).isUndefined()
|
&& !wgConfig.value(amnezia::config_key::responsePacketMagicHeader).isUndefined()
|
||||||
&& !wgConfig.value(amnezia::config_key::underloadPacketMagicHeader).isUndefined()
|
&& !wgConfig.value(amnezia::config_key::underloadPacketMagicHeader).isUndefined()
|
||||||
&& !wgConfig.value(amnezia::config_key::transportPacketMagicHeader).isUndefined()
|
&& !wgConfig.value(amnezia::config_key::transportPacketMagicHeader).isUndefined()) {
|
||||||
&& !wgConfig.value(amnezia::config_key::specialJunk1).isUndefined()
|
|
||||||
&& !wgConfig.value(amnezia::config_key::specialJunk2).isUndefined()
|
|
||||||
&& !wgConfig.value(amnezia::config_key::specialJunk3).isUndefined()
|
|
||||||
&& !wgConfig.value(amnezia::config_key::specialJunk4).isUndefined()
|
|
||||||
&& !wgConfig.value(amnezia::config_key::specialJunk5).isUndefined()) {
|
|
||||||
json.insert(amnezia::config_key::junkPacketCount, wgConfig.value(amnezia::config_key::junkPacketCount));
|
json.insert(amnezia::config_key::junkPacketCount, wgConfig.value(amnezia::config_key::junkPacketCount));
|
||||||
json.insert(amnezia::config_key::junkPacketMinSize, wgConfig.value(amnezia::config_key::junkPacketMinSize));
|
json.insert(amnezia::config_key::junkPacketMinSize, wgConfig.value(amnezia::config_key::junkPacketMinSize));
|
||||||
json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize));
|
json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize));
|
||||||
|
|||||||
@@ -72,9 +72,9 @@ void NetworkWatcher::initialize() {
|
|||||||
connect(m_impl, &NetworkWatcherImpl::unsecuredNetwork, this,
|
connect(m_impl, &NetworkWatcherImpl::unsecuredNetwork, this,
|
||||||
&NetworkWatcher::unsecuredNetwork);
|
&NetworkWatcher::unsecuredNetwork);
|
||||||
connect(m_impl, &NetworkWatcherImpl::networkChanged, this,
|
connect(m_impl, &NetworkWatcherImpl::networkChanged, this,
|
||||||
&NetworkWatcher::networkChange);
|
&NetworkWatcher::networkChanged);
|
||||||
connect(m_impl, &NetworkWatcherImpl::sleepMode, this,
|
connect(m_impl, &NetworkWatcherImpl::wakeup, this,
|
||||||
&NetworkWatcher::onSleepMode);
|
&NetworkWatcher::wakeup);
|
||||||
m_impl->initialize();
|
m_impl->initialize();
|
||||||
|
|
||||||
// Enable sleep/wake monitoring for VPN auto-reconnection
|
// Enable sleep/wake monitoring for VPN auto-reconnection
|
||||||
@@ -97,12 +97,6 @@ void NetworkWatcher::settingsChanged() {
|
|||||||
logger.debug() << "NetworkWatcher settings changed - keeping sleep monitoring active";
|
logger.debug() << "NetworkWatcher settings changed - keeping sleep monitoring active";
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkWatcher::onSleepMode()
|
|
||||||
{
|
|
||||||
logger.debug() << "Resumed from sleep mode";
|
|
||||||
emit sleepMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkWatcher::unsecuredNetwork(const QString& networkName,
|
void NetworkWatcher::unsecuredNetwork(const QString& networkName,
|
||||||
const QString& networkId) {
|
const QString& networkId) {
|
||||||
logger.debug() << "Unsecured network:" << logger.sensitive(networkName)
|
logger.debug() << "Unsecured network:" << logger.sensitive(networkName)
|
||||||
|
|||||||
@@ -29,13 +29,11 @@ public:
|
|||||||
// false to restore.
|
// false to restore.
|
||||||
void simulateDisconnection(bool simulatedDisconnection);
|
void simulateDisconnection(bool simulatedDisconnection);
|
||||||
|
|
||||||
void onSleepMode();
|
|
||||||
|
|
||||||
QNetworkInformation::Reachability getReachability();
|
QNetworkInformation::Reachability getReachability();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void networkChange();
|
void networkChanged();
|
||||||
void sleepMode();
|
void wakeup();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void settingsChanged();
|
void settingsChanged();
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ signals:
|
|||||||
// TODO: Only windows-networkwatcher has this, the other plattforms should
|
// TODO: Only windows-networkwatcher has this, the other plattforms should
|
||||||
// too.
|
// too.
|
||||||
void networkChanged(QString newBSSID);
|
void networkChanged(QString newBSSID);
|
||||||
void sleepMode();
|
void wakeup();
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ import NetworkExtension
|
|||||||
import Network
|
import Network
|
||||||
import os
|
import os
|
||||||
import Darwin
|
import Darwin
|
||||||
|
#if !os(tvOS)
|
||||||
import OpenVPNAdapter
|
import OpenVPNAdapter
|
||||||
|
#endif
|
||||||
|
|
||||||
enum TunnelProtoType: String {
|
enum TunnelProtoType: String {
|
||||||
case wireguard, openvpn, xray
|
case wireguard, openvpn, xray
|
||||||
@@ -38,8 +40,10 @@ struct Constants {
|
|||||||
|
|
||||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
var wgAdapter: WireGuardAdapter?
|
var wgAdapter: WireGuardAdapter?
|
||||||
|
#if !os(tvOS)
|
||||||
var ovpnAdapter: OpenVPNAdapter?
|
var ovpnAdapter: OpenVPNAdapter?
|
||||||
private lazy var openVPNPacketFlowAdapter = PacketTunnelFlowAdapter(flow: packetFlow)
|
private lazy var openVPNPacketFlowAdapter = PacketTunnelFlowAdapter(flow: packetFlow)
|
||||||
|
#endif
|
||||||
private let pathMonitorQueue = DispatchQueue(label: Constants.processQueueName + ".path-monitor")
|
private let pathMonitorQueue = DispatchQueue(label: Constants.processQueueName + ".path-monitor")
|
||||||
private let pathMonitor = NWPathMonitor()
|
private let pathMonitor = NWPathMonitor()
|
||||||
private var didReceiveInitialPathUpdate = false
|
private var didReceiveInitialPathUpdate = false
|
||||||
@@ -49,7 +53,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
var splitTunnelType: Int?
|
var splitTunnelType: Int?
|
||||||
var splitTunnelSites: [String]?
|
var splitTunnelSites: [String]?
|
||||||
|
|
||||||
|
#if !os(tvOS)
|
||||||
let vpnReachability = OpenVPNReachability()
|
let vpnReachability = OpenVPNReachability()
|
||||||
|
#endif
|
||||||
|
|
||||||
var startHandler: ((Error?) -> Void)?
|
var startHandler: ((Error?) -> Void)?
|
||||||
var stopHandler: (() -> Void)?
|
var stopHandler: (() -> Void)?
|
||||||
@@ -57,9 +63,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
|
|
||||||
var activeIfaceIdx: UInt32 = 0
|
var activeIfaceIdx: UInt32 = 0
|
||||||
|
|
||||||
|
#if !os(tvOS)
|
||||||
func openVPNPacketFlow() -> OpenVPNAdapterPacketFlow {
|
func openVPNPacketFlow() -> OpenVPNAdapterPacketFlow {
|
||||||
openVPNPacketFlowAdapter
|
openVPNPacketFlowAdapter
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
super.init()
|
super.init()
|
||||||
@@ -206,9 +214,21 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
errorNotifier: errorNotifier,
|
errorNotifier: errorNotifier,
|
||||||
completionHandler: completionHandler)
|
completionHandler: completionHandler)
|
||||||
case .openvpn:
|
case .openvpn:
|
||||||
|
#if os(tvOS)
|
||||||
|
completionHandler(NSError(domain: "org.amnezia.ne",
|
||||||
|
code: -1002,
|
||||||
|
userInfo: [NSLocalizedDescriptionKey: "OpenVPN backend is not available for tvOS in this build"]))
|
||||||
|
#else
|
||||||
startOpenVPN(completionHandler: completionHandler)
|
startOpenVPN(completionHandler: completionHandler)
|
||||||
|
#endif
|
||||||
case .xray:
|
case .xray:
|
||||||
|
#if os(tvOS)
|
||||||
|
completionHandler(NSError(domain: "org.amnezia.ne",
|
||||||
|
code: -1003,
|
||||||
|
userInfo: [NSLocalizedDescriptionKey: "Xray backend is not available for tvOS in this build"]))
|
||||||
|
#else
|
||||||
startXray(completionHandler: completionHandler)
|
startXray(completionHandler: completionHandler)
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,10 +245,18 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
stopWireguard(with: reason,
|
stopWireguard(with: reason,
|
||||||
completionHandler: completionHandler)
|
completionHandler: completionHandler)
|
||||||
case .openvpn:
|
case .openvpn:
|
||||||
|
#if os(tvOS)
|
||||||
|
completionHandler()
|
||||||
|
#else
|
||||||
stopOpenVPN(with: reason,
|
stopOpenVPN(with: reason,
|
||||||
completionHandler: completionHandler)
|
completionHandler: completionHandler)
|
||||||
|
#endif
|
||||||
case .xray:
|
case .xray:
|
||||||
|
#if os(tvOS)
|
||||||
|
completionHandler()
|
||||||
|
#else
|
||||||
stopXray(completionHandler: completionHandler)
|
stopXray(completionHandler: completionHandler)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,7 +270,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
case .wireguard:
|
case .wireguard:
|
||||||
handleWireguardStatusMessage(messageData, completionHandler: completionHandler)
|
handleWireguardStatusMessage(messageData, completionHandler: completionHandler)
|
||||||
case .openvpn:
|
case .openvpn:
|
||||||
|
#if !os(tvOS)
|
||||||
handleOpenVPNStatusMessage(messageData, completionHandler: completionHandler)
|
handleOpenVPNStatusMessage(messageData, completionHandler: completionHandler)
|
||||||
|
#else
|
||||||
|
completionHandler?(nil)
|
||||||
|
#endif
|
||||||
case .xray:
|
case .xray:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -260,7 +292,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
|
|
||||||
private func handle(networkChange changePath: Network.NWPath, completion: @escaping (Error?) -> Void) {
|
private func handle(networkChange changePath: Network.NWPath, completion: @escaping (Error?) -> Void) {
|
||||||
updateActiveInterfaceIndex(for: changePath)
|
updateActiveInterfaceIndex(for: changePath)
|
||||||
wg_log(.info, message: "Tunnel restarted.")
|
neLog(.info, message: "Tunnel restarted.")
|
||||||
startTunnel(options: nil, completionHandler: completion)
|
startTunnel(options: nil, completionHandler: completion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,16 +343,17 @@ private extension PacketTunnelProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension WireGuardLogLevel {
|
extension WireGuardLogLevel {
|
||||||
var osLogLevel: OSLogType {
|
var osLogLevel: OSLogType {
|
||||||
switch self {
|
switch self {
|
||||||
case .verbose:
|
case .verbose:
|
||||||
return .debug
|
return .debug
|
||||||
case .error:
|
case .error:
|
||||||
return .error
|
return .error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !os(tvOS)
|
||||||
final class PacketTunnelFlowAdapter: NSObject, OpenVPNAdapterPacketFlow {
|
final class PacketTunnelFlowAdapter: NSObject, OpenVPNAdapterPacketFlow {
|
||||||
private let flow: NEPacketTunnelFlow
|
private let flow: NEPacketTunnelFlow
|
||||||
|
|
||||||
@@ -339,6 +372,7 @@ final class PacketTunnelFlowAdapter: NSObject, OpenVPNAdapterPacketFlow {
|
|||||||
flow.writePackets(packets, withProtocols: protocols)
|
flow.writePackets(packets, withProtocols: protocols)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
extension NEProviderStopReason {
|
extension NEProviderStopReason {
|
||||||
var amneziaDescription: String {
|
var amneziaDescription: String {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#if !MACOS_NE
|
#if !MACOS_NE && !TARGET_OS_TV
|
||||||
#include "QRCodeReaderBase.h"
|
#include "QRCodeReaderBase.h"
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|||||||
@@ -959,6 +959,10 @@ void IosController::sendVpnExtensionMessage(NSDictionary* message, std::function
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool IosController::shareText(const QStringList& filesToSend) {
|
bool IosController::shareText(const QStringList& filesToSend) {
|
||||||
|
#if defined(Q_OS_TVOS)
|
||||||
|
Q_UNUSED(filesToSend)
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
NSMutableArray *sharingItems = [NSMutableArray new];
|
NSMutableArray *sharingItems = [NSMutableArray new];
|
||||||
|
|
||||||
for (int i = 0; i < filesToSend.size(); i++) {
|
for (int i = 0; i < filesToSend.size(); i++) {
|
||||||
@@ -967,7 +971,7 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
|||||||
}
|
}
|
||||||
#if !MACOS_NE
|
#if !MACOS_NE
|
||||||
UIViewController *qtController = getViewController();
|
UIViewController *qtController = getViewController();
|
||||||
if (!qtController) return;
|
if (!qtController) return false;
|
||||||
|
|
||||||
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
|
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
|
||||||
#endif
|
#endif
|
||||||
@@ -991,23 +995,25 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
|||||||
wait.exec();
|
wait.exec();
|
||||||
|
|
||||||
return isAccepted;
|
return isAccepted;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QString IosController::openFile() {
|
QString IosController::openFile() {
|
||||||
#if !MACOS_NE
|
#if defined(Q_OS_TVOS)
|
||||||
|
return QString();
|
||||||
|
#elif !MACOS_NE
|
||||||
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen];
|
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen];
|
||||||
|
|
||||||
DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init];
|
DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init];
|
||||||
documentPicker.delegate = documentPickerDelegate;
|
documentPicker.delegate = documentPickerDelegate;
|
||||||
|
|
||||||
UIViewController *qtController = getViewController();
|
UIViewController *qtController = getViewController();
|
||||||
if (!qtController) return;
|
if (!qtController) return QString();
|
||||||
|
|
||||||
[qtController presentViewController:documentPicker animated:YES completion:nil];
|
[qtController presentViewController:documentPicker animated:YES completion:nil];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
__block QString filePath;
|
__block QString filePath;
|
||||||
#if !MACOS_NE
|
#if !MACOS_NE && !defined(Q_OS_TVOS)
|
||||||
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
|
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
|
||||||
if (path) {
|
if (path) {
|
||||||
filePath = QString::fromUtf8(path.UTF8String);
|
filePath = QString::fromUtf8(path.UTF8String);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#import <NetworkExtension/NetworkExtension.h>
|
#import <NetworkExtension/NetworkExtension.h>
|
||||||
#import <NetworkExtension/NETunnelProviderSession.h>
|
#import <NetworkExtension/NETunnelProviderSession.h>
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
|
||||||
#if !MACOS_NE
|
#if !MACOS_NE
|
||||||
#include <UIKit/UIKit.h>
|
#include <UIKit/UIKit.h>
|
||||||
@@ -21,7 +22,7 @@ class IosController;
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
typedef void (^DocumentPickerClosedCallback)(NSString *path);
|
typedef void (^DocumentPickerClosedCallback)(NSString *path);
|
||||||
#if !MACOS_NE
|
#if !MACOS_NE && !TARGET_OS_TV
|
||||||
@interface DocumentPickerDelegate : NSObject <UIDocumentPickerDelegate>
|
@interface DocumentPickerDelegate : NSObject <UIDocumentPickerDelegate>
|
||||||
|
|
||||||
@property (nonatomic, copy) DocumentPickerClosedCallback documentPickerClosedCallback;
|
@property (nonatomic, copy) DocumentPickerClosedCallback documentPickerClosedCallback;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#if !MACOS_NE
|
#if !MACOS_NE && !TARGET_OS_TV
|
||||||
@implementation DocumentPickerDelegate
|
@implementation DocumentPickerDelegate
|
||||||
|
|
||||||
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
|
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
|
||||||
|
|||||||
@@ -7,6 +7,24 @@
|
|||||||
#import <UserNotifications/UserNotifications.h>
|
#import <UserNotifications/UserNotifications.h>
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#if defined(Q_OS_TVOS)
|
||||||
|
|
||||||
|
IOSNotificationHandler::IOSNotificationHandler(QObject* parent) : NotificationHandler(parent) {}
|
||||||
|
|
||||||
|
IOSNotificationHandler::~IOSNotificationHandler() {}
|
||||||
|
|
||||||
|
void IOSNotificationHandler::notify(NotificationHandler::Message type,
|
||||||
|
const QString& title,
|
||||||
|
const QString& message,
|
||||||
|
int timerMsec) {
|
||||||
|
Q_UNUSED(type)
|
||||||
|
Q_UNUSED(title)
|
||||||
|
Q_UNUSED(message)
|
||||||
|
Q_UNUSED(timerMsec)
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
#if !MACOS_NE
|
#if !MACOS_NE
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
@@ -172,3 +190,5 @@ void IOSNotificationHandler::notify(NotificationHandler::Message type, const QSt
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif // Q_OS_TVOS
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
/*
|
||||||
|
* tvOS does not export these iOS runtime helpers used by Go cgo archives.
|
||||||
|
* WireGuardKitGo references them indirectly; provide no-op stubs for tvOS.
|
||||||
|
*/
|
||||||
|
void darwin_arm_init_mach_exception_handler(void) {}
|
||||||
|
void darwin_arm_init_thread_exception_port(void) {}
|
||||||
@@ -80,7 +80,7 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
|
|||||||
|
|
||||||
QDir appPath(QCoreApplication::applicationDirPath());
|
QDir appPath(QCoreApplication::applicationDirPath());
|
||||||
QStringList wgArgs = {"-f", "amn0"};
|
QStringList wgArgs = {"-f", "amn0"};
|
||||||
m_tunnel.start(appPath.filePath("../../client/bin/wireguard-go"), wgArgs);
|
m_tunnel.start(appPath.filePath("amneziawg-go"), wgArgs);
|
||||||
if (!m_tunnel.waitForStarted(WG_TUN_PROC_TIMEOUT)) {
|
if (!m_tunnel.waitForStarted(WG_TUN_PROC_TIMEOUT)) {
|
||||||
logger.error() << "Unable to start tunnel process due to timeout";
|
logger.error() << "Unable to start tunnel process due to timeout";
|
||||||
m_tunnel.kill();
|
m_tunnel.kill();
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ void LinuxNetworkWatcher::initialize() {
|
|||||||
connect(m_worker, &LinuxNetworkWatcherWorker::unsecuredNetwork, this,
|
connect(m_worker, &LinuxNetworkWatcherWorker::unsecuredNetwork, this,
|
||||||
&LinuxNetworkWatcher::unsecuredNetwork);
|
&LinuxNetworkWatcher::unsecuredNetwork);
|
||||||
|
|
||||||
connect(m_worker, &LinuxNetworkWatcherWorker::sleepMode, this,
|
connect(m_worker, &LinuxNetworkWatcherWorker::wakeup, this,
|
||||||
&NetworkWatcherImpl::sleepMode);
|
&NetworkWatcherImpl::wakeup);
|
||||||
|
|
||||||
// Let's wait a few seconds to allow the UI to be fully loaded and shown.
|
// Let's wait a few seconds to allow the UI to be fully loaded and shown.
|
||||||
// This is not strictly needed, but it's better for user experience because
|
// This is not strictly needed, but it's better for user experience because
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ void LinuxNetworkWatcherWorker::checkDevices() {
|
|||||||
void LinuxNetworkWatcherWorker::NMStateChanged(quint32 state)
|
void LinuxNetworkWatcherWorker::NMStateChanged(quint32 state)
|
||||||
{
|
{
|
||||||
if (state == NM_STATE_ASLEEP) {
|
if (state == NM_STATE_ASLEEP) {
|
||||||
emit sleepMode();
|
emit wakeup();
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug() << "NMStateChanged " << state;
|
logger.debug() << "NMStateChanged " << state;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class LinuxNetworkWatcherWorker final : public QObject {
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
||||||
void sleepMode();
|
void wakeup();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void initialize();
|
void initialize();
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) {
|
|||||||
|
|
||||||
QDir appPath(QCoreApplication::applicationDirPath());
|
QDir appPath(QCoreApplication::applicationDirPath());
|
||||||
QStringList wgArgs = {"-f", "utun"};
|
QStringList wgArgs = {"-f", "utun"};
|
||||||
m_tunnel.start(appPath.filePath("wireguard-go"), wgArgs);
|
m_tunnel.start(appPath.filePath("amneziawg-go"), wgArgs);
|
||||||
if (!m_tunnel.waitForStarted(WG_TUN_PROC_TIMEOUT)) {
|
if (!m_tunnel.waitForStarted(WG_TUN_PROC_TIMEOUT)) {
|
||||||
logger.error() << "Unable to start tunnel process due to timeout";
|
logger.error() << "Unable to start tunnel process due to timeout";
|
||||||
m_tunnel.kill();
|
m_tunnel.kill();
|
||||||
|
|||||||
@@ -173,10 +173,10 @@ void PowerNotificationsListener::sleepWakeupCallBack(void *refParam, io_service_
|
|||||||
|
|
||||||
case kIOMessageSystemHasPoweredOn:
|
case kIOMessageSystemHasPoweredOn:
|
||||||
/* Announces that the system and its devices have woken up. */
|
/* Announces that the system and its devices have woken up. */
|
||||||
logger.debug() << "System has powered on - emitting sleepMode signal from dedicated CFRunLoop thread";
|
logger.debug() << "System has powered on - emitting wakeup signal from dedicated CFRunLoop thread";
|
||||||
if (listener->m_watcher) {
|
if (listener->m_watcher) {
|
||||||
// Use QMetaObject::invokeMethod for thread-safe signal emission
|
// Use QMetaObject::invokeMethod for thread-safe signal emission
|
||||||
QMetaObject::invokeMethod(listener->m_watcher, "sleepMode", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(listener->m_watcher, "wakeup", Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ void WindowsDaemon::prepareActivation(const InterfaceConfig& config, int inetAda
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WindowsDaemon::activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex) {
|
void WindowsDaemon::activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex) {
|
||||||
|
if (m_splitTunnelManager == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
if (config.m_vpnDisabledApps.length() > 0) {
|
if (config.m_vpnDisabledApps.length() > 0) {
|
||||||
m_splitTunnelManager->start(m_inetAdapterIndex, vpnAdapterIndex);
|
m_splitTunnelManager->start(m_inetAdapterIndex, vpnAdapterIndex);
|
||||||
m_splitTunnelManager->excludeApps(config.m_vpnDisabledApps);
|
m_splitTunnelManager->excludeApps(config.m_vpnDisabledApps);
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ LRESULT WindowsNetworkWatcher::PowerWndProcCallback(HWND hwnd, UINT uMsg, WPARAM
|
|||||||
switch (uMsg) {
|
switch (uMsg) {
|
||||||
case WM_POWERBROADCAST:
|
case WM_POWERBROADCAST:
|
||||||
if (wParam == PBT_APMRESUMESUSPEND) {
|
if (wParam == PBT_APMRESUMESUSPEND) {
|
||||||
emit obj->sleepMode();
|
emit obj->wakeup();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -232,12 +232,6 @@ ErrorCode OpenVpnProtocol::start()
|
|||||||
return ErrorCode::AmneziaServiceConnectionFailed;
|
return ErrorCode::AmneziaServiceConnectionFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_openVpnProcess->waitForSource(5000);
|
|
||||||
if (!m_openVpnProcess->isInitialized()) {
|
|
||||||
qWarning() << "IpcProcess replica is not connected!";
|
|
||||||
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
|
|
||||||
return ErrorCode::AmneziaServiceConnectionFailed;
|
|
||||||
}
|
|
||||||
m_openVpnProcess->setProgram(PermittedProcess::OpenVPN);
|
m_openVpnProcess->setProgram(PermittedProcess::OpenVPN);
|
||||||
QStringList arguments({
|
QStringList arguments({
|
||||||
"--config", configPath(), "--management", m_managementHost, QString::number(mgmtPort),
|
"--config", configPath(), "--management", m_managementHost, QString::number(mgmtPort),
|
||||||
@@ -246,13 +240,13 @@ ErrorCode OpenVpnProtocol::start()
|
|||||||
m_openVpnProcess->setArguments(arguments);
|
m_openVpnProcess->setArguments(arguments);
|
||||||
|
|
||||||
qDebug() << arguments.join(" ");
|
qDebug() << arguments.join(" ");
|
||||||
connect(m_openVpnProcess.data(), &PrivilegedProcess::errorOccurred,
|
connect(m_openVpnProcess.data(), &IpcProcessInterfaceReplica::errorOccurred,
|
||||||
[&](QProcess::ProcessError error) { qDebug() << "PrivilegedProcess errorOccurred" << error; });
|
[&](QProcess::ProcessError error) { qDebug() << "PrivilegedProcess errorOccurred" << error; });
|
||||||
|
|
||||||
connect(m_openVpnProcess.data(), &PrivilegedProcess::stateChanged,
|
connect(m_openVpnProcess.data(), &IpcProcessInterfaceReplica::stateChanged,
|
||||||
[&](QProcess::ProcessState newState) { qDebug() << "PrivilegedProcess stateChanged" << newState; });
|
[&](QProcess::ProcessState newState) { qDebug() << "PrivilegedProcess stateChanged" << newState; });
|
||||||
|
|
||||||
connect(m_openVpnProcess.data(), &PrivilegedProcess::finished, this,
|
connect(m_openVpnProcess.data(), &IpcProcessInterfaceReplica::finished, this,
|
||||||
[&]() { setConnectionState(Vpn::ConnectionState::Disconnected); });
|
[&]() { setConnectionState(Vpn::ConnectionState::Disconnected); });
|
||||||
|
|
||||||
m_openVpnProcess->start();
|
m_openVpnProcess->start();
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ private:
|
|||||||
void updateRouteGateway(QString line);
|
void updateRouteGateway(QString line);
|
||||||
void updateVpnGateway(const QString &line);
|
void updateVpnGateway(const QString &line);
|
||||||
|
|
||||||
QSharedPointer<PrivilegedProcess> m_openVpnProcess;
|
QSharedPointer<IpcProcessInterfaceReplica> m_openVpnProcess;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // OPENVPNPROTOCOL_H
|
#endif // OPENVPNPROTOCOL_H
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ namespace amnezia
|
|||||||
|
|
||||||
constexpr char defaultPort[] = "51820";
|
constexpr char defaultPort[] = "51820";
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_TVOS) || defined(MACOS_NE)
|
||||||
constexpr char defaultMtu[] = "1280";
|
constexpr char defaultMtu[] = "1280";
|
||||||
#else
|
#else
|
||||||
constexpr char defaultMtu[] = "1376";
|
constexpr char defaultMtu[] = "1376";
|
||||||
@@ -210,7 +210,7 @@ namespace amnezia
|
|||||||
namespace awg
|
namespace awg
|
||||||
{
|
{
|
||||||
constexpr char defaultPort[] = "55424";
|
constexpr char defaultPort[] = "55424";
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_TVOS) || defined(MACOS_NE)
|
||||||
constexpr char defaultMtu[] = "1280";
|
constexpr char defaultMtu[] = "1280";
|
||||||
#else
|
#else
|
||||||
constexpr char defaultMtu[] = "1376";
|
constexpr char defaultMtu[] = "1376";
|
||||||
@@ -233,7 +233,7 @@ namespace amnezia
|
|||||||
constexpr char defaultResponsePacketMagicHeader[] = "3288052141";
|
constexpr char defaultResponsePacketMagicHeader[] = "3288052141";
|
||||||
constexpr char defaultTransportPacketMagicHeader[] = "2528465083";
|
constexpr char defaultTransportPacketMagicHeader[] = "2528465083";
|
||||||
constexpr char defaultUnderloadPacketMagicHeader[] = "1766607858";
|
constexpr char defaultUnderloadPacketMagicHeader[] = "1766607858";
|
||||||
constexpr char defaultSpecialJunk1[] = "<b 0x084481800001000300000000077469636b65747306776964676574096b696e6f706f69736b0272750000010001c00c0005000100000039001806776964676574077469636b6574730679616e646578c025c0390005000100000039002b1765787465726e616c2d7469636b6574732d776964676574066166697368610679616e646578036e657400c05d000100010000001c000457fafe25>";
|
constexpr char defaultSpecialJunk1[] = "<r 2><b 0x858000010001000000000669636c6f756403636f6d0000010001c00c000100010000105a00044d583737>";
|
||||||
constexpr char defaultSpecialJunk2[] = "";
|
constexpr char defaultSpecialJunk2[] = "";
|
||||||
constexpr char defaultSpecialJunk3[] = "";
|
constexpr char defaultSpecialJunk3[] = "";
|
||||||
constexpr char defaultSpecialJunk4[] = "";
|
constexpr char defaultSpecialJunk4[] = "";
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "core/errorstrings.h"
|
#include "core/errorstrings.h"
|
||||||
#include "vpnprotocol.h"
|
#include "vpnprotocol.h"
|
||||||
|
|
||||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) and !defined MACOS_NE || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
#if defined(Q_OS_WINDOWS) || (defined(Q_OS_MACX) && !defined(Q_OS_IOS) && !defined(Q_OS_TVOS) && !defined(MACOS_NE)) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||||
#include "openvpnovercloakprotocol.h"
|
#include "openvpnovercloakprotocol.h"
|
||||||
#include "openvpnprotocol.h"
|
#include "openvpnprotocol.h"
|
||||||
#include "shadowsocksvpnprotocol.h"
|
#include "shadowsocksvpnprotocol.h"
|
||||||
@@ -114,7 +114,7 @@ VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject &
|
|||||||
#if defined(Q_OS_WINDOWS)
|
#if defined(Q_OS_WINDOWS)
|
||||||
case DockerContainer::Ipsec: return new Ikev2Protocol(configuration);
|
case DockerContainer::Ipsec: return new Ikev2Protocol(configuration);
|
||||||
#endif
|
#endif
|
||||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) and !defined MACOS_NE || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
#if defined(Q_OS_WINDOWS) || (defined(Q_OS_MACX) && !defined(Q_OS_IOS) && !defined(Q_OS_TVOS) && !defined(MACOS_NE)) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||||
case DockerContainer::OpenVpn: return new OpenVpnProtocol(configuration);
|
case DockerContainer::OpenVpn: return new OpenVpnProtocol(configuration);
|
||||||
case DockerContainer::Cloak: return new OpenVpnOverCloakProtocol(configuration);
|
case DockerContainer::Cloak: return new OpenVpnOverCloakProtocol(configuration);
|
||||||
case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration);
|
case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject *
|
|||||||
m_impl.reset(new LocalSocketController());
|
m_impl.reset(new LocalSocketController());
|
||||||
connect(m_impl.get(), &ControllerImpl::connected, this,
|
connect(m_impl.get(), &ControllerImpl::connected, this,
|
||||||
[this](const QString &pubkey, const QDateTime &connectionTimestamp) {
|
[this](const QString &pubkey, const QDateTime &connectionTimestamp) {
|
||||||
emit connectionStateChanged(Vpn::ConnectionState::Connected);
|
setConnectionState(Vpn::ConnectionState::Connected);
|
||||||
});
|
});
|
||||||
connect(m_impl.get(), &ControllerImpl::statusUpdated, this,
|
connect(m_impl.get(), &ControllerImpl::statusUpdated, this,
|
||||||
[this](const QString& serverIpv4Gateway,
|
[this](const QString& serverIpv4Gateway,
|
||||||
@@ -38,7 +38,7 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject *
|
|||||||
});
|
});
|
||||||
|
|
||||||
connect(m_impl.get(), &ControllerImpl::disconnected, this,
|
connect(m_impl.get(), &ControllerImpl::disconnected, this,
|
||||||
[this]() { emit connectionStateChanged(Vpn::ConnectionState::Disconnected); });
|
[this]() { setConnectionState(Vpn::ConnectionState::Disconnected); });
|
||||||
m_impl->initialize(nullptr, nullptr);
|
m_impl->initialize(nullptr, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "xrayprotocol.h"
|
#include "xrayprotocol.h"
|
||||||
|
|
||||||
#include "core/ipcclient.h"
|
#include "core/ipcclient.h"
|
||||||
|
#include "ipc.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
#include "core/networkUtilities.h"
|
#include "core/networkUtilities.h"
|
||||||
|
|
||||||
@@ -9,14 +10,37 @@
|
|||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QNetworkInterface>
|
#include <QNetworkInterface>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
#include <QtCore/qlogging.h>
|
||||||
|
#include <QtCore/qobjectdefs.h>
|
||||||
|
#include <QtCore/qprocess.h>
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
static const QString tunName = "utun22";
|
||||||
|
#else
|
||||||
|
static const QString tunName = "tun2";
|
||||||
|
#endif
|
||||||
|
|
||||||
XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) : VpnProtocol(configuration, parent)
|
XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) : VpnProtocol(configuration, parent)
|
||||||
{
|
{
|
||||||
readXrayConfiguration(configuration);
|
|
||||||
m_routeGateway = NetworkUtilities::getGatewayAndIface().first;
|
|
||||||
m_vpnGateway = amnezia::protocols::xray::defaultLocalAddr;
|
m_vpnGateway = amnezia::protocols::xray::defaultLocalAddr;
|
||||||
m_vpnLocalAddress = amnezia::protocols::xray::defaultLocalAddr;
|
m_vpnLocalAddress = amnezia::protocols::xray::defaultLocalAddr;
|
||||||
m_t2sProcess = IpcClient::InterfaceTun2Socks();
|
m_routeGateway = NetworkUtilities::getGatewayAndIface().first;
|
||||||
|
|
||||||
|
m_routeMode = static_cast<Settings::RouteMode>(configuration.value(amnezia::config_key::splitTunnelType).toInt());
|
||||||
|
m_remoteAddress = NetworkUtilities::getIPAddress(m_rawConfig.value(amnezia::config_key::hostName).toString());
|
||||||
|
|
||||||
|
const QString primaryDns = configuration.value(amnezia::config_key::dns1).toString();
|
||||||
|
m_dnsServers.push_back(QHostAddress(primaryDns));
|
||||||
|
if (primaryDns != amnezia::protocols::dns::amneziaDnsIp) {
|
||||||
|
const QString secondaryDns = configuration.value(amnezia::config_key::dns2).toString();
|
||||||
|
m_dnsServers.push_back(QHostAddress(secondaryDns));
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject xrayConfiguration = configuration.value(ProtocolProps::key_proto_config_data(Proto::Xray)).toObject();
|
||||||
|
if (xrayConfiguration.isEmpty()) {
|
||||||
|
xrayConfiguration = configuration.value(ProtocolProps::key_proto_config_data(Proto::SSXray)).toObject();
|
||||||
|
}
|
||||||
|
m_xrayConfig = xrayConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
XrayProtocol::~XrayProtocol()
|
XrayProtocol::~XrayProtocol()
|
||||||
@@ -29,72 +53,16 @@ ErrorCode XrayProtocol::start()
|
|||||||
{
|
{
|
||||||
qDebug() << "XrayProtocol::start()";
|
qDebug() << "XrayProtocol::start()";
|
||||||
|
|
||||||
const ErrorCode err = IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
return IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||||
iface->xrayStart(QJsonDocument(m_xrayConfig).toJson());
|
auto xrayStart = iface->xrayStart(QJsonDocument(m_xrayConfig).toJson());
|
||||||
return ErrorCode::NoError;
|
if (!xrayStart.waitForFinished() || !xrayStart.returnValue()) {
|
||||||
|
qCritical() << "Failed to start xray";
|
||||||
|
return ErrorCode::XrayExecutableCrashed;
|
||||||
|
}
|
||||||
|
return startTun2Socks();
|
||||||
}, [] () {
|
}, [] () {
|
||||||
return ErrorCode::AmneziaServiceConnectionFailed;
|
return ErrorCode::AmneziaServiceConnectionFailed;
|
||||||
});
|
});
|
||||||
if (err != ErrorCode::NoError)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
setConnectionState(Vpn::ConnectionState::Connecting);
|
|
||||||
return startTun2Sock();
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode XrayProtocol::startTun2Sock()
|
|
||||||
{
|
|
||||||
m_t2sProcess->start();
|
|
||||||
|
|
||||||
connect(m_t2sProcess.data(), &IpcProcessTun2SocksReplica::stateChanged, this,
|
|
||||||
[&](QProcess::ProcessState newState) { qDebug() << "PrivilegedProcess stateChanged" << newState; });
|
|
||||||
|
|
||||||
connect(m_t2sProcess.data(), &IpcProcessTun2SocksReplica::setConnectionState, this, [&](int vpnState) {
|
|
||||||
qDebug() << "PrivilegedProcess setConnectionState " << vpnState;
|
|
||||||
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
|
||||||
if (vpnState == Vpn::ConnectionState::Connected) {
|
|
||||||
setConnectionState(Vpn::ConnectionState::Connecting);
|
|
||||||
QList<QHostAddress> dnsAddr;
|
|
||||||
|
|
||||||
dnsAddr.push_back(QHostAddress(m_primaryDNS));
|
|
||||||
// We don't use secondary DNS if primary DNS is AmneziaDNS
|
|
||||||
if (!m_primaryDNS.contains(amnezia::protocols::dns::amneziaDnsIp)) {
|
|
||||||
dnsAddr.push_back(QHostAddress(m_secondaryDNS));
|
|
||||||
}
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
QThread::msleep(8000);
|
|
||||||
#endif
|
|
||||||
#ifdef Q_OS_MACOS
|
|
||||||
QThread::msleep(5000);
|
|
||||||
iface->createTun("utun22", amnezia::protocols::xray::defaultLocalAddr);
|
|
||||||
iface->updateResolvers("utun22", dnsAddr);
|
|
||||||
#endif
|
|
||||||
#ifdef Q_OS_LINUX
|
|
||||||
QThread::msleep(1000);
|
|
||||||
iface->createTun("tun2", amnezia::protocols::xray::defaultLocalAddr);
|
|
||||||
iface->updateResolvers("tun2", dnsAddr);
|
|
||||||
#endif
|
|
||||||
if (m_routeMode == Settings::RouteMode::VpnAllSites) {
|
|
||||||
iface->routeAddList(m_vpnGateway, QStringList() << "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");
|
|
||||||
}
|
|
||||||
iface->StopRoutingIpv6();
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
iface->updateResolvers("tun2", dnsAddr);
|
|
||||||
#endif
|
|
||||||
setConnectionState(Vpn::ConnectionState::Connected);
|
|
||||||
}
|
|
||||||
#if !defined(Q_OS_MACOS)
|
|
||||||
if (vpnState == Vpn::ConnectionState::Disconnected) {
|
|
||||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
|
||||||
iface->deleteTun("tun2");
|
|
||||||
iface->StartRoutingIpv6();
|
|
||||||
iface->clearSavedRoutes();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void XrayProtocol::stop()
|
void XrayProtocol::stop()
|
||||||
@@ -102,43 +70,177 @@ void XrayProtocol::stop()
|
|||||||
qDebug() << "XrayProtocol::stop()";
|
qDebug() << "XrayProtocol::stop()";
|
||||||
|
|
||||||
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
|
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||||
#ifdef AMNEZIA_DESKTOP
|
auto disableKillSwitch = iface->disableKillSwitch();
|
||||||
QRemoteObjectPendingReply<bool> StartRoutingIpv6Resp = iface->StartRoutingIpv6();
|
if (!disableKillSwitch.waitForFinished() || !disableKillSwitch.returnValue())
|
||||||
if (!StartRoutingIpv6Resp.waitForFinished(1000)) {
|
qWarning() << "Failed to disable killswitch";
|
||||||
qWarning() << "XrayProtocol::stop(): Failed to start routing ipv6";
|
|
||||||
}
|
|
||||||
|
|
||||||
QRemoteObjectPendingReply<bool> restoreResolvers = iface->restoreResolvers();
|
auto StartRoutingIpv6 = iface->StartRoutingIpv6();
|
||||||
if (!restoreResolvers.waitForFinished(1000)) {
|
if (!StartRoutingIpv6.waitForFinished() || !StartRoutingIpv6.returnValue())
|
||||||
qWarning() << "XrayProtocol::stop(): Failed to restore resolvers";
|
qWarning() << "Failed to start routing ipv6";
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(Q_OS_MACOS)
|
auto restoreResolvers = iface->restoreResolvers();
|
||||||
QRemoteObjectPendingReply<bool> deleteTunResp = iface->deleteTun("tun2");
|
if (!restoreResolvers.waitForFinished() || !restoreResolvers.returnValue())
|
||||||
if (!deleteTunResp.waitForFinished(1000)) {
|
qWarning() << "Failed to restore resolvers";
|
||||||
qWarning() << "XrayProtocol::stop(): Failed to delete tun";
|
|
||||||
}
|
auto deleteTun = iface->deleteTun(tunName);
|
||||||
#endif
|
if (!deleteTun.waitForFinished() || !deleteTun.returnValue())
|
||||||
#endif
|
qWarning() << "Failed to delete tun";
|
||||||
iface->xrayStop();
|
|
||||||
|
auto xrayStop = iface->xrayStop();
|
||||||
|
if (!xrayStop.waitForFinished() || !xrayStop.returnValue())
|
||||||
|
qWarning() << "Failed to stop xray";
|
||||||
});
|
});
|
||||||
|
|
||||||
if (m_t2sProcess) {
|
if (m_tun2socksProcess) {
|
||||||
m_t2sProcess->stop();
|
m_tun2socksProcess->blockSignals(true);
|
||||||
QThread::msleep(200);
|
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
m_tun2socksProcess->terminate();
|
||||||
|
auto waitForFinished = m_tun2socksProcess->waitForFinished(1000);
|
||||||
|
if (!waitForFinished.waitForFinished() || !waitForFinished.returnValue()) {
|
||||||
|
qWarning() << "Failed to terminate tun2socks. Killing the process...";
|
||||||
|
m_tun2socksProcess->kill();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// terminate does not do anything useful on Windows
|
||||||
|
// so just kill the process
|
||||||
|
m_tun2socksProcess->kill();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_tun2socksProcess->close();
|
||||||
|
m_tun2socksProcess.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
void XrayProtocol::readXrayConfiguration(const QJsonObject &configuration)
|
ErrorCode XrayProtocol::startTun2Socks()
|
||||||
{
|
{
|
||||||
QJsonObject xrayConfiguration = configuration.value(ProtocolProps::key_proto_config_data(Proto::Xray)).toObject();
|
m_tun2socksProcess = IpcClient::CreatePrivilegedProcess();
|
||||||
if (xrayConfiguration.isEmpty()) {
|
if (!m_tun2socksProcess->waitForSource()) {
|
||||||
xrayConfiguration = configuration.value(ProtocolProps::key_proto_config_data(Proto::SSXray)).toObject();
|
return ErrorCode::AmneziaServiceConnectionFailed;
|
||||||
}
|
}
|
||||||
m_xrayConfig = xrayConfiguration;
|
|
||||||
m_routeMode = static_cast<Settings::RouteMode>(configuration.value(amnezia::config_key::splitTunnelType).toInt());
|
m_tun2socksProcess->setProgram(PermittedProcess::Tun2Socks);
|
||||||
m_primaryDNS = configuration.value(amnezia::config_key::dns1).toString();
|
m_tun2socksProcess->setArguments({"-device", QString("tun://%1").arg(tunName), "-proxy", "socks5://127.0.0.1:10808" });
|
||||||
m_secondaryDNS = configuration.value(amnezia::config_key::dns2).toString();
|
|
||||||
|
connect(m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::readyReadStandardError, this, [this]() {
|
||||||
|
auto readAllStandardError = m_tun2socksProcess->readAllStandardError();
|
||||||
|
if (!readAllStandardError.waitForFinished()) {
|
||||||
|
qWarning() << "Failed to read output from tun2socks";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString line = readAllStandardError.returnValue();
|
||||||
|
|
||||||
|
if (!line.contains("[TCP]") && !line.contains("[UDP]"))
|
||||||
|
qDebug() << "[tun2socks]:" << line;
|
||||||
|
|
||||||
|
if (line.contains("[STACK] tun://") && line.contains("<-> socks5://127.0.0.1")) {
|
||||||
|
disconnect(m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::readyReadStandardOutput, this, nullptr);
|
||||||
|
|
||||||
|
if (ErrorCode res = setupRouting(); res != ErrorCode::NoError) {
|
||||||
|
stop();
|
||||||
|
setLastError(res);
|
||||||
|
} else {
|
||||||
|
setConnectionState(Vpn::ConnectionState::Connected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
connect(m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::finished, this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||||
|
if (exitStatus == QProcess::ExitStatus::CrashExit) {
|
||||||
|
qCritical() << "Tun2socks process crashed!";
|
||||||
|
} else {
|
||||||
|
qCritical() << QString("Tun2socks process was closed with %1 exit code").arg(exitCode);
|
||||||
|
}
|
||||||
|
stop();
|
||||||
|
setLastError(ErrorCode::Tun2SockExecutableCrashed);
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
m_tun2socksProcess->start();
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode XrayProtocol::setupRouting() {
|
||||||
|
return IpcClient::withInterface([this](QSharedPointer<IpcInterfaceReplica> iface) -> ErrorCode {
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
const int inetAdapterIndex = NetworkUtilities::AdapterIndexTo(QHostAddress(m_remoteAddress));
|
||||||
|
#endif
|
||||||
|
auto createTun = iface->createTun(tunName, amnezia::protocols::xray::defaultLocalAddr);
|
||||||
|
if (!createTun.waitForFinished() || !createTun.returnValue()) {
|
||||||
|
qCritical() << "Failed to assign IP address for TUN";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto updateResolvers = iface->updateResolvers(tunName, m_dnsServers);
|
||||||
|
if (!updateResolvers.waitForFinished() || !updateResolvers.returnValue()) {
|
||||||
|
qCritical() << "Failed to set DNS resolvers for TUN";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
int vpnAdapterIndex = -1;
|
||||||
|
QList<QNetworkInterface> netInterfaces = QNetworkInterface::allInterfaces();
|
||||||
|
for (auto& netInterface : netInterfaces) {
|
||||||
|
for (auto& address : netInterface.addressEntries()) {
|
||||||
|
if (m_vpnLocalAddress == address.ip().toString())
|
||||||
|
vpnAdapterIndex = netInterface.index();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static const int vpnAdapterIndex = 0;
|
||||||
|
#endif
|
||||||
|
const bool killSwitchEnabled = QVariant(m_rawConfig.value(config_key::killSwitchOption).toString()).toBool();
|
||||||
|
if (killSwitchEnabled) {
|
||||||
|
if (vpnAdapterIndex != -1) {
|
||||||
|
QJsonObject config = m_rawConfig;
|
||||||
|
config.insert("vpnServer", m_remoteAddress);
|
||||||
|
|
||||||
|
auto enableKillSwitch = IpcClient::Interface()->enableKillSwitch(config, vpnAdapterIndex);
|
||||||
|
if (!enableKillSwitch.waitForFinished() || !enableKillSwitch.returnValue()) {
|
||||||
|
qCritical() << "Failed to enable killswitch";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
qWarning() << "Failed to get vpnAdapterIndex. Killswitch disabled";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_routeMode == Settings::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", "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);
|
||||||
|
if (!routeAddList.waitForFinished() || routeAddList.returnValue() != subnets.count()) {
|
||||||
|
qCritical() << "Failed to set routes for TUN";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto StopRoutingIpv6 = iface->StopRoutingIpv6();
|
||||||
|
if (!StopRoutingIpv6.waitForFinished() || !StopRoutingIpv6.returnValue()) {
|
||||||
|
qCritical() << "Failed to disable IPv6 routing";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
if (inetAdapterIndex != -1 && vpnAdapterIndex != -1) {
|
||||||
|
QJsonObject config = m_rawConfig;
|
||||||
|
config.insert("inetAdapterIndex", inetAdapterIndex);
|
||||||
|
config.insert("vpnAdapterIndex", vpnAdapterIndex);
|
||||||
|
config.insert("vpnGateway", m_vpnGateway);
|
||||||
|
config.insert("vpnServer", m_remoteAddress);
|
||||||
|
|
||||||
|
auto enablePeerTraffic = iface->enablePeerTraffic(config);
|
||||||
|
if (!enablePeerTraffic.waitForFinished() || !enablePeerTraffic.returnValue()) {
|
||||||
|
qCritical() << "Failed to enable peer traffic";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
qWarning() << "Failed to get adapter indexes. Split-tunneling disabled";
|
||||||
|
#endif
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
},
|
||||||
|
[] () {
|
||||||
|
return ErrorCode::AmneziaServiceConnectionFailed;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "core/ipcclient.h"
|
#include "core/ipcclient.h"
|
||||||
#include "vpnprotocol.h"
|
#include "vpnprotocol.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include <QtCore/qsharedpointer.h>
|
||||||
|
|
||||||
class XrayProtocol : public VpnProtocol
|
class XrayProtocol : public VpnProtocol
|
||||||
{
|
{
|
||||||
@@ -14,19 +15,18 @@ public:
|
|||||||
virtual ~XrayProtocol() override;
|
virtual ~XrayProtocol() override;
|
||||||
|
|
||||||
ErrorCode start() override;
|
ErrorCode start() override;
|
||||||
ErrorCode startTun2Sock();
|
|
||||||
void stop() override;
|
void stop() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void readXrayConfiguration(const QJsonObject &configuration);
|
ErrorCode setupRouting();
|
||||||
|
ErrorCode startTun2Socks();
|
||||||
|
|
||||||
QJsonObject m_xrayConfig;
|
QJsonObject m_xrayConfig;
|
||||||
Settings::RouteMode m_routeMode;
|
Settings::RouteMode m_routeMode;
|
||||||
QString m_primaryDNS;
|
QList<QHostAddress> m_dnsServers;
|
||||||
QString m_secondaryDNS;
|
QString m_remoteAddress;
|
||||||
#ifndef Q_OS_IOS
|
|
||||||
QSharedPointer<IpcProcessTun2SocksReplica> m_t2sProcess;
|
QSharedPointer<IpcProcessInterfaceReplica> m_tun2socksProcess;
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // XRAYPROTOCOL_H
|
#endif // XRAYPROTOCOL_H
|
||||||
|
|||||||
@@ -129,6 +129,7 @@
|
|||||||
<file>ui/qml/Components/AdLabel.qml</file>
|
<file>ui/qml/Components/AdLabel.qml</file>
|
||||||
<file>ui/qml/Components/ConnectButton.qml</file>
|
<file>ui/qml/Components/ConnectButton.qml</file>
|
||||||
<file>ui/qml/Components/ConnectionTypeSelectionDrawer.qml</file>
|
<file>ui/qml/Components/ConnectionTypeSelectionDrawer.qml</file>
|
||||||
|
<file>ui/qml/Components/GamepadLoader.qml</file>
|
||||||
<file>ui/qml/Components/HomeContainersListView.qml</file>
|
<file>ui/qml/Components/HomeContainersListView.qml</file>
|
||||||
<file>ui/qml/Components/HomeSplitTunnelingDrawer.qml</file>
|
<file>ui/qml/Components/HomeSplitTunnelingDrawer.qml</file>
|
||||||
<file>ui/qml/Components/InstalledAppsDrawer.qml</file>
|
<file>ui/qml/Components/InstalledAppsDrawer.qml</file>
|
||||||
|
|||||||
@@ -21,4 +21,5 @@ if [ "$(systemctl is-active docker)" != "active" ]; then \
|
|||||||
sleep 5; sudo systemctl start docker; sleep 5;\
|
sleep 5; sudo systemctl start docker; sleep 5;\
|
||||||
fi;\
|
fi;\
|
||||||
if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install sudo, command not found"; exit 1; fi;\
|
if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install sudo, command not found"; exit 1; fi;\
|
||||||
docker --version
|
docker --version;\
|
||||||
|
uname -sr
|
||||||
|
|||||||
@@ -94,6 +94,15 @@ public:
|
|||||||
setValue("Conf/startMinimized", enabled);
|
setValue("Conf/startMinimized", enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isNewsNotifications() const
|
||||||
|
{
|
||||||
|
return value("Conf/newsNotifications", true).toBool();
|
||||||
|
}
|
||||||
|
void setNewsNotifications(bool enabled)
|
||||||
|
{
|
||||||
|
setValue("Conf/newsNotifications", enabled);
|
||||||
|
}
|
||||||
|
|
||||||
bool isSaveLogs() const
|
bool isSaveLogs() const
|
||||||
{
|
{
|
||||||
return value("Conf/saveLogs", false).toBool();
|
return value("Conf/saveLogs", false).toBool();
|
||||||
|
|||||||
+1388
-841
File diff suppressed because it is too large
Load Diff
+1402
-867
File diff suppressed because it is too large
Load Diff
+1388
-841
File diff suppressed because it is too large
Load Diff
+1391
-840
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+1408
-865
File diff suppressed because it is too large
Load Diff
+1396
-853
File diff suppressed because it is too large
Load Diff
+1402
-875
File diff suppressed because it is too large
Load Diff
@@ -382,6 +382,51 @@ bool ApiConfigsController::fillAvailableServices()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject data = QJsonDocument::fromJson(responseBody).object();
|
QJsonObject data = QJsonDocument::fromJson(responseBody).object();
|
||||||
|
|
||||||
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
|
QEventLoop waitProducts;
|
||||||
|
bool productsFetched = false;
|
||||||
|
QString productPrice;
|
||||||
|
QString productCurrency;
|
||||||
|
|
||||||
|
IosController::Instance()->fetchProducts(QStringList() << QStringLiteral("amnezia_premium_6_month"),
|
||||||
|
[&](const QList<QVariantMap> &products,
|
||||||
|
const QStringList &invalidIds,
|
||||||
|
const QString &errorString) {
|
||||||
|
if (!errorString.isEmpty() || products.isEmpty()) {
|
||||||
|
qWarning().noquote() << "[IAP] Failed to fetch product price:" << errorString;
|
||||||
|
} else {
|
||||||
|
const auto &product = products.first();
|
||||||
|
productPrice = product.value("price").toString();
|
||||||
|
productCurrency = product.value("currencyCode").toString();
|
||||||
|
productsFetched = true;
|
||||||
|
qInfo().noquote() << "[IAP] Fetched product price:" << productPrice << productCurrency;
|
||||||
|
}
|
||||||
|
waitProducts.quit();
|
||||||
|
});
|
||||||
|
waitProducts.exec();
|
||||||
|
|
||||||
|
if (productsFetched && !productPrice.isEmpty()) {
|
||||||
|
QJsonArray services = data.value("services").toArray();
|
||||||
|
for (int i = 0; i < services.size(); ++i) {
|
||||||
|
QJsonObject service = services[i].toObject();
|
||||||
|
if (service.value(configKey::serviceType).toString() == serviceType::amneziaPremium) {
|
||||||
|
QJsonObject serviceInfo = service.value(configKey::serviceInfo).toObject();
|
||||||
|
QString formattedPrice = productPrice;
|
||||||
|
if (!productCurrency.isEmpty()) {
|
||||||
|
formattedPrice += " " + productCurrency;
|
||||||
|
}
|
||||||
|
serviceInfo["price"] = formattedPrice;
|
||||||
|
service[configKey::serviceInfo] = serviceInfo;
|
||||||
|
services[i] = service;
|
||||||
|
data["services"] = services;
|
||||||
|
qInfo().noquote() << "[IAP] Updated premium service price in data:" << formattedPrice;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
m_apiServicesModel->updateModel(data);
|
m_apiServicesModel->updateModel(data);
|
||||||
if (m_apiServicesModel->rowCount() > 0) {
|
if (m_apiServicesModel->rowCount() > 0) {
|
||||||
m_apiServicesModel->setServiceIndex(0);
|
m_apiServicesModel->setServiceIndex(0);
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
#include "connectionController.h"
|
#include "connectionController.h"
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
|
||||||
#include <QGuiApplication>
|
|
||||||
#else
|
|
||||||
#include <QApplication>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
#include "core/controllers/vpnConfigurationController.h"
|
#include "core/controllers/vpnConfigurationController.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
@@ -33,7 +27,7 @@ ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &s
|
|||||||
|
|
||||||
void ConnectionController::openConnection()
|
void ConnectionController::openConnection()
|
||||||
{
|
{
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(Q_OS_TVOS) && !defined(MACOS_NE)
|
||||||
if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true))
|
if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true))
|
||||||
{
|
{
|
||||||
emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning);
|
emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
#include "platforms/android/android_controller.h"
|
#include "platforms/android/android_controller.h"
|
||||||
#endif
|
#endif
|
||||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) || defined(MACOS_NE)
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -291,6 +291,8 @@ void ImportController::processNativeWireGuardConfig()
|
|||||||
clientProtocolConfig[config_key::cookieReplyPacketJunkSize] = "0";
|
clientProtocolConfig[config_key::cookieReplyPacketJunkSize] = "0";
|
||||||
clientProtocolConfig[config_key::transportPacketJunkSize] = "0";
|
clientProtocolConfig[config_key::transportPacketJunkSize] = "0";
|
||||||
|
|
||||||
|
clientProtocolConfig[config_key::specialJunk1] = protocols::awg::defaultSpecialJunk1;
|
||||||
|
|
||||||
clientProtocolConfig[config_key::isObfuscationEnabled] = true;
|
clientProtocolConfig[config_key::isObfuscationEnabled] = true;
|
||||||
|
|
||||||
serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(clientProtocolConfig).toJson());
|
serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(clientProtocolConfig).toJson());
|
||||||
@@ -602,14 +604,14 @@ bool ImportController::decodeQrCode(const QString &code)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined Q_OS_ANDROID || defined Q_OS_IOS
|
#if defined Q_OS_ANDROID || defined Q_OS_IOS || defined(Q_OS_TVOS)
|
||||||
void ImportController::startDecodingQr()
|
void ImportController::startDecodingQr()
|
||||||
{
|
{
|
||||||
m_qrCodeChunks.clear();
|
m_qrCodeChunks.clear();
|
||||||
m_totalQrCodeChunksCount = 0;
|
m_totalQrCodeChunksCount = 0;
|
||||||
m_receivedQrCodeChunksCount = 0;
|
m_receivedQrCodeChunksCount = 0;
|
||||||
|
|
||||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) || defined(MACOS_NE)
|
||||||
m_isQrCodeProcessed = true;
|
m_isQrCodeProcessed = true;
|
||||||
#endif
|
#endif
|
||||||
#if defined Q_OS_ANDROID
|
#if defined Q_OS_ANDROID
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public slots:
|
|||||||
QString getConfigFileName();
|
QString getConfigFileName();
|
||||||
QString getMaliciousWarningText();
|
QString getMaliciousWarningText();
|
||||||
|
|
||||||
#if defined Q_OS_ANDROID || defined Q_OS_IOS
|
#if defined Q_OS_ANDROID || defined Q_OS_IOS || defined(Q_OS_TVOS)
|
||||||
void startDecodingQr();
|
void startDecodingQr();
|
||||||
bool parseQrCodeChunk(const QString &code);
|
bool parseQrCodeChunk(const QString &code);
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ private:
|
|||||||
|
|
||||||
void processAmneziaConfig(QJsonObject &config);
|
void processAmneziaConfig(QJsonObject &config);
|
||||||
|
|
||||||
#if defined Q_OS_ANDROID || defined Q_OS_IOS
|
#if defined Q_OS_ANDROID || defined Q_OS_IOS || defined(Q_OS_TVOS)
|
||||||
void stopDecodingQr();
|
void stopDecodingQr();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ private:
|
|||||||
ConfigTypes m_configType;
|
ConfigTypes m_configType;
|
||||||
QString m_maliciousWarningText;
|
QString m_maliciousWarningText;
|
||||||
|
|
||||||
#if defined Q_OS_ANDROID || defined Q_OS_IOS
|
#if defined Q_OS_ANDROID || defined Q_OS_IOS || defined(Q_OS_TVOS)
|
||||||
QMap<int, QByteArray> m_qrCodeChunks;
|
QMap<int, QByteArray> m_qrCodeChunks;
|
||||||
bool m_isQrCodeProcessed;
|
bool m_isQrCodeProcessed;
|
||||||
int m_totalQrCodeChunksCount;
|
int m_totalQrCodeChunksCount;
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
#define INSTALLCONTROLLER_H
|
#define INSTALLCONTROLLER_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QProcess>
|
#if !defined(Q_OS_IOS) && !defined(Q_OS_TVOS)
|
||||||
|
#include <QProcess>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
#include "core/defs.h"
|
#include "core/defs.h"
|
||||||
@@ -111,7 +113,7 @@ private:
|
|||||||
|
|
||||||
QString m_privateKeyPassphrase;
|
QString m_privateKeyPassphrase;
|
||||||
|
|
||||||
#ifndef Q_OS_IOS
|
#if !defined(Q_OS_IOS) && !defined(Q_OS_TVOS)
|
||||||
QList<QSharedPointer<QProcess>> m_sftpMountProcesses;
|
QList<QSharedPointer<QProcess>> m_sftpMountProcesses;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
#include "pageController.h"
|
#include "pageController.h"
|
||||||
#include "utils/converter.h"
|
#include "utils/converter.h"
|
||||||
#include "core/errorstrings.h"
|
#include "core/errorstrings.h"
|
||||||
|
#include <QCoreApplication>
|
||||||
#if defined(MACOS_NE)
|
#if defined(MACOS_NE)
|
||||||
#include "platforms/ios/ios_controller.h"
|
#include "platforms/ios/ios_controller.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_TVOS) || defined(MACOS_NE)
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#else
|
#else
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
@@ -14,7 +15,7 @@
|
|||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
#include "platforms/android/android_controller.h"
|
#include "platforms/android/android_controller.h"
|
||||||
#endif
|
#endif
|
||||||
#if defined Q_OS_MAC
|
#if defined(Q_OS_MACX) && !defined(Q_OS_TVOS)
|
||||||
#include "ui/macos_util.h"
|
#include "ui/macos_util.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ PageController::PageController(const QSharedPointer<ServersModel> &serversModel,
|
|||||||
AndroidController::instance()->setNavigationBarColor(initialPageNavigationBarColor);
|
AndroidController::instance()->setNavigationBarColor(initialPageNavigationBarColor);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined Q_OS_MACX
|
#if defined(Q_OS_MACX) && !defined(Q_OS_TVOS)
|
||||||
connect(this, &PageController::raiseMainWindow, []() {
|
connect(this, &PageController::raiseMainWindow, []() {
|
||||||
setDockIconVisible(true);
|
setDockIconVisible(true);
|
||||||
});
|
});
|
||||||
@@ -64,7 +65,7 @@ QString PageController::getPagePath(PageLoader::PageEnum page)
|
|||||||
void PageController::closeWindow()
|
void PageController::closeWindow()
|
||||||
{
|
{
|
||||||
// On mobile platforms, quit app on close; on desktop, just hide window
|
// On mobile platforms, quit app on close; on desktop, just hide window
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_TVOS)
|
||||||
qApp->quit();
|
qApp->quit();
|
||||||
#else
|
#else
|
||||||
emit hideMainWindow();
|
emit hideMainWindow();
|
||||||
@@ -118,7 +119,7 @@ void PageController::showOnStartup()
|
|||||||
} else {
|
} else {
|
||||||
#if defined(Q_OS_WIN) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
#if defined(Q_OS_WIN) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||||
emit hideMainWindow();
|
emit hideMainWindow();
|
||||||
#elif defined(Q_OS_MACX)
|
#elif defined(Q_OS_MACX) && !defined(Q_OS_TVOS)
|
||||||
setDockIconVisible(false);
|
setDockIconVisible(false);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
#include "platforms/android/android_controller.h"
|
#include "platforms/android/android_controller.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) || defined(MACOS_NE)
|
||||||
#include <AmneziaVPN-Swift.h>
|
#include <AmneziaVPN-Swift.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -57,6 +57,8 @@ QString getPlatformName()
|
|||||||
return "Windows";
|
return "Windows";
|
||||||
#elif defined(Q_OS_ANDROID)
|
#elif defined(Q_OS_ANDROID)
|
||||||
return "Android";
|
return "Android";
|
||||||
|
#elif defined(Q_OS_TVOS)
|
||||||
|
return "tvOS";
|
||||||
#elif defined(Q_OS_LINUX)
|
#elif defined(Q_OS_LINUX)
|
||||||
return "Linux";
|
return "Linux";
|
||||||
#elif defined(Q_OS_MACX)
|
#elif defined(Q_OS_MACX)
|
||||||
@@ -109,7 +111,7 @@ bool SettingsController::isLoggingEnabled()
|
|||||||
void SettingsController::toggleLogging(bool enable)
|
void SettingsController::toggleLogging(bool enable)
|
||||||
{
|
{
|
||||||
m_settings->setSaveLogs(enable);
|
m_settings->setSaveLogs(enable);
|
||||||
#if defined(Q_OS_IOS)
|
#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
|
||||||
AmneziaVPN::toggleLogging(enable);
|
AmneziaVPN::toggleLogging(enable);
|
||||||
#endif
|
#endif
|
||||||
if (enable == true) {
|
if (enable == true) {
|
||||||
@@ -158,7 +160,6 @@ void SettingsController::clearLogs()
|
|||||||
|
|
||||||
qInfo().noquote() << QString("Started %1 version %2 %3").arg(APPLICATION_NAME, APP_VERSION, GIT_COMMIT_HASH);
|
qInfo().noquote() << QString("Started %1 version %2 %3").arg(APPLICATION_NAME, APP_VERSION, GIT_COMMIT_HASH);
|
||||||
qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture());
|
qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture());
|
||||||
qInfo().noquote() << QString("SSL backend: %1").arg(QSslSocket::sslLibraryVersionString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsController::backupAppConfig(const QString &fileName)
|
void SettingsController::backupAppConfig(const QString &fileName)
|
||||||
@@ -193,7 +194,7 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
|
|||||||
if (ok) {
|
if (ok) {
|
||||||
QJsonObject newConfigData = QJsonDocument::fromJson(data).object();
|
QJsonObject newConfigData = QJsonDocument::fromJson(data).object();
|
||||||
|
|
||||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_LINUX) || defined(Q_OS_MACX)
|
#if defined(Q_OS_WINDOWS) || defined(Q_OS_LINUX) || (defined(Q_OS_MACX) && !defined(Q_OS_TVOS))
|
||||||
bool autoStart = false;
|
bool autoStart = false;
|
||||||
if (newConfigData.contains("Conf/autoStart")) {
|
if (newConfigData.contains("Conf/autoStart")) {
|
||||||
autoStart = newConfigData["Conf/autoStart"].toBool();
|
autoStart = newConfigData["Conf/autoStart"].toBool();
|
||||||
@@ -230,7 +231,7 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
|
|||||||
m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode);
|
m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode);
|
||||||
m_sitesModel->toggleSplitTunneling(siteSplittunnelingEnabled);
|
m_sitesModel->toggleSplitTunneling(siteSplittunnelingEnabled);
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_TVOS)
|
||||||
m_settings->setAutoConnect(false);
|
m_settings->setAutoConnect(false);
|
||||||
m_settings->setStartMinimized(false);
|
m_settings->setStartMinimized(false);
|
||||||
m_settings->setKillSwitchEnabled(false);
|
m_settings->setKillSwitchEnabled(false);
|
||||||
@@ -269,7 +270,7 @@ void SettingsController::clearSettings()
|
|||||||
|
|
||||||
emit changeSettingsFinished(tr("All settings have been reset to default values"));
|
emit changeSettingsFinished(tr("All settings have been reset to default values"));
|
||||||
|
|
||||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) || defined(MACOS_NE)
|
||||||
AmneziaVPN::clearSettings();
|
AmneziaVPN::clearSettings();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -308,6 +309,15 @@ void SettingsController::toggleStartMinimized(bool enable)
|
|||||||
emit startMinimizedChanged();
|
emit startMinimizedChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SettingsController::isNewsNotificationsEnabled()
|
||||||
|
{
|
||||||
|
return m_settings->isNewsNotifications();
|
||||||
|
}
|
||||||
|
void SettingsController::toggleNewsNotificationsEnabled(bool enable)
|
||||||
|
{
|
||||||
|
m_settings->setNewsNotifications(enable);
|
||||||
|
}
|
||||||
|
|
||||||
bool SettingsController::isScreenshotsEnabled()
|
bool SettingsController::isScreenshotsEnabled()
|
||||||
{
|
{
|
||||||
return m_settings->isScreenshotsEnabled();
|
return m_settings->isScreenshotsEnabled();
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ public slots:
|
|||||||
bool isStartMinimizedEnabled();
|
bool isStartMinimizedEnabled();
|
||||||
void toggleStartMinimized(bool enable);
|
void toggleStartMinimized(bool enable);
|
||||||
|
|
||||||
|
bool isNewsNotificationsEnabled();
|
||||||
|
void toggleNewsNotificationsEnabled(bool enable);
|
||||||
|
|
||||||
bool isScreenshotsEnabled();
|
bool isScreenshotsEnabled();
|
||||||
void toggleScreenshotsEnabled(bool enable);
|
void toggleScreenshotsEnabled(bool enable);
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
#include "platforms/android/android_controller.h"
|
#include "platforms/android/android_controller.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) || defined(MACOS_NE)
|
||||||
#include "platforms/ios/ios_controller.h"
|
#include "platforms/ios/ios_controller.h"
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -31,7 +31,7 @@ void SystemController::saveFile(const QString &fileName, const QString &data)
|
|||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
|
||||||
QUrl fileUrl = QDir::tempPath() + "/" + fileName;
|
QUrl fileUrl = QDir::tempPath() + "/" + fileName;
|
||||||
QFile file(fileUrl.toString());
|
QFile file(fileUrl.toString());
|
||||||
#else
|
#else
|
||||||
@@ -43,7 +43,7 @@ void SystemController::saveFile(const QString &fileName, const QString &data)
|
|||||||
file.write(data.toUtf8());
|
file.write(data.toUtf8());
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
|
||||||
QStringList filesToSend;
|
QStringList filesToSend;
|
||||||
filesToSend.append(fileUrl.toString());
|
filesToSend.append(fileUrl.toString());
|
||||||
// todo check if save successful
|
// todo check if save successful
|
||||||
@@ -98,7 +98,7 @@ QString SystemController::getFileName(const QString &acceptLabel, const QString
|
|||||||
return AndroidController::instance()->openFile(nameFilter);
|
return AndroidController::instance()->openFile(nameFilter);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
|
||||||
|
|
||||||
fileName = IosController::Instance()->openFile();
|
fileName = IosController::Instance()->openFile();
|
||||||
if (fileName.isEmpty()) {
|
if (fileName.isEmpty()) {
|
||||||
|
|||||||
@@ -112,7 +112,11 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
|
|||||||
if (price == "free") {
|
if (price == "free") {
|
||||||
return tr("Free");
|
return tr("Free");
|
||||||
}
|
}
|
||||||
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
|
return tr("%1 $").arg(price);
|
||||||
|
#else
|
||||||
return tr("%1 $/month").arg(price);
|
return tr("%1 $/month").arg(price);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
case EndDateRole: {
|
case EndDateRole: {
|
||||||
return QDateTime::fromString(apiServiceData.subscription.endDate, Qt::ISODate).toLocalTime().toString("d MMM yyyy");
|
return QDateTime::fromString(apiServiceData.subscription.endDate, Qt::ISODate).toLocalTime().toString("d MMM yyyy");
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include "notificationhandler.h"
|
#include "notificationhandler.h"
|
||||||
|
|
||||||
#if defined(Q_OS_IOS)
|
#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
|
||||||
# include "platforms/ios/iosnotificationhandler.h"
|
# include "platforms/ios/iosnotificationhandler.h"
|
||||||
#else
|
#else
|
||||||
# include "systemtray_notificationhandler.h"
|
# include "systemtray_notificationhandler.h"
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
// static
|
// static
|
||||||
NotificationHandler* NotificationHandler::create(QObject* parent) {
|
NotificationHandler* NotificationHandler::create(QObject* parent) {
|
||||||
#if defined(Q_OS_IOS)
|
#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
|
||||||
return new IOSNotificationHandler(parent);
|
return new IOSNotificationHandler(parent);
|
||||||
#else
|
#else
|
||||||
return new SystemTrayNotificationHandler(parent);
|
return new SystemTrayNotificationHandler(parent);
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ QString Autostart::appPath() {
|
|||||||
return QCoreApplication::applicationFilePath() + " --autostart";
|
return QCoreApplication::applicationFilePath() + " --autostart";
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined Q_OS_MACX
|
#elif defined(Q_OS_MACX) && !defined(Q_OS_TVOS)
|
||||||
|
|
||||||
bool Autostart::isAutostart() {
|
bool Autostart::isAutostart() {
|
||||||
QProcess process;
|
QProcess process;
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtGamepadLegacy
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property alias gamepad: gamepad
|
||||||
|
property alias gamepadKeyNav: gamepadKeyNav
|
||||||
|
|
||||||
|
Gamepad {
|
||||||
|
id: gamepad
|
||||||
|
deviceId: GamepadManager.connectedGamepads.length > 0 ? GamepadManager.connectedGamepads[0] : -1
|
||||||
|
|
||||||
|
onButtonStartChanged: {
|
||||||
|
if (buttonStart) {
|
||||||
|
ServersModel.setProcessedServerIndex(ServersModel.defaultIndex)
|
||||||
|
ConnectionController.connectButtonClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GamepadKeyNavigation {
|
||||||
|
id: gamepadKeyNav
|
||||||
|
gamepad: gamepad
|
||||||
|
active: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: GamepadManager
|
||||||
|
function onConnectedGamepadsChanged() {
|
||||||
|
if (GamepadManager.connectedGamepads.length > 0) {
|
||||||
|
gamepad.deviceId = GamepadManager.connectedGamepads[0]
|
||||||
|
} else {
|
||||||
|
gamepad.deviceId = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -111,11 +111,11 @@ Button {
|
|||||||
color: {
|
color: {
|
||||||
if (root.enabled) {
|
if (root.enabled) {
|
||||||
if (root.pressed) {
|
if (root.pressed) {
|
||||||
return pressedColor
|
return root.pressedColor
|
||||||
}
|
}
|
||||||
return root.hovered ? hoveredColor : defaultColor
|
return root.hovered ? root.hoveredColor : root.defaultColor
|
||||||
} else {
|
} else {
|
||||||
return disabledColor
|
return root.disabledColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,55 @@ Item {
|
|||||||
return drawerContent.state === stateName
|
return drawerContent.state === stateName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isDrawerType2(obj) {
|
||||||
|
return obj && typeof obj.drawerExpandedStateName !== "undefined" &&
|
||||||
|
typeof obj.drawerCollapsedStateName !== "undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDescendantOfDrawer(obj) {
|
||||||
|
var current = obj
|
||||||
|
while (current && current !== root.parent) {
|
||||||
|
if (isDrawerType2(current)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
current = current.parent
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function findComponent(obj, typeCtor) {
|
||||||
|
if (!obj)
|
||||||
|
return null
|
||||||
|
|
||||||
|
if (isDrawerType2(obj) || isDescendantOfDrawer(obj))
|
||||||
|
return null
|
||||||
|
|
||||||
|
if (obj instanceof typeCtor)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
if (obj.children && obj.children.length > 0) {
|
||||||
|
for (var i = 0; i < obj.children.length; i++) {
|
||||||
|
var matchingChildren = findComponent(obj.children[i], typeCtor)
|
||||||
|
if (matchingChildren) return matchingChildren
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.contentItem) {
|
||||||
|
var matchingContentItem = findComponent(obj.contentItem, typeCtor)
|
||||||
|
if (matchingContentItem) return matchingContentItem
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function setParentInteractive(value) {
|
||||||
|
var flickableType = findComponent(root.parent, Flickable)
|
||||||
|
var listViewType = findComponent(root.parent, ListView)
|
||||||
|
|
||||||
|
if (flickableType) flickableType.interactive = value
|
||||||
|
if (listViewType) listViewType.interactive = value
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Qt.application
|
target: Qt.application
|
||||||
|
|
||||||
@@ -93,6 +142,8 @@ Item {
|
|||||||
|
|
||||||
aboutToHide()
|
aboutToHide()
|
||||||
|
|
||||||
|
setParentInteractive(true)
|
||||||
|
|
||||||
closed()
|
closed()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +169,8 @@ Item {
|
|||||||
|
|
||||||
root.aboutToShow()
|
root.aboutToShow()
|
||||||
|
|
||||||
|
setParentInteractive(false)
|
||||||
|
|
||||||
root.opened()
|
root.opened()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ Item {
|
|||||||
implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin
|
implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
hoverEnabled: root.enabled
|
hoverEnabled: root.enabled
|
||||||
@@ -296,13 +298,13 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Keys.onEnterPressed: {
|
Keys.onEnterPressed: {
|
||||||
if (clickedFunction && typeof clickedFunction === "function") {
|
if (!rightImageSource && clickedFunction && typeof clickedFunction === "function") {
|
||||||
clickedFunction()
|
clickedFunction()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
Keys.onReturnPressed: {
|
||||||
if (clickedFunction && typeof clickedFunction === "function") {
|
if (!rightImageSource && clickedFunction && typeof clickedFunction === "function") {
|
||||||
clickedFunction()
|
clickedFunction()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,6 @@ Item {
|
|||||||
|
|
||||||
property string buttonText
|
property string buttonText
|
||||||
property string buttonImageSource
|
property string buttonImageSource
|
||||||
property string buttonImageColor: AmneziaStyle.color.midnightBlack
|
|
||||||
property string buttonBackgroundColor: AmneziaStyle.color.paleGray
|
|
||||||
property string buttonHoveredColor: AmneziaStyle.color.lightGray
|
|
||||||
property var clickedFunc
|
property var clickedFunc
|
||||||
|
|
||||||
property alias textField: textField
|
property alias textField: textField
|
||||||
@@ -70,7 +67,7 @@ Item {
|
|||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
Behavior on border.color {
|
Behavior on border.color {
|
||||||
PropertyAnimation { duration: 100 }
|
PropertyAnimation { duration: 200 }
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
@@ -124,7 +121,7 @@ Item {
|
|||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor
|
color: root.backgroundDisabledColor
|
||||||
}
|
}
|
||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
@@ -189,14 +186,6 @@ Item {
|
|||||||
focusPolicy: Qt.NoFocus
|
focusPolicy: Qt.NoFocus
|
||||||
text: root.buttonText
|
text: root.buttonText
|
||||||
leftImageSource: root.buttonImageSource
|
leftImageSource: root.buttonImageSource
|
||||||
leftImageColor: root.buttonImageColor
|
|
||||||
|
|
||||||
defaultColor: root.buttonBackgroundColor
|
|
||||||
hoveredColor: root.buttonHoveredColor
|
|
||||||
pressedColor: root.buttonHoveredColor
|
|
||||||
disabledColor: AmneziaStyle.color.transparent
|
|
||||||
|
|
||||||
borderWidth: 0
|
|
||||||
|
|
||||||
anchors.top: content.top
|
anchors.top: content.top
|
||||||
anchors.bottom: content.bottom
|
anchors.bottom: content.bottom
|
||||||
@@ -204,7 +193,7 @@ Item {
|
|||||||
|
|
||||||
height: content.implicitHeight
|
height: content.implicitHeight
|
||||||
width: content.implicitHeight
|
width: content.implicitHeight
|
||||||
squareLeftSide: false
|
squareLeftSide: true
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||||
|
|||||||
@@ -140,6 +140,16 @@ PageType {
|
|||||||
ListElement { name : "aes-128-gcm" }
|
ListElement { name : "aes-128-gcm" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateSelectedIndex() {
|
||||||
|
cipherDropDown.text = cipher
|
||||||
|
for (var i = 0; i < cipherListView.model.count; i++) {
|
||||||
|
if (cipherListView.model.get(i).name === cipher) {
|
||||||
|
selectedIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
cipherDropDown.text = selectedText
|
cipherDropDown.text = selectedText
|
||||||
cipher = cipherDropDown.text
|
cipher = cipherDropDown.text
|
||||||
@@ -147,13 +157,14 @@ PageType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
cipherDropDown.text = cipher
|
updateSelectedIndex()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < cipherListView.model.count; i++) {
|
Connections {
|
||||||
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
target: listView.model
|
||||||
selectedIndex = i
|
function onDataChanged() {
|
||||||
}
|
cipherListView.updateSelectedIndex()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,6 +192,16 @@ PageType {
|
|||||||
ListElement { name : qsTr("SHA1") }
|
ListElement { name : qsTr("SHA1") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateSelectedIndex() {
|
||||||
|
hashDropDown.text = hash
|
||||||
|
for (var i = 0; i < hashListView.model.count; i++) {
|
||||||
|
if (hashListView.model.get(i).name === hash) {
|
||||||
|
selectedIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
hashDropDown.text = selectedText
|
hashDropDown.text = selectedText
|
||||||
hash = hashDropDown.text
|
hash = hashDropDown.text
|
||||||
@@ -199,13 +209,14 @@ PageType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
hashDropDown.text = hash
|
updateSelectedIndex()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < hashListView.model.count; i++) {
|
Connections {
|
||||||
if (hashListView.model.get(i).name === hashDropDown.text) {
|
target: listView.model
|
||||||
currentIndex = i
|
function onDataChanged() {
|
||||||
}
|
hashListView.updateSelectedIndex()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,6 +253,16 @@ PageType {
|
|||||||
ListElement { name : qsTr("none") }
|
ListElement { name : qsTr("none") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateSelectedIndex() {
|
||||||
|
cipherDropDown.text = cipher
|
||||||
|
for (var i = 0; i < cipherListView.model.count; i++) {
|
||||||
|
if (cipherListView.model.get(i).name === cipher) {
|
||||||
|
selectedIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
cipherDropDown.text = selectedText
|
cipherDropDown.text = selectedText
|
||||||
cipher = cipherDropDown.text
|
cipher = cipherDropDown.text
|
||||||
@@ -249,13 +270,14 @@ PageType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
cipherDropDown.text = cipher
|
updateSelectedIndex()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < cipherListView.model.count; i++) {
|
Connections {
|
||||||
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
target: listView.model
|
||||||
currentIndex = i
|
function onDataChanged() {
|
||||||
}
|
cipherListView.updateSelectedIndex()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,6 +109,16 @@ PageType {
|
|||||||
ListElement { name : "aes-128-gcm" }
|
ListElement { name : "aes-128-gcm" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateSelectedIndex() {
|
||||||
|
cipherDropDown.text = cipher
|
||||||
|
for (var i = 0; i < cipherListView.model.count; i++) {
|
||||||
|
if (cipherListView.model.get(i).name === cipher) {
|
||||||
|
selectedIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
cipherDropDown.text = selectedText
|
cipherDropDown.text = selectedText
|
||||||
cipher = cipherDropDown.text
|
cipher = cipherDropDown.text
|
||||||
@@ -116,13 +126,14 @@ PageType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
cipherDropDown.text = cipher
|
updateSelectedIndex()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < cipherListView.model.count; i++) {
|
Connections {
|
||||||
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
target: listView.model
|
||||||
currentIndex = i
|
function onDataChanged() {
|
||||||
}
|
cipherListView.updateSelectedIndex()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user