Compare commits

..
63 changed files with 2652 additions and 13773 deletions

No files matched your search

+101 -163
View File
@@ -5,8 +5,38 @@ on:
branches:
- '**'
# Self-hosted server tests (SelfHostedServerSetup, MultipleExports) install and
# remove docker containers on a single shared VPS: two workflow runs executing
# simultaneously corrupt each other's state (error 202, "No such container").
# Queue runs instead of running them in parallel; do not cancel the active one.
concurrency:
group: vps-tests
cancel-in-progress: false
env:
QT_MIRROR: https://mirrors.ocf.berkeley.edu/qt/ # https://download.qt.io/static/mirrorlist/
QTEST_FUNCTION_TIMEOUT: 7200000
SELF_HOSTED_ADMIN_EXPORT_ROUND_TRIP_VPN_KEY: ${{ secrets.SELF_HOSTED_ADMIN_EXPORT_ROUND_TRIP_VPN_KEY }}
THIRD_PARTY_AWG_VPN_KEY: ${{ secrets.THIRD_PARTY_AWG_VPN_KEY }}
THIRD_PARTY_WIRE_GUARD_VPN_KEY: ${{ secrets.THIRD_PARTY_WIRE_GUARD_VPN_KEY }}
THIRD_PARTY_XRAY_VPN_KEY: ${{ secrets.THIRD_PARTY_XRAY_VPN_KEY }}
THIRD_PARTY_OPEN_VPN_KEY: ${{ secrets.THIRD_PARTY_OPEN_VPN_KEY }}
THIRD_PARTY_CLOAK_VPN_KEY: ${{ secrets.THIRD_PARTY_CLOAK_VPN_KEY }}
THIRD_PARTY_SS_VPN_KEY: ${{ secrets.THIRD_PARTY_SS_VPN_KEY }}
THIRD_PARTY_IKEV_VPN_KEY: ${{ secrets.THIRD_PARTY_IKEV_VPN_KEY }}
THIRD_PARTY_PREMIUM_VPN_KEY: ${{ secrets.THIRD_PARTY_PREMIUM_VPN_KEY }}
THIRD_PARTY_VMESS_NEW_IMPORT_DATA: ${{ secrets.THIRD_PARTY_VMESS_NEW_IMPORT_DATA }}
THIRD_PARTY_VMESS_IMPORT_DATA: ${{ secrets.THIRD_PARTY_VMESS_IMPORT_DATA }}
THIRD_PARTY_TROJAN_IMPORT_DATA: ${{ secrets.THIRD_PARTY_TROJAN_IMPORT_DATA }}
THIRD_PARTY_VLESS_IMPORT_DATA: ${{ secrets.THIRD_PARTY_VLESS_IMPORT_DATA }}
THIRD_PARTY_SHADOWSOCKS_IMPORT_DATA: ${{ secrets.THIRD_PARTY_SHADOWSOCKS_IMPORT_DATA }}
THIRD_PARTY_SHADOWSOCKS_SUBSCRIPTION_IMPORT_DATA: ${{ secrets.THIRD_PARTY_SHADOWSOCKS_SUBSCRIPTION_IMPORT_DATA }}
SELF_HOSTED_SERVER_HOST_NAME: ${{ secrets.SELF_HOSTED_SERVER_HOST_NAME }}
SELF_HOSTED_SERVER_USER_NAME: ${{ secrets.SELF_HOSTED_SERVER_USER_NAME }}
SELF_HOSTED_SERVER_PASSWORD: ${{ secrets.SELF_HOSTED_SERVER_PASSWORD }}
SELF_HOSTED_SERVER_SSH_PORT: ${{ secrets.SELF_HOSTED_SERVER_SSH_PORT }}
DNS_LIST_PATH: ${{ github.workspace }}/tests/data/dns_list.json
IP_LIST_PATH: ${{ github.workspace }}/tests/data/ip_list.json
jobs:
Detect-Changes:
@@ -118,7 +148,6 @@ jobs:
shell: bash
env:
QT_INSTALL_DIR: ${{ runner.temp }}
AMNEZIA_BUILD_TESTS: ON
run: deploy/build.sh --generator Ninja --installer all
- name: 'Upload Linux test build'
@@ -250,7 +279,6 @@ jobs:
shell: cmd
env:
QT_INSTALL_DIR: ${{ runner.temp }}
AMNEZIA_BUILD_TESTS: ON
run: |
set WIX_ROOT_PATH="${{ env.USERPROFILE }}\.dotnet\tools"
deploy\build.bat --installer all
@@ -522,7 +550,7 @@ jobs:
xcode-version: '26.3.0'
- name: 'Install Qt'
uses: jurplel/install-qt-action@v4.3.1
uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
host: 'mac'
@@ -554,10 +582,29 @@ jobs:
NOTARYTOOL_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
NOTARYTOOL_EMAIL: ${{ secrets.APPLE_DEV_EMAIL }}
NOTARYTOOL_PASSWORD: ${{ secrets.APPLE_DEV_PASSWORD }}
AMNEZIA_BUILD_TESTS: ON
shell: bash
run: deploy/build.sh --generator Ninja --installer all
- name: 'Prepare macOS test runtime'
shell: bash
run: |
FRAMEWORKS_DIR=$(find deploy/build/_CPack_Packages \
-type d \
-path '*/AmneziaVPN.app/Contents/Frameworks' \
-print -quit)
echo "Frameworks directory: $FRAMEWORKS_DIR"
mkdir -p deploy/build/client/tests/openssl
cp "$FRAMEWORKS_DIR/libssl.3.dylib" \
deploy/build/client/tests/openssl/
cp "$FRAMEWORKS_DIR/libcrypto.3.dylib" \
deploy/build/client/tests/openssl/
ls -la deploy/build/client/tests/openssl
- name: 'Upload macOS test build'
uses: actions/upload-artifact@v7
with:
@@ -818,7 +865,7 @@ jobs:
submodules: 'true'
- name: 'Install desktop Qt'
uses: jurplel/install-qt-action@v4.3.1
uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
@@ -830,10 +877,10 @@ jobs:
extra: '--base ${{ env.QT_MIRROR }}'
- name: 'Install android_x86_64 Qt'
uses: jurplel/install-qt-action@v4.3.1
uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
host: 'all_os'
host: 'linux'
target: 'android'
arch: 'android_x86_64'
modules: ${{ env.QT_MODULES }}
@@ -842,10 +889,10 @@ jobs:
extra: '--base ${{ env.QT_MIRROR }}'
- name: 'Install android_x86 Qt'
uses: jurplel/install-qt-action@v4.3.1
uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
host: 'all_os'
host: 'linux'
target: 'android'
arch: 'android_x86'
modules: ${{ env.QT_MODULES }}
@@ -854,10 +901,10 @@ jobs:
extra: '--base ${{ env.QT_MIRROR }}'
- name: 'Install android_armv7 Qt'
uses: jurplel/install-qt-action@v4.3.1
uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
host: 'all_os'
host: 'linux'
target: 'android'
arch: 'android_armv7'
modules: ${{ env.QT_MODULES }}
@@ -866,10 +913,10 @@ jobs:
extra: '--base ${{ env.QT_MIRROR }}'
- name: 'Install android_arm64_v8a Qt'
uses: jurplel/install-qt-action@v4.3.1
uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
host: 'all_os'
host: 'linux'
target: 'android'
arch: 'android_arm64_v8a'
modules: ${{ env.QT_MODULES }}
@@ -1061,21 +1108,6 @@ jobs:
env:
QT_VERSION: 6.10.1
QTEST_FUNCTION_TIMEOUT: 7200000
THIRD_PARTY_AWG_VPN_KEY: ${{ secrets.THIRD_PARTY_AWG_VPN_KEY }}
THIRD_PARTY_WIRE_GUARD_VPN_KEY: ${{ secrets.THIRD_PARTY_WIRE_GUARD_VPN_KEY }}
THIRD_PARTY_XRAY_VPN_KEY: ${{ secrets.THIRD_PARTY_XRAY_VPN_KEY }}
THIRD_PARTY_OPEN_VPN_KEY: ${{ secrets.THIRD_PARTY_OPEN_VPN_KEY }}
THIRD_PARTY_CLOAK_VPN_KEY: ${{ secrets.THIRD_PARTY_CLOAK_VPN_KEY }}
THIRD_PARTY_SS_VPN_KEY: ${{ secrets.THIRD_PARTY_SS_VPN_KEY }}
THIRD_PARTY_IKEV_VPN_KEY: ${{ secrets.THIRD_PARTY_IKEV_VPN_KEY }}
THIRD_PARTY_PREMIUM_VPN_KEY: ${{ secrets.THIRD_PARTY_PREMIUM_VPN_KEY }}
THIRD_PARTY_VMESS_NEW_IMPORT_DATA: ${{ secrets.THIRD_PARTY_VMESS_NEW_IMPORT_DATA }}
THIRD_PARTY_VMESS_IMPORT_DATA: ${{ secrets.THIRD_PARTY_VMESS_IMPORT_DATA }}
THIRD_PARTY_TROJAN_IMPORT_DATA: ${{ secrets.THIRD_PARTY_TROJAN_IMPORT_DATA }}
THIRD_PARTY_VLESS_IMPORT_DATA: ${{ secrets.THIRD_PARTY_VLESS_IMPORT_DATA }}
THIRD_PARTY_SHADOWSOCKS_IMPORT_DATA: ${{ secrets.THIRD_PARTY_SHADOWSOCKS_IMPORT_DATA }}
THIRD_PARTY_SHADOWSOCKS_SUBSCRIPTION_IMPORT_DATA: ${{ secrets.THIRD_PARTY_SHADOWSOCKS_SUBSCRIPTION_IMPORT_DATA }}
steps:
- name: Checkout
@@ -1086,16 +1118,12 @@ jobs:
- name: Decode backup file
if: matrix.filter == 'BackupExportImportTest'
shell: bash
env:
BACKUP_FILE_BASE64: ${{ secrets.BACKUP_FILE_BASE64 }}
shell: pwsh
run: |
if [ -z "$BACKUP_FILE_BASE64" ]; then
echo "::error::BACKUP_FILE_BASE64 secret is not set"
exit 1
fi
echo "$BACKUP_FILE_BASE64" | base64 --decode > "$RUNNER_TEMP/test.backup"
echo "BACKUP_PATH=$RUNNER_TEMP/test.backup" >> "$GITHUB_ENV"
$backup = Join-Path $env:RUNNER_TEMP "test.backup"
$bytes = [Convert]::FromBase64String("${{ secrets.BACKUP_FILE_BASE64 }}")
[IO.File]::WriteAllBytes($backup, $bytes)
"BACKUP_PATH=$backup" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
- name: Install Qt
uses: jurplel/install-qt-action@v3
@@ -1127,6 +1155,7 @@ jobs:
- name: Restore executable permissions
run: |
find deploy/build/client/tests \
-maxdepth 1 \
-type f \
-name "test_*" \
-exec chmod +x {} \;
@@ -1136,86 +1165,11 @@ jobs:
run: |
QT_ROOT="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64"
export LD_LIBRARY_PATH="$PWD/deploy/build/client/tests:$QT_ROOT/lib:${LD_LIBRARY_PATH:-}"
export LD_LIBRARY_PATH="$QT_ROOT/lib:${LD_LIBRARY_PATH:-}"
export QT_QPA_PLATFORM=offscreen
ctest --test-dir deploy/build/client/tests --output-on-failure -VV -R "^${{ matrix.filter }}$"
# ------------------------------------------------------
test-vps:
name: Test self-hosted VPS
needs: Build-Linux-Ubuntu
# These tests install and remove docker containers on a single shared VPS:
# two runs at once corrupt each other's state, so only one job may touch it.
concurrency:
group: vps-tests
cancel-in-progress: false
runs-on: ubuntu-22.04
env:
QT_VERSION: 6.10.1
QTEST_FUNCTION_TIMEOUT: 7200000
SELF_HOSTED_SERVER_HOST_NAME: ${{ secrets.SELF_HOSTED_SERVER_HOST_NAME }}
SELF_HOSTED_SERVER_USER_NAME: ${{ secrets.SELF_HOSTED_SERVER_USER_NAME }}
SELF_HOSTED_SERVER_PASSWORD: ${{ secrets.SELF_HOSTED_SERVER_PASSWORD }}
SELF_HOSTED_SERVER_SSH_PORT: ${{ secrets.SELF_HOSTED_SERVER_SSH_PORT }}
SELF_HOSTED_ADMIN_EXPORT_ROUND_TRIP_VPN_KEY: ${{ secrets.SELF_HOSTED_ADMIN_EXPORT_ROUND_TRIP_VPN_KEY }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: 'true'
fetch-depth: 10
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: linux
target: desktop
arch: linux_gcc_64
modules: qtremoteobjects qt5compat qtshadertools
dir: ${{ runner.temp }}
set-env: true
aqtversion: '==3.3.0'
py7zrversion: '==0.22.*'
extra: '--base ${{ env.QT_MIRROR }}'
- name: Install system packages
run: |
sudo apt-get update
sudo apt-get install -y \
libxkbcommon-x11-0 \
libsecret-1-dev
- name: Download build
uses: actions/download-artifact@v4
with:
name: linux-build
path: deploy/build/client/tests
- name: Restore executable permissions
run: |
find deploy/build/client/tests \
-type f \
-name "test_*" \
-exec chmod +x {} \;
- name: Run tests
shell: bash
run: |
QT_ROOT="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64"
export LD_LIBRARY_PATH="$PWD/deploy/build/client/tests:$QT_ROOT/lib:${LD_LIBRARY_PATH:-}"
export QT_QPA_PLATFORM=offscreen
ctest --test-dir deploy/build/client/tests --output-on-failure -VV \
-R "^(SelfHostedServerSetupTest|MultipleExportsTest|AdminSelfHostedExportTest)$"
# ------------------------------------------------------
test-windows:
@@ -1248,21 +1202,6 @@ jobs:
env:
QT_VERSION: 6.10.1
QTEST_FUNCTION_TIMEOUT: 7200000
THIRD_PARTY_AWG_VPN_KEY: ${{ secrets.THIRD_PARTY_AWG_VPN_KEY }}
THIRD_PARTY_WIRE_GUARD_VPN_KEY: ${{ secrets.THIRD_PARTY_WIRE_GUARD_VPN_KEY }}
THIRD_PARTY_XRAY_VPN_KEY: ${{ secrets.THIRD_PARTY_XRAY_VPN_KEY }}
THIRD_PARTY_OPEN_VPN_KEY: ${{ secrets.THIRD_PARTY_OPEN_VPN_KEY }}
THIRD_PARTY_CLOAK_VPN_KEY: ${{ secrets.THIRD_PARTY_CLOAK_VPN_KEY }}
THIRD_PARTY_SS_VPN_KEY: ${{ secrets.THIRD_PARTY_SS_VPN_KEY }}
THIRD_PARTY_IKEV_VPN_KEY: ${{ secrets.THIRD_PARTY_IKEV_VPN_KEY }}
THIRD_PARTY_PREMIUM_VPN_KEY: ${{ secrets.THIRD_PARTY_PREMIUM_VPN_KEY }}
THIRD_PARTY_VMESS_NEW_IMPORT_DATA: ${{ secrets.THIRD_PARTY_VMESS_NEW_IMPORT_DATA }}
THIRD_PARTY_VMESS_IMPORT_DATA: ${{ secrets.THIRD_PARTY_VMESS_IMPORT_DATA }}
THIRD_PARTY_TROJAN_IMPORT_DATA: ${{ secrets.THIRD_PARTY_TROJAN_IMPORT_DATA }}
THIRD_PARTY_VLESS_IMPORT_DATA: ${{ secrets.THIRD_PARTY_VLESS_IMPORT_DATA }}
THIRD_PARTY_SHADOWSOCKS_IMPORT_DATA: ${{ secrets.THIRD_PARTY_SHADOWSOCKS_IMPORT_DATA }}
THIRD_PARTY_SHADOWSOCKS_SUBSCRIPTION_IMPORT_DATA: ${{ secrets.THIRD_PARTY_SHADOWSOCKS_SUBSCRIPTION_IMPORT_DATA }}
steps:
- name: Checkout
@@ -1274,15 +1213,9 @@ jobs:
- name: Decode backup file
if: matrix.filter == 'BackupExportImportTest'
shell: pwsh
env:
BACKUP_FILE_BASE64: ${{ secrets.BACKUP_FILE_BASE64 }}
run: |
if ([string]::IsNullOrWhiteSpace($env:BACKUP_FILE_BASE64)) {
Write-Output "::error::BACKUP_FILE_BASE64 secret is not set"
exit 1
}
$backup = Join-Path $env:RUNNER_TEMP "test.backup"
$bytes = [Convert]::FromBase64String($env:BACKUP_FILE_BASE64)
$bytes = [Convert]::FromBase64String("${{ secrets.BACKUP_FILE_BASE64 }}")
[IO.File]::WriteAllBytes($backup, $bytes)
"BACKUP_PATH=$backup" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
@@ -1352,21 +1285,6 @@ jobs:
env:
QT_VERSION: 6.10.1
QTEST_FUNCTION_TIMEOUT: 7200000
THIRD_PARTY_AWG_VPN_KEY: ${{ secrets.THIRD_PARTY_AWG_VPN_KEY }}
THIRD_PARTY_WIRE_GUARD_VPN_KEY: ${{ secrets.THIRD_PARTY_WIRE_GUARD_VPN_KEY }}
THIRD_PARTY_XRAY_VPN_KEY: ${{ secrets.THIRD_PARTY_XRAY_VPN_KEY }}
THIRD_PARTY_OPEN_VPN_KEY: ${{ secrets.THIRD_PARTY_OPEN_VPN_KEY }}
THIRD_PARTY_CLOAK_VPN_KEY: ${{ secrets.THIRD_PARTY_CLOAK_VPN_KEY }}
THIRD_PARTY_SS_VPN_KEY: ${{ secrets.THIRD_PARTY_SS_VPN_KEY }}
THIRD_PARTY_IKEV_VPN_KEY: ${{ secrets.THIRD_PARTY_IKEV_VPN_KEY }}
THIRD_PARTY_PREMIUM_VPN_KEY: ${{ secrets.THIRD_PARTY_PREMIUM_VPN_KEY }}
THIRD_PARTY_VMESS_NEW_IMPORT_DATA: ${{ secrets.THIRD_PARTY_VMESS_NEW_IMPORT_DATA }}
THIRD_PARTY_VMESS_IMPORT_DATA: ${{ secrets.THIRD_PARTY_VMESS_IMPORT_DATA }}
THIRD_PARTY_TROJAN_IMPORT_DATA: ${{ secrets.THIRD_PARTY_TROJAN_IMPORT_DATA }}
THIRD_PARTY_VLESS_IMPORT_DATA: ${{ secrets.THIRD_PARTY_VLESS_IMPORT_DATA }}
THIRD_PARTY_SHADOWSOCKS_IMPORT_DATA: ${{ secrets.THIRD_PARTY_SHADOWSOCKS_IMPORT_DATA }}
THIRD_PARTY_SHADOWSOCKS_SUBSCRIPTION_IMPORT_DATA: ${{ secrets.THIRD_PARTY_SHADOWSOCKS_SUBSCRIPTION_IMPORT_DATA }}
steps:
- name: Setup Xcode
@@ -1382,16 +1300,12 @@ jobs:
- name: Decode backup file
if: matrix.filter == 'BackupExportImportTest'
shell: bash
env:
BACKUP_FILE_BASE64: ${{ secrets.BACKUP_FILE_BASE64 }}
shell: pwsh
run: |
if [ -z "$BACKUP_FILE_BASE64" ]; then
echo "::error::BACKUP_FILE_BASE64 secret is not set"
exit 1
fi
echo "$BACKUP_FILE_BASE64" | base64 --decode > "$RUNNER_TEMP/test.backup"
echo "BACKUP_PATH=$RUNNER_TEMP/test.backup" >> "$GITHUB_ENV"
$backup = Join-Path $env:RUNNER_TEMP "test.backup"
$bytes = [Convert]::FromBase64String("${{ secrets.BACKUP_FILE_BASE64 }}")
[IO.File]::WriteAllBytes($backup, $bytes)
"BACKUP_PATH=$backup" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
- name: Download build
uses: actions/download-artifact@v4
@@ -1410,9 +1324,23 @@ jobs:
dir: ${{ runner.temp }}
set-env: true
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: "3.14"
- name: Install Conan
run: pip install "conan==2.28.0"
- name: Install dependencies
run: |
brew update
brew install cmake ninja
- name: Restore executable permissions
run: |
find deploy/build/client/tests \
-maxdepth 1 \
-type f \
-name "test_*" \
-exec chmod +x {} \;
@@ -1422,7 +1350,9 @@ jobs:
run: |
QT_ROOT="$RUNNER_TEMP/Qt/$QT_VERSION/macos"
export DYLD_LIBRARY_PATH="$PWD/deploy/build/client/tests:$QT_ROOT/lib:${DYLD_LIBRARY_PATH:-}"
export DYLD_LIBRARY_PATH="$PWD/deploy/build/client/tests/openssl:$QT_ROOT/lib:$DYLD_LIBRARY_PATH"
# export QT_QPA_PLATFORM=offscreen
ctest --test-dir deploy/build/client/tests -R "${{ matrix.filter }}" --output-on-failure
@@ -1466,7 +1396,7 @@ jobs:
uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
host: 'all_os'
host: 'linux'
target: 'android'
arch: 'android_x86_64'
modules: ${{ env.QT_MODULES }}
@@ -1511,8 +1441,7 @@ jobs:
-DANDROID_PLATFORM=${{ env.ANDROID_PLATFORM }} \
-DCMAKE_ANDROID_NDK=$CMAKE_ANDROID_NDK \
-DQT_HOST_PATH=$QT_HOST \
-DANDROID_SDK_ROOT=$ANDROID_HOME \
-DAMNEZIA_BUILD_TESTS=ON
-DANDROID_SDK_ROOT=$ANDROID_HOME
- name: 'Build Android tests'
# env:
@@ -1585,6 +1514,15 @@ jobs:
EOF
chmod +x ci_run_android_tests.sh
- name: 'Verify Android test files'
run: |
echo "Runner script:"
grep 'local apk=' ci_run_android_tests.sh
echo
echo "APKs:"
find build -type f -name '*.apk' -print
- name: 'Run Android tests on emulator'
uses: reactivecircus/android-emulator-runner@v2
with:
+2 -4
View File
@@ -5,7 +5,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(PROJECT AmneziaVPN)
if(NOT AMNEZIAVPN_VERSION)
set(AMNEZIAVPN_VERSION 5.0.3.1 CACHE STRING "Client app version")
set(AMNEZIAVPN_VERSION 5.0.2.1 CACHE STRING "Client app version")
endif()
set(QT_CREATOR_SKIP_PACKAGE_MANAGER_SETUP ON CACHE BOOL "" FORCE)
@@ -20,8 +20,6 @@ project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
HOMEPAGE_URL "https://amnezia.org/"
)
option(AMNEZIA_BUILD_TESTS "Build test executables" OFF)
# trigger conan to kick off `conan install` globally
find_package(OpenSSL REQUIRED)
if (PREBUILTS_ONLY)
@@ -34,7 +32,7 @@ set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
# bump by 2 on every release, because we're releasing two versions on the play store
set(APP_ANDROID_VERSION_CODE 2171)
set(APP_ANDROID_VERSION_CODE 2161)
if(DEFINED APP_ANDROID_VERSION_CODE_OFFSET)
math(EXPR APP_ANDROID_VERSION_CODE "${APP_ANDROID_VERSION_CODE} + ${APP_ANDROID_VERSION_CODE_OFFSET}")
endif()
+5 -4
View File
@@ -92,7 +92,6 @@ else()
${CMAKE_CURRENT_LIST_DIR}/translations/${CLIENT_TS_PREFIX}_hi_IN.ts
${CMAKE_CURRENT_LIST_DIR}/translations/${CLIENT_TS_PREFIX}_es_ES.ts
${CMAKE_CURRENT_LIST_DIR}/translations/${CLIENT_TS_PREFIX}_ko_KR.ts
${CMAKE_CURRENT_LIST_DIR}/translations/${CLIENT_TS_PREFIX}_fr_FR.ts
)
endif()
@@ -315,6 +314,8 @@ if(OVPN_SCRIPTS)
)
endif()
if(AMNEZIA_BUILD_TESTS)
add_subdirectory(tests)
endif()
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
add_subdirectory(tests)
-2
View File
@@ -179,9 +179,7 @@ void AmneziaApplication::init()
m_coreController->setQmlRoot();
#if CLIENT_ENABLE_APP_UPDATES
m_coreController->checkForAppUpdates();
#endif
#ifdef Q_OS_WIN //TODO
if (m_parser.isSet(m_optAutostart))
-4
View File
@@ -58,7 +58,3 @@ endif()
if(NOT CLIENT_NOTIFICATION_IDENTIFIER)
set(CLIENT_NOTIFICATION_IDENTIFIER "${CLIENT_TS_PREFIX}" CACHE STRING "Local notification request identifier")
endif()
if(NOT DEFINED CLIENT_ENABLE_APP_UPDATES)
set(CLIENT_ENABLE_APP_UPDATES 1)
endif()
-2
View File
@@ -22,7 +22,6 @@ set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/core/controllers/coreSignalHandlers.h
${CLIENT_ROOT_DIR}/core/controllers/gatewayController.h
${CLIENT_ROOT_DIR}/core/utils/selfhosted/sshSession.h
${CLIENT_ROOT_DIR}/core/utils/selfhosted/sshExecutor.h
${CLIENT_ROOT_DIR}/core/controllers/serversController.h
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/usersController.h
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/installController.h
@@ -108,7 +107,6 @@ set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/core/controllers/coreSignalHandlers.cpp
${CLIENT_ROOT_DIR}/core/controllers/gatewayController.cpp
${CLIENT_ROOT_DIR}/core/utils/selfhosted/sshSession.cpp
${CLIENT_ROOT_DIR}/core/utils/selfhosted/sshExecutor.cpp
${CLIENT_ROOT_DIR}/core/controllers/serversController.cpp
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/usersController.cpp
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/installController.cpp
@@ -188,9 +188,6 @@ ErrorCode StorePurchaseController::importServiceFromMarket(const QString &userCo
quint16 crc = qChecksum(QJsonDocument(configObject).toJson());
if (!serverConfigUtils::isConfigFormatVersionSupported(configObject)) {
return ErrorCode::ConfigFormatVersionNotSupportedError;
}
if (configObject.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) {
return ErrorCode::InternalError;
}
@@ -239,9 +239,6 @@ ErrorCode SubscriptionController::applyImportedServiceConfig(const QString &user
updateApiConfigInJson(serverConfigJson, serviceType, serviceProtocol, userCountryCode, responseBody);
if (!serverConfigUtils::isConfigFormatVersionSupported(serverConfigJson)) {
return ErrorCode::ConfigFormatVersionNotSupportedError;
}
if (serverConfigJson.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) {
return ErrorCode::InternalError;
}
@@ -294,9 +291,6 @@ ErrorCode SubscriptionController::importTrialFromGateway(const QString &userCoun
}
QJsonObject configObject = QJsonDocument::fromJson(configBytes).object();
if (!serverConfigUtils::isConfigFormatVersionSupported(configObject)) {
return ErrorCode::ConfigFormatVersionNotSupportedError;
}
if (configObject.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) {
return ErrorCode::InternalError;
}
@@ -386,9 +380,6 @@ ErrorCode SubscriptionController::applyUpdatedServiceConfig(const QString &serve
updateApiConfigInJson(serverConfigJson, apiV2->apiConfig.serviceType, serviceProtocol, apiV2->apiConfig.userCountryCode, responseBody);
if (!serverConfigUtils::isConfigFormatVersionSupported(serverConfigJson)) {
return ErrorCode::ConfigFormatVersionNotSupportedError;
}
if (serverConfigJson.value(configKey::configVersion).toInt() != serverConfigUtils::ConfigSource::AmneziaGateway) {
return ErrorCode::InternalError;
}
@@ -1,9 +1,9 @@
#include "coreSignalHandlers.h"
#include <QTimer>
#include <QtConcurrent>
#include "core/utils/selfhosted/sshSession.h"
#include "core/utils/selfhosted/sshExecutor.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/controllers/coreController.h"
@@ -145,7 +145,7 @@ void CoreSignalHandlers::initExportControllerHandler()
});
connect(m_coreController->m_exportController, &ExportController::revokeClientRequested, this,
[this](const QString &serverId, int row, DockerContainer container) {
SshExecutor::instance().run(serverId, [this, serverId, row, container]() {
QtConcurrent::run([this, serverId, row, container]() {
m_coreController->m_usersController->revokeClient(serverId, row, container);
});
});
@@ -205,10 +205,8 @@ void CoreSignalHandlers::initAdminConfigRevokedHandler()
{
connect(m_coreController->m_installController, &InstallController::clientRevocationRequested, this,
[this](const QString &serverId, const ContainerConfig &containerConfig, DockerContainer container) {
SshExecutor::instance().run(serverId, [this, serverId, containerConfig, container]() {
m_coreController->m_usersController->revokeClient(serverId, containerConfig, container);
});
});
m_coreController->m_usersController->revokeClient(serverId, containerConfig, container);
}, Qt::DirectConnection);
connect(m_coreController->m_installController, &InstallController::clientAppendRequested, this,
[this](const QString &serverId, const QString &clientId, const QString &clientName, DockerContainer container) {
@@ -33,18 +33,6 @@ using namespace ProtocolUtils;
namespace
{
bool rejectUnsupportedFormatVersion(ImportController::ImportResult &result)
{
if (serverConfigUtils::isConfigFormatVersionSupported(result.config)) {
return false;
}
qWarning() << "Config format version" << serverConfigUtils::configFormatVersion(result.config)
<< "is newer than supported" << serverConfigUtils::currentConfigFormatVersion;
result.errorCode = ErrorCode::ConfigFormatVersionNotSupportedError;
result.config = {};
return true;
}
ConfigTypes checkConfigFormat(const QString &config)
{
const QString openVpnConfigPatternCli = "client";
@@ -219,10 +207,6 @@ ImportController::ImportResult ImportController::extractConfigFromData(const QSt
case ConfigTypes::Amnezia: {
result.config = QJsonDocument::fromJson(config.toUtf8()).object();
if (rejectUnsupportedFormatVersion(result)) {
return result;
}
if (serverConfigUtils::isServerFromApi(result.config)) {
auto apiConfig = result.config.value(apiDefs::key::apiConfig).toObject();
apiConfig[apiDefs::key::vpnKey] = data;
@@ -272,7 +256,6 @@ ImportController::ImportResult ImportController::extractConfigFromQr(const QByte
if (!dataObj.isEmpty()) {
result.config = dataObj;
result.configType = ConfigTypes::Amnezia;
rejectUnsupportedFormatVersion(result);
return result;
}
@@ -284,7 +267,6 @@ ImportController::ImportResult ImportController::extractConfigFromQr(const QByte
return result;
}
result.configType = ConfigTypes::Amnezia;
rejectUnsupportedFormatVersion(result);
return result;
}
@@ -302,7 +284,6 @@ ImportController::ImportResult ImportController::extractConfigFromQr(const QByte
return result;
}
result.configType = ConfigTypes::Amnezia;
rejectUnsupportedFormatVersion(result);
return result;
}
@@ -398,11 +379,6 @@ int ImportController::qrChunksTotal() const
void ImportController::importConfig(const QJsonObject &config)
{
if (!serverConfigUtils::isConfigFormatVersionSupported(config)) {
emit importErrorOccurred(ErrorCode::ConfigFormatVersionNotSupportedError, false);
return;
}
ServerCredentials credentials;
credentials.hostName = config.value(configKey::hostName).toString();
credentials.port = config.value(configKey::port).toInt();
@@ -16,7 +16,6 @@
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/utils/selfhosted/sshExecutor.h"
#include "core/installers/awgInstaller.h"
#include "core/installers/installerBase.h"
#include "core/installers/openvpnInstaller.h"
@@ -105,7 +104,6 @@ InstallController::~InstallController()
ErrorCode InstallController::setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config,
bool isUpdate)
{
qDebug().noquote() << "InstallController::setupContainer" << ContainerUtils::containerToString(container);
SshSession sshSession;
ErrorCode e = ErrorCode::NoError;
@@ -373,7 +371,7 @@ ErrorCode InstallController::validateAndPrepareConfig(const QString &serverId)
void InstallController::validateConfig(const QString &serverId)
{
QFuture<ErrorCode> future = SshExecutor::instance().run(serverId, [this, serverId]() {
QFuture<ErrorCode> future = QtConcurrent::run([this, serverId]() {
return validateAndPrepareConfig(serverId);
});
@@ -119,11 +119,6 @@ QByteArray SettingsController::backupAppConfig() const
return QJsonDocument(config).toJson();
}
int SettingsController::unsupportedFormatConfigsSkippedCount() const
{
return m_serversRepository->unsupportedFormatConfigsCount();
}
ErrorCode SettingsController::restoreAppConfigFromData(const QByteArray &data)
{
if (!m_appSettingsRepository->restoreAppConfig(data)) {
@@ -39,7 +39,6 @@ public:
QByteArray backupAppConfig() const;
ErrorCode restoreAppConfigFromData(const QByteArray &data);
int unsupportedFormatConfigsSkippedCount() const;
QString getAppVersion() const;
@@ -129,10 +129,6 @@ void UpdateController::setUpdateCheckRunning(bool running)
void UpdateController::checkForUpdates()
{
#if !CLIENT_ENABLE_APP_UPDATES
return;
#endif
if (m_updateCheckRunning || !m_appSettingsRepository) {
return;
}
@@ -85,7 +85,6 @@ QPair<QString, QString> ApiV2ServerConfig::getDnsPair(const QString &primaryDns,
QJsonObject ApiV2ServerConfig::toJson() const
{
QJsonObject obj;
obj[configKey::formatVersion] = serverConfigUtils::currentConfigFormatVersion;
if (!name.isEmpty()) {
obj[configKey::name] = name;
@@ -8,7 +8,6 @@
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/utils/networkUtilities.h"
@@ -52,7 +51,6 @@ QPair<QString, QString> NativeServerConfig::getDnsPair(const QString &primaryDns
QJsonObject NativeServerConfig::toJson() const
{
QJsonObject obj;
obj[configKey::formatVersion] = serverConfigUtils::currentConfigFormatVersion;
if (!description.isEmpty()) {
obj[configKey::description] = this->description;
@@ -9,7 +9,6 @@
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/networkUtilities.h"
#include "core/utils/serverConfigUtils.h"
namespace amnezia
{
@@ -84,7 +83,6 @@ QPair<QString, QString> SelfHostedAdminServerConfig::getDnsPair(bool isAmneziaDn
QJsonObject SelfHostedAdminServerConfig::toJson() const
{
QJsonObject obj;
obj[configKey::formatVersion] = serverConfigUtils::currentConfigFormatVersion;
if (!description.isEmpty()) {
obj[configKey::description] = this->description;
@@ -4,7 +4,6 @@
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
@@ -67,7 +66,6 @@ QPair<QString, QString> SelfHostedUserServerConfig::getDnsPair(const QString &pr
QJsonObject SelfHostedUserServerConfig::toJson() const
{
QJsonObject obj;
obj[configKey::formatVersion] = serverConfigUtils::currentConfigFormatVersion;
if (!description.isEmpty()) {
obj[configKey::description] = this->description;
@@ -5,7 +5,6 @@
#include <QJsonValue>
#include <QSet>
#include <QUuid>
#include <QDebug>
#include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/apiKeys.h"
@@ -143,7 +142,6 @@ void SecureServersRepository::persistDefaultServerFields()
void SecureServersRepository::loadFromStorage()
{
clearServerStateMaps();
m_unsupportedFormatConfigsCount = 0;
const QJsonArray serversArray =
QJsonDocument::fromJson(value(QStringLiteral("Servers/serversList"), QByteArray()).toByteArray())
@@ -154,12 +152,6 @@ void SecureServersRepository::loadFromStorage()
const QString candidateId = readStorageServerId(json);
const QString serverId = normalizedOrGeneratedServerId(candidateId);
const QJsonObject strippedJson = withoutStorageServerId(json);
if (!serverConfigUtils::isConfigFormatVersionSupported(strippedJson)) {
qWarning() << "Skipping stored server config with unsupported format version"
<< serverConfigUtils::configFormatVersion(strippedJson);
++m_unsupportedFormatConfigsCount;
continue;
}
const serverConfigUtils::ConfigType kind = serverConfigUtils::configTypeFromJson(strippedJson);
if (m_serverJsonById.contains(serverId) || kind == serverConfigUtils::ConfigType::Invalid) {
@@ -192,11 +184,6 @@ void SecureServersRepository::invalidateCache()
loadFromStorage();
}
int SecureServersRepository::unsupportedFormatConfigsCount() const
{
return m_unsupportedFormatConfigsCount;
}
void SecureServersRepository::clearServers()
{
clearServerStateMaps();
@@ -235,8 +222,7 @@ QString SecureServersRepository::addServer(const QString &serverId, const QJsonO
return id;
}
const QJsonObject strippedJson = withoutStorageServerId(serverJson);
if (!serverConfigUtils::isConfigFormatVersionSupported(strippedJson)
|| serverConfigUtils::configTypeFromJson(strippedJson) != kind) {
if (serverConfigUtils::configTypeFromJson(strippedJson) != kind) {
return id;
}
m_serverJsonById.insert(id, embedStorageServerId(id, strippedJson));
@@ -52,8 +52,6 @@ public:
void invalidateCache();
int unsupportedFormatConfigsCount() const;
signals:
void serverAdded(const QString &serverId);
void serverEdited(const QString &serverId);
@@ -79,7 +77,6 @@ private:
QVector<QString> m_orderedServerIds;
QString m_defaultServerId;
int m_unsupportedFormatConfigsCount = 0;
};
#endif // SECURESERVERSREPOSITORY_H
-1
View File
@@ -11,6 +11,5 @@
#define APP_IOS_BUNDLE_ID "@CLIENT_IOS_BUNDLE_ID@"
#define APP_IOS_STORE_URL_FALLBACK "@CLIENT_IOS_STORE_URL_FALLBACK@"
#define APP_ANDROID_PACKAGE "@CLIENT_ANDROID_PACKAGE@"
#define CLIENT_ENABLE_APP_UPDATES @CLIENT_ENABLE_APP_UPDATES@
#endif
-1
View File
@@ -25,7 +25,6 @@ namespace amnezia
constexpr QLatin1String accessToken("api_key");
constexpr QLatin1String config("config");
constexpr QLatin1String configVersion("config_version");
constexpr QLatin1String formatVersion("format_version");
constexpr QLatin1String sendPayload("send_payload");
constexpr QLatin1String sendPayloadEndpoint("endpoint");
-2
View File
@@ -82,8 +82,6 @@ namespace amnezia
RestoreBackupInvalidError = 904,
LegacyApiV1NotSupportedError = 905,
LegacyContainerNotSupportedError = 906,
ConfigFormatVersionNotSupportedError = 907,
RestoreBackupUnsupportedConfigsSkipped = 908,
// Android errors
AndroidError = 1000,
-2
View File
@@ -71,8 +71,6 @@ QString errorString(ErrorCode code) {
case (ErrorCode::ImportBackupFileUseRestoreInstead): errorMessage = QObject::tr("Backup files cannot be imported here. Use 'Restore from backup' instead."); break;
case (ErrorCode::RestoreBackupInvalidError): errorMessage = QObject::tr("Backup file is corrupted or has invalid format"); break;
case (ErrorCode::LegacyApiV1NotSupportedError): errorMessage = QObject::tr("This legacy Amnezia subscription format is no longer supported"); break;
case (ErrorCode::ConfigFormatVersionNotSupportedError): errorMessage = QObject::tr("This configuration was created in a newer version of the application and is not fully supported. Please update the application"); break;
case (ErrorCode::RestoreBackupUnsupportedConfigsSkipped): errorMessage = QObject::tr("Some configurations from the backup were not restored because they require a newer version of the application"); break;
case (ErrorCode::LegacyContainerNotSupportedError): errorMessage = QObject::tr("This protocol is no longer supported. Please select another protocol or remove this container from the server settings."); break;
case (ErrorCode::ImportOpenConfigError): errorMessage = QObject::tr("Unable to open config file"); break;
case (ErrorCode::NoInstalledContainersError): errorMessage = QObject::tr("VPN Protocols is not installed.\n Please install VPN container at first"); break;
@@ -1,33 +0,0 @@
#include "sshExecutor.h"
SshExecutor &SshExecutor::instance()
{
static SshExecutor executor;
return executor;
}
SshExecutor::~SshExecutor()
{
QMutexLocker lock(&m_mutex);
for (QThreadPool *pool : std::as_const(m_pools)) {
pool->waitForDone();
delete pool;
}
m_pools.clear();
}
QThreadPool *SshExecutor::poolFor(const QString &serverId)
{
QMutexLocker lock(&m_mutex);
auto it = m_pools.find(serverId);
if (it != m_pools.end()) {
return it.value();
}
auto *pool = new QThreadPool();
pool->setMaxThreadCount(1); // serialize all SSH ops for this server
pool->setExpiryTimeout(-1); // keep the single worker thread alive between ops
m_pools.insert(serverId, pool);
return pool;
}
@@ -1,47 +0,0 @@
#ifndef SSHEXECUTOR_H
#define SSHEXECUTOR_H
#include <QHash>
#include <QMutex>
#include <QString>
#include <QThreadPool>
#include <QtConcurrent>
#include <utility>
// Per-server serial executor for long-running self-hosted SSH operations.
//
// All SSH work for a given serverId is queued onto a dedicated single-thread
// pool, so operations to the same server run strictly one at a time (no
// concurrent SSH sessions to one host => no races, and a natural guard against
// double-run). Operations to different servers still run in parallel.
//
// NOTE: do NOT route nested workers that are awaited from within an already
// queued operation (e.g. InstallController::isServerDpkgBusy) through the same
// per-server pool — the single worker thread would block waiting on itself.
// Such nested helpers must keep using the global QThreadPool.
class SshExecutor
{
public:
static SshExecutor &instance();
SshExecutor(const SshExecutor &) = delete;
SshExecutor &operator=(const SshExecutor &) = delete;
template <typename Functor>
auto run(const QString &serverId, Functor &&functor)
{
return QtConcurrent::run(poolFor(serverId), std::forward<Functor>(functor));
}
private:
SshExecutor() = default;
~SshExecutor();
QThreadPool *poolFor(const QString &serverId);
QHash<QString, QThreadPool *> m_pools;
QMutex m_mutex;
};
#endif // SSHEXECUTOR_H
-10
View File
@@ -34,16 +34,6 @@ bool hasThirdPartyConfig(const QJsonObject &json)
namespace serverConfigUtils
{
int configFormatVersion(const QJsonObject &serverConfigObject)
{
return serverConfigObject.value(amnezia::configKey::formatVersion).toInt(0);
}
bool isConfigFormatVersionSupported(const QJsonObject &serverConfigObject)
{
return configFormatVersion(serverConfigObject) <= currentConfigFormatVersion;
}
bool isServerFromApi(const QJsonObject &serverConfigObject)
{
const int configVersion = serverConfigObject.value(amnezia::configKey::configVersion).toInt();
-6
View File
@@ -25,12 +25,6 @@ enum ConfigSource {
AmneziaGateway
};
constexpr int currentConfigFormatVersion = 1;
int configFormatVersion(const QJsonObject &serverConfigObject);
bool isConfigFormatVersionSupported(const QJsonObject &serverConfigObject);
bool isServerFromApi(const QJsonObject &serverConfigObject);
ConfigSource getConfigSource(const QJsonObject &serverConfigObject);
+7
View File
@@ -6,8 +6,15 @@
<true/>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>app-proxy-provider</string>
<string>packet-tunnel-provider</string>
<string>dns-settings</string>
<string>relay</string>
<string>content-filter-provider</string>
<string>dns-proxy</string>
</array>
<key>com.apple.developer.system-extension.install</key>
<true/>
<key>com.apple.developer.networking.vpn.api</key>
<array>
<string>allow-vpn</string>
@@ -6,7 +6,12 @@
<true/>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>dns-settings</string>
<string>relay</string>
<string>packet-tunnel-provider</string>
<string>content-filter-provider</string>
<string>dns-proxy</string>
<string>app-proxy-provider</string>
</array>
<key>com.apple.developer.networking.vpn.api</key>
<array>
+27 -53
View File
@@ -122,15 +122,6 @@ if(NOT ANDROID)
test_common
)
add_executable(test_config_format_version
testConfigFormatVersion.cpp
)
target_link_libraries(test_config_format_version PRIVATE
Qt6::Test
test_common
)
add_executable(test_xray_serialization
testXraySerialization.cpp
)
@@ -203,51 +194,35 @@ if(NOT ANDROID)
test_common
)
# Tests run from a bare artifact directory on CI, so the OpenSSL runtime we
# compiled against must sit next to the binary (PATH / LD_LIBRARY_PATH /
# DYLD_LIBRARY_PATH point there). Otherwise the loader picks up the system
# library and the version checks in testOpenSSLLibrary fail.
foreach(_ssl_tgt OpenSSL::Crypto OpenSSL::SSL)
if(NOT TARGET "${_ssl_tgt}")
continue()
endif()
set(_ssl_loc "")
foreach(_prop IMPORTED_LOCATION IMPORTED_LOCATION_RELEASE IMPORTED_LOCATION_DEBUG
IMPORTED_LOCATION_MINSIZEREL IMPORTED_LOCATION_RELWITHDEBINFO
IMPORTED_LOCATION_NOCONFIG)
get_target_property(_candidate "${_ssl_tgt}" "${_prop}")
if(_candidate AND NOT _candidate MATCHES "-NOTFOUND")
set(_ssl_loc "${_candidate}")
break()
if(WIN32)
# libcrypto-3-x64.dll is a hard import of this binary.
# Without copying Conan's DLL here, Windows loads the first match in PATH
# (MySQL, Git, PHP...) which differs from the Conan version we compiled against,
# causing testRuntimeVersionMatchesCompileTime and testWindowsNoDllHijack to fail.
foreach(_ssl_tgt OpenSSL::Crypto OpenSSL::SSL)
if(NOT TARGET "${_ssl_tgt}")
continue()
endif()
set(_ssl_dll "")
foreach(_prop IMPORTED_LOCATION IMPORTED_LOCATION_RELEASE IMPORTED_LOCATION_DEBUG
IMPORTED_LOCATION_MINSIZEREL IMPORTED_LOCATION_RELWITHDEBINFO
IMPORTED_LOCATION_NOCONFIG)
get_target_property(_candidate "${_ssl_tgt}" "${_prop}")
if(_candidate AND NOT _candidate MATCHES "-NOTFOUND" AND _candidate MATCHES "\\.dll$")
set(_ssl_dll "${_candidate}")
break()
endif()
endforeach()
if(_ssl_dll)
add_custom_command(TARGET test_openssl_library POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${_ssl_dll}" "$<TARGET_FILE_DIR:test_openssl_library>"
)
else()
message(STATUS "No .dll found for ${_ssl_tgt} (IMPORTED_LOCATION may point to .lib) — skipping copy")
endif()
endforeach()
if(NOT _ssl_loc)
continue()
endif()
set(_ssl_files "")
if(WIN32)
if(_ssl_loc MATCHES "\\.dll$")
set(_ssl_files "${_ssl_loc}")
endif()
else()
get_filename_component(_ssl_dir "${_ssl_loc}" DIRECTORY)
get_filename_component(_ssl_name "${_ssl_loc}" NAME)
string(REGEX REPLACE "\\..*$" "" _ssl_stem "${_ssl_name}")
file(GLOB _ssl_files
"${_ssl_dir}/${_ssl_stem}.so*"
"${_ssl_dir}/${_ssl_stem}.dylib"
"${_ssl_dir}/${_ssl_stem}.*.dylib")
endif()
if(NOT _ssl_files)
message(STATUS "No shared runtime found for ${_ssl_tgt} (${_ssl_loc}) - skipping copy")
endif()
foreach(_ssl_file ${_ssl_files})
add_custom_command(TARGET test_openssl_library POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${_ssl_file}" "$<TARGET_FILE_DIR:test_openssl_library>"
)
endforeach()
endforeach()
endif()
endif() # NOT ANDROID
if(ANDROID)
@@ -403,7 +378,6 @@ if(NOT ANDROID)
add_test(NAME SettingsSignalsTest COMMAND test_settings_signals)
add_test(NAME UiServersModelAndControllerTest COMMAND test_ui_servers_model_and_controller)
add_test(NAME XraySerializationTest COMMAND test_xray_serialization)
add_test(NAME ConfigFormatVersionTest COMMAND test_config_format_version)
add_test(NAME UiLanguageModelAndControllerTest COMMAND test_ui_language_model_and_controller)
add_test(NAME UiIpModelAndControllerTest COMMAND test_ui_ip_model_and_controller)
add_test(NAME UiAppSTModelAndControllerTest COMMAND test_ui_app_st_model_and_controller)
-2
View File
@@ -54,8 +54,6 @@ private slots:
if (!isEnvValueConfigured(backup)) {
QSKIP("Set BACKUP_PATH");
}
QVERIFY2(QFileInfo(backup).size() > 0,
"BACKUP_PATH file is empty - is the BACKUP_FILE_BASE64 secret configured?");
QSignalSpy errorOccurredSpy(m_coreController->m_settingsUiController, &SettingsUiController::errorOccurred);
-206
View File
@@ -1,206 +0,0 @@
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QSignalSpy>
#include <QTest>
#include <QUuid>
#include "utils/testCoreController.h"
#include "core/models/api/apiV2ServerConfig.h"
#include "core/models/selfhosted/nativeServerConfig.h"
#include "core/models/selfhosted/selfHostedAdminServerConfig.h"
#include "core/models/selfhosted/selfHostedUserServerConfig.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/serverConfigUtils.h"
#include "secureQSettings.h"
#include "vpnConnection.h"
using namespace amnezia;
namespace
{
QJsonObject adminConfigJson(const QString &hostName, int formatVersion = -1)
{
QJsonObject json;
json[configKey::hostName] = hostName;
json[configKey::userName] = QStringLiteral("root");
json[configKey::password] = QStringLiteral("secret");
json[configKey::port] = 22;
json[configKey::description] = QStringLiteral("Server ") + hostName;
if (formatVersion >= 0) {
json[configKey::formatVersion] = formatVersion;
}
return json;
}
QByteArray serversListBytes(const QList<QJsonObject> &configs)
{
QJsonArray array;
for (const QJsonObject &config : configs) {
array.append(config);
}
return QJsonDocument(array).toJson();
}
} // namespace
class TestConfigFormatVersion : public QObject
{
Q_OBJECT
private:
TestCoreController *m_coreController = nullptr;
SecureQSettings *m_settings = nullptr;
private slots:
void initTestCase()
{
const QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString();
m_settings = new SecureQSettings(testOrg, "amnezia-client", nullptr, false);
auto vpnConnection = QSharedPointer<VpnConnection>::create(nullptr, nullptr);
m_coreController = new TestCoreController(vpnConnection, m_settings, nullptr, this);
}
void cleanupTestCase()
{
m_settings->clearSettings();
delete m_coreController;
delete m_settings;
}
void init()
{
m_settings->clearSettings();
m_coreController->m_serversRepository->invalidateCache();
}
void testVersionCheckHelpers()
{
QCOMPARE(serverConfigUtils::configFormatVersion(QJsonObject()), 0);
QVERIFY(serverConfigUtils::isConfigFormatVersionSupported(QJsonObject()));
QVERIFY(serverConfigUtils::isConfigFormatVersionSupported(
adminConfigJson("1.1.1.1", serverConfigUtils::currentConfigFormatVersion)));
QVERIFY(!serverConfigUtils::isConfigFormatVersionSupported(
adminConfigJson("1.1.1.1", serverConfigUtils::currentConfigFormatVersion + 1)));
}
void testToJsonWritesCurrentFormatVersion()
{
SelfHostedAdminServerConfig admin;
admin.hostName = "1.1.1.1";
QCOMPARE(admin.toJson().value(configKey::formatVersion).toInt(-1), serverConfigUtils::currentConfigFormatVersion);
SelfHostedUserServerConfig user;
user.hostName = "1.1.1.1";
QCOMPARE(user.toJson().value(configKey::formatVersion).toInt(-1), serverConfigUtils::currentConfigFormatVersion);
NativeServerConfig native;
native.hostName = "1.1.1.1";
QCOMPARE(native.toJson().value(configKey::formatVersion).toInt(-1), serverConfigUtils::currentConfigFormatVersion);
ApiV2ServerConfig api;
api.hostName = "1.1.1.1";
QCOMPARE(api.toJson().value(configKey::formatVersion).toInt(-1), serverConfigUtils::currentConfigFormatVersion);
}
void testImportAcceptsMissingAndCurrentVersion()
{
auto *importController = m_coreController->m_importCoreController;
QSignalSpy importFinishedSpy(importController, &ImportController::importFinished);
const QString withoutVersion = QJsonDocument(adminConfigJson("1.1.1.1")).toJson();
auto result = importController->extractConfigFromData(withoutVersion);
QCOMPARE(result.errorCode, ErrorCode::NoError);
importController->importConfig(result.config);
const QString currentVersion =
QJsonDocument(adminConfigJson("2.2.2.2", serverConfigUtils::currentConfigFormatVersion)).toJson();
result = importController->extractConfigFromData(currentVersion);
QCOMPARE(result.errorCode, ErrorCode::NoError);
importController->importConfig(result.config);
QCOMPARE(importFinishedSpy.count(), 2);
QCOMPARE(m_coreController->m_serversRepository->serversCount(), 2);
const auto stored = m_coreController->m_serversRepository->selfHostedAdminConfig(
m_coreController->m_serversRepository->serverIdAt(0));
QVERIFY(stored.has_value());
QCOMPARE(stored->toJson().value(configKey::formatVersion).toInt(-1), serverConfigUtils::currentConfigFormatVersion);
}
void testImportRejectsNewerVersion()
{
auto *importController = m_coreController->m_importCoreController;
QSignalSpy importFinishedSpy(importController, &ImportController::importFinished);
QSignalSpy importErrorSpy(importController, &ImportController::importErrorOccurred);
const QJsonObject newer = adminConfigJson("3.3.3.3", serverConfigUtils::currentConfigFormatVersion + 1);
const QByteArray newerBytes = QJsonDocument(newer).toJson();
auto result = importController->extractConfigFromData(QString::fromUtf8(newerBytes));
QCOMPARE(result.errorCode, ErrorCode::ConfigFormatVersionNotSupportedError);
QVERIFY(result.config.isEmpty());
const QString vpnUrl = "vpn://"
+ QString::fromUtf8(qCompress(newerBytes, 8).toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals));
result = importController->extractConfigFromData(vpnUrl);
QCOMPARE(result.errorCode, ErrorCode::ConfigFormatVersionNotSupportedError);
QVERIFY(result.config.isEmpty());
result = importController->extractConfigFromQr(newerBytes);
QCOMPARE(result.errorCode, ErrorCode::ConfigFormatVersionNotSupportedError);
QVERIFY(result.config.isEmpty());
importController->importConfig(newer);
QCOMPARE(importErrorSpy.count(), 1);
QCOMPARE(importErrorSpy.first().at(0).value<ErrorCode>(), ErrorCode::ConfigFormatVersionNotSupportedError);
QCOMPARE(importFinishedSpy.count(), 0);
QCOMPARE(m_coreController->m_serversRepository->serversCount(), 0);
}
void testRepositorySkipsNewerVersionOnLoad()
{
m_settings->setValue("Servers/serversList",
serversListBytes({ adminConfigJson("1.1.1.1"),
adminConfigJson("2.2.2.2", serverConfigUtils::currentConfigFormatVersion + 1),
adminConfigJson("3.3.3.3", serverConfigUtils::currentConfigFormatVersion) }));
m_coreController->m_serversRepository->invalidateCache();
QCOMPARE(m_coreController->m_serversRepository->serversCount(), 2);
QCOMPARE(m_coreController->m_serversRepository->unsupportedFormatConfigsCount(), 1);
const auto rejected = m_coreController->m_serversRepository->addServer(
QString(), adminConfigJson("4.4.4.4", serverConfigUtils::currentConfigFormatVersion + 1),
serverConfigUtils::ConfigType::SelfHostedAdmin);
QVERIFY(!rejected.isEmpty());
QCOMPARE(m_coreController->m_serversRepository->serversCount(), 2);
}
void testRestoreBackupReportsSkippedConfigs()
{
QJsonObject backup;
backup["Servers/serversList"] = QString::fromUtf8(serversListBytes(
{ adminConfigJson("1.1.1.1"),
adminConfigJson("2.2.2.2", serverConfigUtils::currentConfigFormatVersion + 1) }));
QSignalSpy errorSpy(m_coreController->m_settingsUiController, &SettingsUiController::errorOccurred);
QSignalSpy restoredSpy(m_coreController->m_settingsUiController, &SettingsUiController::restoreBackupFinished);
m_coreController->m_settingsUiController->restoreAppConfigFromData(QJsonDocument(backup).toJson());
QCOMPARE(restoredSpy.count(), 1);
QCOMPARE(errorSpy.count(), 1);
QCOMPARE(errorSpy.first().at(0).value<ErrorCode>(), ErrorCode::RestoreBackupUnsupportedConfigsSkipped);
QCOMPARE(m_coreController->m_serversRepository->serversCount(), 1);
errorSpy.clear();
restoredSpy.clear();
QJsonObject cleanBackup;
cleanBackup["Servers/serversList"] = QString::fromUtf8(serversListBytes({ adminConfigJson("5.5.5.5") }));
m_coreController->m_settingsUiController->restoreAppConfigFromData(QJsonDocument(cleanBackup).toJson());
QCOMPARE(restoredSpy.count(), 1);
QCOMPARE(errorSpy.count(), 0);
}
};
QTEST_MAIN(TestConfigFormatVersion)
#include "testConfigFormatVersion.moc"
+17 -9
View File
@@ -41,11 +41,11 @@ private:
const auto description = serverDescriptionAt(m_coreController->m_serversRepository, expected-1);
QVERIFY2(description.has_value(), "Server config should exist");
if (*description != expectedDescription) qWarning() << "Server description should match";
if (*description == expectedDescription) qWarning() << "Server description should match";
if (m_coreController->m_serversModel) {
QString modelDesc = m_coreController->m_serversModel->data(m_coreController->m_serversModel->index(expected-1, 0), ServersModel::NameRole).toString();
if (modelDesc != expectedDescription) qWarning() << "Server description in model should match";
if (modelDesc == expectedDescription) qWarning() << "Server description in model should match";
}
}
@@ -145,8 +145,8 @@ private slots:
const auto description0 = serverDescriptionAt(m_coreController->m_serversRepository, 0);
const auto description1 = serverDescriptionAt(m_coreController->m_serversRepository, 1);
QVERIFY2(description0.has_value() && description1.has_value(), "Server configs should exist");
if (*description0 != "AWG Server") qWarning() << "First server description should match";
if (*description1 != "Xray Server") qWarning() << "Second server description should match";
if (*description0 == "AWG Server") qWarning() << "First server description should match";
if (*description1 == "Xray Server") qWarning() << "Second server description should match";
defaultServerChangedSpy.clear();
serverRemovedSpy.clear();
@@ -165,7 +165,7 @@ private slots:
if (m_coreController->m_serversModel) {
QVERIFY2(m_coreController->m_serversModel->rowCount() == 1, "After removing first server, model row count should be 1");
QString modelDesc = m_coreController->m_serversModel->data(m_coreController->m_serversModel->index(0, 0), ServersModel::NameRole).toString();
if (modelDesc != "Xray Server") qWarning() << "Remaining server description in model should match";
if (modelDesc == "Xray Server") qWarning() << "Remaining server description in model should match";
}
defaultServerChangedSpy.clear();
@@ -184,11 +184,19 @@ private slots:
}
void testListImports() {
const QString dnsListPath = QFINDTESTDATA("data/dns_list.json");
const QString ipListPath = QFINDTESTDATA("data/ip_list.json");
QString dnsListPath = getEnvValue("DNS_LIST_PATH");
QString ipListPath = getEnvValue("IP_LIST_PATH");
QVERIFY2(!dnsListPath.isEmpty(), "data/dns_list.json not found");
QVERIFY2(!ipListPath.isEmpty(), "data/ip_list.json not found");
logEnvValueState("DNS_LIST_PATH");
logEnvValueState("IP_LIST_PATH");
qInfo() << "DNS_LIST_PATH file exists:" << QFile::exists(dnsListPath)
<< "size:" << QFileInfo(dnsListPath).size();
qInfo() << "IP_LIST_PATH file exists:" << QFile::exists(ipListPath)
<< "size:" << QFileInfo(ipListPath).size();
if (!isEnvValueConfigured(dnsListPath) || !isEnvValueConfigured(ipListPath)) {
QSKIP("set DNS_LIST_PATH and IP_LIST_PATH");
}
QSignalSpy dnsErrorOccurredSpy(m_coreController->m_allowedDnsUiController, &AllowedDnsUiController::errorOccurred);
QSignalSpy dnsFinishedSpy(m_coreController->m_allowedDnsUiController, &AllowedDnsUiController::finished);
@@ -55,14 +55,6 @@ private:
clientConfig[configKey::specialJunk3] = protocols::awg::defaultSpecialJunk3;
clientConfig[configKey::specialJunk4] = protocols::awg::defaultSpecialJunk4;
clientConfig[configKey::specialJunk5] = protocols::awg::defaultSpecialJunk5;
clientConfig[configKey::initPacketJunkSize] = QString::number(protocols::awg::initPacketJunkSizeMax);
clientConfig[configKey::responsePacketJunkSize] = QString::number(protocols::awg::responsePacketJunkSizeMax);
clientConfig[configKey::cookieReplyPacketJunkSize] = QString::number(protocols::awg::cookieReplyPacketJunkSizeMax);
clientConfig[configKey::transportPacketJunkSize] = protocols::awg::defaultPadding;
clientConfig[configKey::initPacketMagicHeader] = protocols::awg::defaultInitPacketMagicHeader;
clientConfig[configKey::responsePacketMagicHeader] = protocols::awg::defaultResponsePacketMagicHeader;
clientConfig[configKey::underloadPacketMagicHeader] = protocols::awg::defaultUnderloadPacketMagicHeader;
clientConfig[configKey::transportPacketMagicHeader] = protocols::awg::defaultTransportPacketMagicHeader;
clientConfig[configKey::clientPrivKey] = "test_client_private_key";
clientConfig[configKey::clientPubKey] = "test_client_public_key";
clientConfig[configKey::serverPubKey] = "test_server_public_key";
@@ -79,9 +71,9 @@ private:
awgConfig[configKey::junkPacketCount] = protocols::awg::defaultJunkPacketCount;
awgConfig[configKey::junkPacketMinSize] = protocols::awg::defaultJunkPacketMinSize;
awgConfig[configKey::junkPacketMaxSize] = protocols::awg::defaultJunkPacketMaxSize;
awgConfig[configKey::initPacketJunkSize] = QString::number(protocols::awg::initPacketJunkSizeMax);
awgConfig[configKey::responsePacketJunkSize] = QString::number(protocols::awg::responsePacketJunkSizeMax);
awgConfig[configKey::cookieReplyPacketJunkSize] = QString::number(protocols::awg::cookieReplyPacketJunkSizeMax);
awgConfig[configKey::initPacketJunkSize] = protocols::awg::initPacketJunkSizeMax;
awgConfig[configKey::responsePacketJunkSize] = protocols::awg::responsePacketJunkSizeMax;
awgConfig[configKey::cookieReplyPacketJunkSize] = protocols::awg::cookieReplyPacketJunkSizeMax;
awgConfig[configKey::transportPacketJunkSize] = protocols::awg::defaultPadding;
awgConfig[configKey::initPacketMagicHeader] = protocols::awg::defaultInitPacketMagicHeader;
awgConfig[configKey::responsePacketMagicHeader] = protocols::awg::defaultResponsePacketMagicHeader;
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -31,7 +31,6 @@ int LanguageUiController::getCurrentLanguageIndex() const
case QLocale::Hindi: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Hindi); break;
case QLocale::Korean: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Korean); break;
case QLocale::Spanish: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Spanish); break;
case QLocale::French: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::French); break;
default: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::English); break;
}
}
@@ -66,7 +65,6 @@ LanguageSettings::AvailableLanguageEnum LanguageUiController::getSystemLanguageE
case QLocale::Korean: return LanguageSettings::AvailableLanguageEnum::Korean;
case QLocale::English: return LanguageSettings::AvailableLanguageEnum::English;
case QLocale::Spanish: return LanguageSettings::AvailableLanguageEnum::Spanish;
case QLocale::French: return LanguageSettings::AvailableLanguageEnum::French;
default: return LanguageSettings::AvailableLanguageEnum::English;
}
}
@@ -113,7 +111,6 @@ QString LanguageUiController::getLocalLanguageName(const LanguageSettings::Avail
case LanguageSettings::AvailableLanguageEnum::Hindi: strLanguage = "हिन्दी"; break;
case LanguageSettings::AvailableLanguageEnum::Korean: strLanguage = "한국어"; break;
case LanguageSettings::AvailableLanguageEnum::Spanish: strLanguage = "Español"; break;
case LanguageSettings::AvailableLanguageEnum::French: strLanguage = "Français"; break;
default: break;
}
@@ -134,7 +131,6 @@ QLocale LanguageUiController::languageEnumToLocale(const LanguageSettings::Avail
case LanguageSettings::AvailableLanguageEnum::Hindi: return QLocale::Hindi;
case LanguageSettings::AvailableLanguageEnum::Korean: return QLocale::Korean;
case LanguageSettings::AvailableLanguageEnum::Spanish: return QLocale::Spanish;
case LanguageSettings::AvailableLanguageEnum::French: return QLocale::French;
default: return QLocale::English;
}
}
@@ -12,7 +12,6 @@
#include <utility>
#include "core/utils/api/apiUtils.h"
#include "core/utils/selfhosted/sshExecutor.h"
#include "core/controllers/selfhosted/installController.h"
#include "core/controllers/connectionController.h"
#include "core/utils/networkUtilities.h"
@@ -348,7 +347,7 @@ void InstallUiController::updateServerConfig(const QString &serverId, int contai
ContainerConfig oldConfigCopy = oldContainerConfig;
InstallController *installController = m_installController;
QFuture<ErrorCode> future =
SshExecutor::instance().run(serverId, [installController, serverId, container, oldConfigCopy,
QtConcurrent::run([installController, serverId, container, oldConfigCopy,
newConfigCopy]() mutable -> ErrorCode {
return installController->updateServerConfig(serverId, container, oldConfigCopy, newConfigCopy);
});
@@ -541,7 +540,7 @@ void InstallUiController::removeContainer(const QString &serverId, int container
});
InstallController *installController = m_installController;
QFuture<ErrorCode> future = SshExecutor::instance().run(serverId,
QFuture<ErrorCode> future = QtConcurrent::run(
[installController, serverId, container]() -> ErrorCode {
return installController->removeContainer(serverId, container);
});
@@ -163,10 +163,6 @@ void SettingsUiController::restoreAppConfigFromData(const QByteArray &data)
emit restoreBackupFinished();
emit autoStartChanged();
emit startMinimizedChanged();
if (m_settingsController->unsupportedFormatConfigsSkippedCount() > 0) {
emit errorOccurred(ErrorCode::RestoreBackupUnsupportedConfigsSkipped);
}
} else {
emit errorOccurred(errorCode);
}
-1
View File
@@ -50,7 +50,6 @@ QString LanguageModel::getLocalLanguageName(const LanguageSettings::AvailableLan
case LanguageSettings::AvailableLanguageEnum::Hindi: strLanguage = "हिन्दी"; break;
case LanguageSettings::AvailableLanguageEnum::Spanish: strLanguage = "Español"; break;
case LanguageSettings::AvailableLanguageEnum::Korean: strLanguage = "한국어"; break;
case LanguageSettings::AvailableLanguageEnum::French: strLanguage = "Français"; break;
default: break;
}
+1 -2
View File
@@ -18,8 +18,7 @@ namespace LanguageSettings
Urdu,
Hindi,
Spanish,
Korean,
French
Korean
};
Q_ENUM_NS(AvailableLanguageEnum)
@@ -196,7 +196,7 @@ PageType {
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 24
visible: Qt.platform.os === "ios" || IsMacOsNeBuild
visible: (Qt.platform.os === "ios" || IsMacOsNeBuild) && !(root.currentPlan && root.currentPlan.hasFreeTrial)
spacing: 16
ParagraphTextType {
-9
View File
@@ -174,15 +174,6 @@ Window {
PopupType {
id: popupNotificationMessage
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
popupNotificationMessage.close()
popupNotificationTimer.stop()
}
}
}
Timer {
-1
View File
@@ -1,6 +1,5 @@
find_program(CONAN_COMMAND "conan" REQUIRED
HINTS
"${CMAKE_SOURCE_DIR}/.venv/Scripts"
"${CMAKE_SOURCE_DIR}/.venv/bin"
"/opt/homebrew/bin"
)
+7 -7
View File
@@ -1,9 +1,6 @@
if(NOT DEFINED SIGNTOOL_SUBJECT_NAME)
set(SIGNTOOL_SUBJECT_NAME "$ENV{SIGNTOOL_SUBJECT_NAME}")
endif()
if(NOT DEFINED SIGNTOOL_CERT_HAS_UI)
set(SIGNTOOL_CERT_HAS_UI "$ENV{SIGNTOOL_CERT_HAS_UI}")
endif()
if(NOT DEFINED CODESIGN_SIGNATURE)
set(CODESIGN_SIGNATURE "$ENV{CODESIGN_SIGNATURE}")
endif()
@@ -12,11 +9,14 @@ if(NOT DEFINED CODESIGN_KEYCHAIN)
endif()
if(WIN32)
set(AMNEZIA_PACKAGE_DIR "${CPACK_TEMPORARY_DIRECTORY}/packages/AmneziaVPN/data")
if(SIGNTOOL_SUBJECT_NAME)
file(GLOB_RECURSE BINARIES
"${CPACK_TEMPORARY_DIRECTORY}/*.dll"
"${CPACK_TEMPORARY_DIRECTORY}/*.exe"
)
if(BINARIES AND SIGNTOOL_SUBJECT_NAME)
include(${CMAKE_CURRENT_LIST_DIR}/util/signtool.cmake)
signtool_sign_files("${AMNEZIA_PACKAGE_DIR}/*.exe" "${SIGNTOOL_SUBJECT_NAME}" "${SIGNTOOL_CERT_HAS_UI}")
signtool_sign_files("${AMNEZIA_PACKAGE_DIR}/*.dll" "${SIGNTOOL_SUBJECT_NAME}" "${SIGNTOOL_CERT_HAS_UI}")
signtool_sign_files("${BINARIES}" "${SIGNTOOL_SUBJECT_NAME}")
endif()
endif()
+1 -4
View File
@@ -1,9 +1,6 @@
if(NOT DEFINED SIGNTOOL_SUBJECT_NAME)
set(SIGNTOOL_SUBJECT_NAME "$ENV{SIGNTOOL_SUBJECT_NAME}")
endif()
if(NOT DEFINED SIGNTOOL_CERT_HAS_UI)
set(SIGNTOOL_CERT_HAS_UI "$ENV{SIGNTOOL_CERT_HAS_UI}")
endif()
if(NOT DEFINED NOTARYTOOL_EMAIL)
set(NOTARYTOOL_EMAIL "$ENV{NOTARYTOOL_EMAIL}")
endif()
@@ -18,7 +15,7 @@ if(WIN32)
if (SIGNTOOL_SUBJECT_NAME)
include(${CMAKE_CURRENT_LIST_DIR}/util/signtool.cmake)
foreach(PACKAGE_FILE IN LISTS CPACK_PACKAGE_FILES)
signtool_sign_files("${PACKAGE_FILE}" "${SIGNTOOL_SUBJECT_NAME}" "${SIGNTOOL_CERT_HAS_UI}")
signtool_sign_files("${PACKAGE_FILE}" "${SIGNTOOL_SUBJECT_NAME}")
endforeach()
endif()
endif()
+19 -22
View File
@@ -1,6 +1,10 @@
find_program(SIGNTOOL_COMMAND signtool REQUIRED)
function(signtool_sign_files PACKAGE_FILES SUBJECT_NAME CERT_HAS_UI)
function(signtool_sign_files PACKAGE_FILES SUBJECT_NAME)
if(NOT SUBJECT_NAME)
set(SUBJECT_NAME "Privacy Technologies OU")
endif()
if(PACKAGE_FILES)
set(args sign
/n ${SUBJECT_NAME}
@@ -9,33 +13,26 @@ function(signtool_sign_files PACKAGE_FILES SUBJECT_NAME CERT_HAS_UI)
/tr http://timestamp.comodoca.com/?td=sha256
)
if (NOT "${PACKAGE_FILES}" MATCHES "\\.msi$")
list(APPEND args /as)
endif()
foreach(file IN LISTS PACKAGE_FILES)
set(local_args ${args})
if (NOT "${file}" MATCHES "\\.msi$")
list(APPEND local_args /as)
endif()
set(cmd ${SIGNTOOL_COMMAND} ${args} ${PACKAGE_FILES})
set(cmd ${SIGNTOOL_COMMAND} ${local_args} ${file})
list(JOIN cmd " " cmd_str)
message(STATUS "${cmd_str}")
list(JOIN cmd " " cmd_str)
message(STATUS "${cmd_str}")
if (CERT_HAS_UI)
execute_process(
COMMAND cmd /c start "" /wait ${cmd}
RESULT_VARIABLE result
)
if(NOT result EQUAL 0)
message(FATAL_ERROR "signtool failed with code ${result}")
endif()
else()
execute_process(
execute_process(
COMMAND ${cmd}
RESULT_VARIABLE result
ERROR_VARIABLE error
)
if(NOT result EQUAL 0)
string(REPLACE "\n" "\n " error " ${error}")
message(FATAL_ERROR "signtool failed:\n${error}")
endif()
endif()
if(NOT result EQUAL 0)
string(REPLACE "\n" "\n " error " ${error}")
message(FATAL_ERROR "signtool failed:\n${error}")
endif()
endforeach()
endif()
endfunction()
+1 -5
View File
@@ -10,8 +10,6 @@ if /i "%~1" == "-i" set "ARG_BUILD_INSTALLERS=!ARG_BUILD_INSTALLERS!
if /i "%~1" == "--installer" set "ARG_BUILD_INSTALLERS=!ARG_BUILD_INSTALLERS! %~2" & shift
if /i "%~1" == "-arch" set "ARCH=%~2" & shift
if /i "%~1" == "--architecture" set "ARCH=%~2" & shift
if /i "%~1" == "--sign-cloud" set "SIGNTOOL_SUBJECT_NAME=%~2" & shift
if /i "%~1" == "--sign-phys" set "SIGNTOOL_SUBJECT_NAME=%~2" & set "SIGNTOOL_CERT_HAS_UI=true" & shift
shift
goto :parse_args
:done_args
@@ -96,10 +94,8 @@ if exist "%VCVARS_PATH%" (
)
:: build project and installers
set "_tests_arg="
if defined AMNEZIA_BUILD_TESTS set "_tests_arg=-DAMNEZIA_BUILD_TESTS=%AMNEZIA_BUILD_TESTS%"
@echo on
cmake -S "%PROJECT_DIR%" -B "%BUILD_DIR%" -DCMAKE_BUILD_TYPE=Release "-DCMAKE_PREFIX_PATH=%QT_ROOT_PATH%\msvc2022_%_qt_postfix_arg%" "-DCMAKE_VS_GLOBALS=UseMultiToolTask=true;EnforceProcessCountAcrossBuilds=true" %_tests_arg% || goto :fail
cmake -S "%PROJECT_DIR%" -B "%BUILD_DIR%" -DCMAKE_BUILD_TYPE=Release "-DCMAKE_PREFIX_PATH=%QT_ROOT_PATH%\msvc2022_%_qt_postfix_arg%" "-DCMAKE_VS_GLOBALS=UseMultiToolTask=true;EnforceProcessCountAcrossBuilds=true" || goto :fail
cmake --build "%BUILD_DIR%" --config Release -- /m || goto :fail
@echo off
for %%I in (%ARG_BUILD_INSTALLERS%) do (
-1
View File
@@ -198,7 +198,6 @@ args=()
[[ -n "$CMAKE_OSX_SYSROOT" ]] && args+=("-DCMAKE_OSX_SYSROOT=$CMAKE_OSX_SYSROOT")
[[ -n "$MACOS_NE" ]] && args+=("-DMACOS_NE=$MACOS_NE")
[[ -n "$DEPLOY" ]] && args+=("-DDEPLOY=$DEPLOY")
[[ -n "$AMNEZIA_BUILD_TESTS" ]] && args+=("-DAMNEZIA_BUILD_TESTS=$AMNEZIA_BUILD_TESTS")
[[ -n "$ANDROID_ABI" ]] && args+=("-DANDROID_ABI=$ANDROID_ABI")
[[ -n "$ANDROID_SDK_ROOT" ]] && args+=("-DANDROID_SDK_ROOT=$ANDROID_SDK_ROOT")
[[ -n "$ANDROID_NDK_ROOT" ]] && args+=("-DANDROID_NDK_ROOT=$ANDROID_NDK_ROOT")
+1 -2
View File
@@ -111,9 +111,8 @@ class AmneziaXrayBindings(ConanFile):
env.define("CGO_LDFLAGS", " ".join(ldflags))
with env.vars(self).apply():
at = Autotools(self)
make_build_dir = build_dir.replace("\\", "/") if self._is_windows else build_dir
at.make(args=[
f"BUILD_DIR={make_build_dir}"
f"BUILD_DIR={build_dir.replace("\\", "/") if self._is_windows else build_dir}"
])
if is_apple_os(self) and self._is_multiarch:
+3 -3
View File
@@ -60,9 +60,9 @@ class HevSocks5Tunnel(ConanFile):
self.run(
f"libtool -static -o {lib_path}"
f" {lib_path}"
f" {os.path.join(self.build_folder, 'third-part', 'lwip', 'bin', 'liblwip.a')}"
f" {os.path.join(self.build_folder, 'third-part', 'yaml', 'bin', 'libyaml.a')}"
f" {os.path.join(self.build_folder, 'third-part', 'hev-task-system', 'bin', 'libhev-task-system.a')}"
f" {os.path.join(self.build_folder, "third-part", "lwip", "bin", "liblwip.a")}"
f" {os.path.join(self.build_folder, "third-part", "yaml", "bin", "libyaml.a")}"
f" {os.path.join(self.build_folder, "third-part", "hev-task-system", "bin", "libhev-task-system.a")}"
)
include_dir = os.path.join(self.build_folder, "framework_include")
+1 -2
View File
@@ -114,9 +114,8 @@ class Tun2Socks(ConanFile):
env.define("CGO_CFLAGS", " ".join(cflags))
with env.vars(self).apply():
at = Autotools(self)
make_build_dir = build_dir.replace("\\", "/") if self._is_windows else build_dir
at.make("tun2socks", args=[
f"BUILD_DIR={make_build_dir}"
f"BUILD_DIR={build_dir.replace("\\", "/") if self._is_windows else build_dir}"
])
if self._is_windows:
os.rename(
@@ -8,24 +8,23 @@
# Apple Silicon have OpenGL emulation bugs that crash Qt apps.
#
# Prerequisites:
# - QT_ROOT points to a Qt installation with desktop + Android kits (e.g. ~/Qt/6.10.1)
# - ANDROID_HOME (or ANDROID_SDK_ROOT) points to an Android SDK with NDK $NDK_VERSION
# - cmake, ninja, adb, python3 in PATH
# - Qt for Android x86_64 (or arm64-v8a for devices) installed via Qt Maintenance Tool
# - Android SDK + NDK available via Android Studio
# - `adb devices` shows at least one device/emulator
# - Conan 2 installed (pip install "conan==2.28.0")
#
# Usage (from the repository root):
# bash client/tests/run_android_tests.sh # x86_64 emulator (default)
# bash client/tests/run_android_tests.sh --abi arm64-v8a # physical ARM device
# bash client/tests/run_android_tests.sh --test openssl # only openssl test
# bash client/tests/run_android_tests.sh --test restore_backup # only restore-backup test
# bash client/tests/run_android_tests.sh --wait-boot # wait for emulator to boot first
# Usage:
# bash run_android_tests.sh # x86_64 emulator (default)
# bash run_android_tests.sh --abi arm64-v8a # physical ARM device
# bash run_android_tests.sh --test openssl # only openssl test
# bash run_android_tests.sh --test restore_backup # only restore-backup test
# bash run_android_tests.sh --wait-boot # wait for emulator to boot first
set -euo pipefail
# ── Configurable defaults ──────────────────────────────────────────────────────
QT_ROOT="${QT_ROOT:?set QT_ROOT to your Qt installation, e.g. ~/Qt/6.10.1}"
ANDROID_SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:?set ANDROID_HOME to your Android SDK}}"
QT_ROOT="${QT_ROOT:-/Users/nickpc/Qt/6.10.1}"
ANDROID_SDK="${ANDROID_HOME:-/Users/nickpc/Library/Android/sdk}"
NDK_VERSION="${NDK_VERSION:-27.3.13750724}"
ANDROID_PLATFORM="${ANDROID_PLATFORM:-android-28}"
# x86_64: recommended for emulators (HVF/Rosetta hardware acceleration works, no GL crashes)
@@ -68,11 +67,7 @@ case "$ABI" in
esac
QT_ANDROID="$QT_ROOT/$QT_ARCH_DIR"
case "$(uname -s)" in
Darwin) QT_HOST="$QT_ROOT/macos" ;;
Linux) QT_HOST="$QT_ROOT/gcc_64" ;;
*) echo "Unsupported host: $(uname -s)"; exit 1 ;;
esac
QT_HOST="$QT_ROOT/macos"
NDK_PATH="$ANDROID_SDK/ndk/$NDK_VERSION"
echo "=== Android test runner (local) ==="
@@ -101,32 +96,43 @@ echo ""
adb devices | grep -qE "device$|emulator" || {
echo "ERROR: No Android device/emulator connected."
echo " Run: bash client/tests/run_android_tests.sh --list-devices"
echo " Then: bash client/tests/run_android_tests.sh --device <serial>"
echo " Run: bash run_android_tests.sh --list-devices"
echo " Then: bash run_android_tests.sh --device <serial>"
exit 1
}
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
cd "$REPO_ROOT"
cd "$SCRIPT_DIR"
command -v cmake >/dev/null || { echo "ERROR: cmake not found in PATH"; exit 1; }
command -v ninja >/dev/null || { echo "ERROR: ninja not found in PATH"; exit 1; }
# Detect the NDK host tag from what's present in the NDK
# (Apple Silicon Macs have only darwin-x86_64 in NDK ≤27, not darwin-aarch64)
NDK_HOST_TAG=""
for candidate in darwin-x86_64 darwin-aarch64 linux-x86_64; do
if [[ -d "$NDK_PATH/toolchains/llvm/prebuilt/$candidate" ]]; then
NDK_HOST_TAG="$candidate"
break
# ── Locate cmake ──────────────────────────────────────────────────────────────
if ! command -v cmake &>/dev/null; then
QT_CMAKE="/Users/nickpc/Qt/Tools/CMake/CMake.app/Contents/bin"
if [[ -x "$QT_CMAKE/cmake" ]]; then
export PATH="$QT_CMAKE:$PATH"
echo ">>> Using Qt-bundled cmake: $QT_CMAKE/cmake"
else
echo "ERROR: cmake not found. Install it or add it to PATH."
exit 1
fi
done
[[ -n "$NDK_HOST_TAG" ]] || { echo "ERROR: Cannot detect NDK host tag in $NDK_PATH/toolchains/llvm/prebuilt/"; exit 1; }
echo ">>> NDK host tag: $NDK_HOST_TAG"
fi
# ── Locate ninja ──────────────────────────────────────────────────────────────
if ! command -v ninja &>/dev/null; then
QT_NINJA="/Users/nickpc/Qt/Tools/Ninja"
if [[ -x "$QT_NINJA/ninja" ]]; then
export PATH="$QT_NINJA:$PATH"
echo ">>> Using Qt-bundled ninja: $QT_NINJA/ninja"
else
echo "ERROR: ninja not found. Install it (brew install ninja) or add it to PATH."
exit 1
fi
fi
# ── Add NDK toolchain to PATH (provides clang / clang++) ─────────────────────
export PATH="$NDK_PATH/toolchains/llvm/prebuilt/$NDK_HOST_TAG/bin:$PATH"
NDK_TOOLCHAIN="$NDK_PATH/toolchains/llvm/prebuilt/darwin-x86_64/bin"
if [[ -d "$NDK_TOOLCHAIN" ]]; then
export PATH="$NDK_TOOLCHAIN:$PATH"
fi
# ── Required env vars for androiddeployqt ────────────────────────────────────
export ANDROID_NDK_ROOT="$NDK_PATH"
@@ -155,6 +161,18 @@ echo ">>> JAVA_HOME: $JAVA_HOME"
QT_TOOLCHAIN="$QT_ANDROID/lib/cmake/Qt6/qt.toolchain.cmake"
[[ -f "$QT_TOOLCHAIN" ]] || { echo "ERROR: Qt toolchain not found: $QT_TOOLCHAIN"; exit 1; }
# Detect the actual NDK host tag from what's present in the NDK
# (Apple Silicon Macs have only darwin-x86_64 in NDK ≤27, not darwin-aarch64)
NDK_HOST_TAG=""
for candidate in darwin-x86_64 darwin-aarch64 linux-x86_64; do
if [[ -d "$NDK_PATH/toolchains/llvm/prebuilt/$candidate" ]]; then
NDK_HOST_TAG="$candidate"
break
fi
done
[[ -n "$NDK_HOST_TAG" ]] || { echo "ERROR: Cannot detect NDK host tag in $NDK_PATH/toolchains/llvm/prebuilt/"; exit 1; }
echo ">>> NDK host tag: $NDK_HOST_TAG"
# ABI → sysroot triple mapping for libc++_shared.so
case "$ABI" in
arm64-v8a) SYSROOT_TRIPLE="aarch64-linux-android" ;;
@@ -175,11 +193,10 @@ cmake -S . -B "$BUILD_DIR" -G Ninja \
-DCMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG="$NDK_HOST_TAG" \
-DQT_HOST_PATH="$QT_HOST" \
-DQt6_DIR="$QT_ANDROID/lib/cmake/Qt6" \
-DCMAKE_PREFIX_PATH="$QT_ANDROID" \
-DAMNEZIA_BUILD_TESTS=ON
-DCMAKE_PREFIX_PATH="$QT_ANDROID"
# Patch deployment settings JSONs: cmake may leave ndk-host empty on Apple Silicon
for json_file in "$BUILD_DIR"/client/tests/android-*-deployment-settings.json; do
for json_file in "$BUILD_DIR"/tests/android-*-deployment-settings.json; do
[[ -f "$json_file" ]] || continue
python3 - "$json_file" "$NDK_HOST_TAG" "$STD_CXX_PATH" <<'PYEOF'
import sys, json
@@ -204,7 +221,7 @@ ADB_BIN=$(which adb)
run_test() {
local target="$1"
local apk_dir="$BUILD_DIR/client/tests/android-build"
local apk_dir="$BUILD_DIR/tests/android-build"
local apk="$apk_dir/${target}.apk"
local pkg_name="org.qtproject.example.${target}"
local activity="${pkg_name}/org.qtproject.qt.android.bindings.QtActivity"