mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-11-14 16:43:28 +09:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee90a32c41 | ||
|
|
c6af4cc266 | ||
|
|
62a8a79931 | ||
|
|
f15932bf22 | ||
|
|
8165462865 | ||
|
|
864c1faab8 | ||
|
|
3a6bf7376c | ||
|
|
ba402f898a |
No files matched your search
@@ -14,7 +14,6 @@ runs:
|
||||
- name: Setup provisioning profile
|
||||
shell: bash
|
||||
run: |
|
||||
PROFILES_DIR="$HOME/Library/MobileDevice/Provisioning Profiles"
|
||||
TEMP_FILE=$(mktemp)
|
||||
|
||||
echo "${{ inputs.provisioning_profile_base64 }}" | base64 --decode > "$TEMP_FILE"
|
||||
@@ -26,6 +25,12 @@ runs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$PROFILES_DIR"
|
||||
mv "$TEMP_FILE" "$PROFILES_DIR/$PROFILE_UUID.mobileprovision"
|
||||
echo "Installed profile: $PROFILE_UUID"
|
||||
PROFILE_EXTENSION=mobileprovision
|
||||
grep -aq "<string>OSX</string>" "$TEMP_FILE" && PROFILE_EXTENSION=provisionprofile
|
||||
|
||||
for PROFILES_DIR in "$HOME/Library/MobileDevice/Provisioning Profiles" "$HOME/Library/Developer/Xcode/UserData/Provisioning Profiles"; do
|
||||
mkdir -p "$PROFILES_DIR"
|
||||
cp "$TEMP_FILE" "$PROFILES_DIR/$PROFILE_UUID.$PROFILE_EXTENSION"
|
||||
done
|
||||
rm -f "$TEMP_FILE"
|
||||
echo "Installed profile: $PROFILE_UUID.$PROFILE_EXTENSION"
|
||||
@@ -0,0 +1,23 @@
|
||||
## Version differences
|
||||
|
||||
### Android
|
||||
* For Android version 11 and higher, you need to use .apk that start with - AmneziaVPN_${VERSION}_android11+
|
||||
* For Android version 9-10, you need to use .apk that start with - AmneziaVPN_${VERSION}_android9-10
|
||||
* ***The app version for Android 7 and 8 is temporarily unavailable***
|
||||
|
||||
### macOS
|
||||
!!! The previous version (4.8.8.2 and lower) of the application (installed via the .dmg installer) must be uninstalled via “move to bin” in the applications folder
|
||||
|
||||
* For macOS version 13 and higher, you need to use .pkg - AmneziaVPN_${VERSION}_macos.pkg
|
||||
* ***The app version for macOS 10.15 - 12, is temporarily unavailable***
|
||||
|
||||
### Linux
|
||||
For Ubuntu you need to install xcb plugin:
|
||||
apt install libxcb-cursor0 libxcb-xinerama0 libxcb-icccm4 libxcb-keysyms1 libopengl0 libxkbcommon-x11-0
|
||||
|
||||
## Changelog
|
||||
|
||||
### General
|
||||
* Improve stability
|
||||
|
||||
**Full Changelog**: https://github.com/amnezia-vpn/amnezia-client/compare/${PREVIOUS_TAG}...${VERSION}
|
||||
+170
-35
@@ -4,6 +4,8 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
tags:
|
||||
- '[0-9]+.[0-9]+.[0-9]+.[0-9]+'
|
||||
|
||||
env:
|
||||
QT_MIRROR: https://mirrors.ocf.berkeley.edu/qt/ # https://download.qt.io/static/mirrorlist/
|
||||
@@ -415,12 +417,24 @@ jobs:
|
||||
APPSTORE_CONNECT_KEY_ID: ${{ secrets.APPSTORE_CONNECT_KEY_ID }}
|
||||
APPSTORE_CONNECT_ISSUER_ID: ${{ secrets.APPSTORE_CONNECT_ISSUER_ID }}
|
||||
APPSTORE_CONNECT_PRIVATE_KEY: ${{ secrets.APPSTORE_CONNECT_PRIVATE_KEY }}
|
||||
CONFIGURE_ONLY: ${{ startsWith(github.ref, 'refs/tags/') && 'true' || '' }}
|
||||
shell: bash
|
||||
run: |
|
||||
sh deploy/build.sh -t ios | \
|
||||
sed -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/d' | \
|
||||
sed -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/d' | \
|
||||
sed -e '/-DPROD_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DPROD_AGW_PUBLIC_KEY/d' | \
|
||||
sed -e '/-DDEV_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DDEV_AGW_PUBLIC_KEY/d'
|
||||
sh deploy/build.sh -t ios | sed -f deploy/hide_secrets.sed
|
||||
|
||||
- name: 'Archive for App Store'
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
working-directory: deploy
|
||||
shell: bash
|
||||
run: fastlane ios archive 2>&1 | sed -f hide_secrets.sed
|
||||
|
||||
- name: 'Upload App Store package'
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: deploy/build/ios/AmneziaVPN.ipa
|
||||
archive: false
|
||||
retention-days: 7
|
||||
|
||||
# ------------------------------------------------------
|
||||
|
||||
@@ -629,12 +643,7 @@ jobs:
|
||||
|
||||
env:
|
||||
QT_VERSION: 6.10.1
|
||||
|
||||
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
|
||||
|
||||
MAC_APP_CERT_CERT: ${{ secrets.MAC_APP_CERT_CERT }}
|
||||
MAC_SIGNER_ID: ${{ secrets.MAC_SIGNER_ID }}
|
||||
MAC_APP_CERT_PW: ${{ secrets.MAC_APP_CERT_PW }}
|
||||
KEYCHAIN_PASSWORD: ""
|
||||
|
||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||
@@ -652,13 +661,37 @@ jobs:
|
||||
submodules: 'true'
|
||||
fetch-depth: 10
|
||||
|
||||
- uses: ./.github/actions/apple-setup-provisioning-profile
|
||||
- uses: ./.github/actions/apple-setup-keychain
|
||||
id: setup-keychain
|
||||
with:
|
||||
provisioning_profile_base64: ${{ secrets.MAC_APP_PROVISIONING_PROFILE }}
|
||||
keychain-password: ${{ env.KEYCHAIN_PASSWORD }}
|
||||
|
||||
- uses: ./.github/actions/apple-setup-provisioning-profile
|
||||
- name: 'Install signing certificate'
|
||||
uses: ./.github/actions/apple-install-cert
|
||||
with:
|
||||
provisioning_profile_base64: ${{ secrets.MAC_NE_PROVISIONING_PROFILE }}
|
||||
keychain-path: ${{ steps.setup-keychain.outputs.keychain-path }}
|
||||
keychain-password: ${{ env.KEYCHAIN_PASSWORD }}
|
||||
cert-base64: ${{ secrets.IOS_SIGNING_CERT_BASE64 }}
|
||||
cert-password: ${{ secrets.IOS_SIGNING_CERT_PASSWORD }}
|
||||
|
||||
- name: 'Install installer certificate'
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: ./.github/actions/apple-install-cert
|
||||
with:
|
||||
keychain-path: ${{ steps.setup-keychain.outputs.keychain-path }}
|
||||
keychain-password: ${{ env.KEYCHAIN_PASSWORD }}
|
||||
cert-base64: ${{ secrets.MAC_APPSTORE_INSTALLER_CERT_BASE64 }}
|
||||
cert-password: ${{ secrets.MAC_APPSTORE_INSTALLER_CERT_PASSWORD }}
|
||||
|
||||
- name: 'Install app provisioning profile'
|
||||
uses: ./.github/actions/apple-setup-provisioning-profile
|
||||
with:
|
||||
provisioning_profile_base64: ${{ secrets.APPSTORE_CONNECT_MAC_PROVISIONING }}
|
||||
|
||||
- name: 'Install NE provisioning profile'
|
||||
uses: ./.github/actions/apple-setup-provisioning-profile
|
||||
with:
|
||||
provisioning_profile_base64: ${{ secrets.APPSTORE_CONNECT_MAC_NE_PROVISIONING }}
|
||||
|
||||
- name: 'Setup xcode'
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
@@ -677,18 +710,6 @@ jobs:
|
||||
set-env: 'true'
|
||||
extra: '--base ${{ env.QT_MIRROR }}'
|
||||
|
||||
- name: 'Install go'
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24'
|
||||
cache: false
|
||||
|
||||
- name: 'Setup gomobile'
|
||||
run: |
|
||||
export PATH=$PATH:~/go/bin
|
||||
go install golang.org/x/mobile/cmd/gomobile@latest
|
||||
gomobile init
|
||||
|
||||
- name: 'Setup python'
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
@@ -698,15 +719,32 @@ jobs:
|
||||
run: pip install "conan==2.28.0"
|
||||
|
||||
- name: 'Build project'
|
||||
run: |
|
||||
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
||||
bash deploy/build_macos_ne.sh
|
||||
env:
|
||||
QT_INSTALL_DIR: ${{ runner.temp }}
|
||||
CONFIGURE_ONLY: ${{ startsWith(github.ref, 'refs/tags/') && 'true' || '' }}
|
||||
shell: bash
|
||||
run: deploy/build.sh -t macos-ne | sed -f deploy/hide_secrets.sed
|
||||
|
||||
- name: 'Upload unpacked artifact'
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN_MacOS_unpacked
|
||||
path: deploy/build/client/AmneziaVPN.app
|
||||
path: deploy/build/client/Release/AmneziaVPN.app
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Archive for App Store'
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
working-directory: deploy
|
||||
shell: bash
|
||||
run: fastlane mac archive 2>&1 | sed -f hide_secrets.sed
|
||||
|
||||
- name: 'Upload App Store package'
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: deploy/build/osx/AmneziaVPN.pkg
|
||||
archive: false
|
||||
retention-days: 7
|
||||
|
||||
# ------------------------------------------------------
|
||||
@@ -802,6 +840,8 @@ jobs:
|
||||
APP_ANDROID_MIN_SDK: ${{ matrix.min_sdk }}
|
||||
APP_ANDROID_MAX_SDK: ${{ matrix.max_sdk }}
|
||||
APP_ANDROID_VERSION_CODE_OFFSET: ${{ matrix.version_code_offset }}
|
||||
CMAKE_C_COMPILER_LAUNCHER: ccache
|
||||
CMAKE_CXX_COMPILER_LAUNCHER: ccache
|
||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||
FALLBACK_S3_ENDPOINT: ${{ secrets.FALLBACK_S3_ENDPOINT }}
|
||||
@@ -896,6 +936,12 @@ jobs:
|
||||
- name: 'Install conan'
|
||||
run: pip install "conan==2.28.0"
|
||||
|
||||
- name: 'Setup ccache'
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: android-${{ matrix.label }}
|
||||
max-size: 1G
|
||||
|
||||
- name: 'Decode keystore secret to file'
|
||||
env:
|
||||
KEYSTORE_BASE64: ${{ secrets.ANDROID_RELEASE_KEYSTORE_BASE64 }}
|
||||
@@ -933,8 +979,7 @@ jobs:
|
||||
fi
|
||||
|
||||
for abi in arm64-v8a armeabi-v7a x86 x86_64; do
|
||||
deploy/build.sh -t android --sign --abi ${abi} --build ./deploy/build/${abi}
|
||||
mv deploy/build/${abi}/client/android-build/AmneziaVPN.apk \
|
||||
mv deploy/build/client/android-build/build/outputs/apk/oss/release/*-${abi}-release.apk \
|
||||
$OUT/AmneziaVPN_${VERSION}_${LABEL}_${abi}.apk
|
||||
done
|
||||
|
||||
@@ -953,9 +998,9 @@ jobs:
|
||||
OUT=deploy/artifacts
|
||||
VERSION=$(grep CMAKE_PROJECT_VERSION:STATIC deploy/build/CMakeCache.txt | cut -d= -f2)
|
||||
|
||||
deploy/build.sh -t android --sign --aab --play --build ./deploy/build/play
|
||||
deploy/build.sh -t android --sign --aab --play
|
||||
|
||||
mv deploy/build/play/client/android-build/build/outputs/bundle/playRelease/android-build-play-release.aab \
|
||||
mv deploy/build/client/android-build/build/outputs/bundle/playRelease/android-build-play-release.aab \
|
||||
$OUT/AmneziaVPN_${VERSION}_${LABEL}_play.aab
|
||||
|
||||
- name: 'Upload universal APK'
|
||||
@@ -1647,3 +1692,93 @@ jobs:
|
||||
disable-animations: true
|
||||
script: bash ci_run_android_tests.sh
|
||||
|
||||
# ------------------------------------------------------
|
||||
|
||||
Pre-Release:
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
needs: [Build-Linux-Ubuntu, Build-MacOS, Build-Android]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: 'Get release notes template'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: .github/release-notes.md
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
- name: 'Download Android APKs'
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: 'AmneziaVPN_*_{android11+,android9-10}_{arm64-v8a,armeabi-v7a,x86,x86_64}.apk'
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
||||
- name: 'Download desktop installers'
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: 'AmneziaVPN_*_{linux_x64.run,macos_x64.pkg}'
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
||||
- name: 'Publish pre-release'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ github.ref_name }}
|
||||
run: |
|
||||
ls -l dist
|
||||
if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
|
||||
gh release upload "$TAG" dist/* --repo "$GITHUB_REPOSITORY" --clobber
|
||||
else
|
||||
export VERSION="$TAG"
|
||||
export PREVIOUS_TAG=$(gh release view --repo "$GITHUB_REPOSITORY" --json tagName -q .tagName)
|
||||
envsubst '$VERSION $PREVIOUS_TAG' < .github/release-notes.md > release-notes.md
|
||||
|
||||
gh release create "$TAG" dist/* --repo "$GITHUB_REPOSITORY" \
|
||||
--title "$TAG" --notes-file release-notes.md --draft --prerelease --verify-tag
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------
|
||||
|
||||
TestFlight:
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
needs: [Build-Linux-Ubuntu, Build-Windows, Build-iOS, Build-MacOS, Build-MacOS-NE, Build-Android]
|
||||
runs-on: macos-26
|
||||
|
||||
env:
|
||||
APPSTORE_CONNECT_KEY_ID: ${{ secrets.APPSTORE_CONNECT_KEY_ID }}
|
||||
APPSTORE_CONNECT_ISSUER_ID: ${{ secrets.APPSTORE_CONNECT_ISSUER_ID }}
|
||||
APPSTORE_CONNECT_PRIVATE_KEY: ${{ secrets.APPSTORE_CONNECT_PRIVATE_KEY }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: deploy
|
||||
shell: bash
|
||||
|
||||
steps:
|
||||
- name: 'Get fastlane config'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: |
|
||||
deploy/fastlane
|
||||
deploy/hide_secrets.sed
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
- name: 'Download iOS package'
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: AmneziaVPN.ipa
|
||||
path: deploy/build/ios
|
||||
|
||||
- name: 'Download macOS package'
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: AmneziaVPN.pkg
|
||||
path: deploy/build/osx
|
||||
|
||||
- name: 'Upload iOS build to TestFlight'
|
||||
run: fastlane ios testflight_upload 2>&1 | sed -f hide_secrets.sed
|
||||
|
||||
- name: 'Upload macOS build to TestFlight'
|
||||
run: fastlane mac testflight_upload 2>&1 | sed -f hide_secrets.sed
|
||||
+2
-2
@@ -5,7 +5,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
if(NOT AMNEZIAVPN_VERSION)
|
||||
set(AMNEZIAVPN_VERSION 5.1.0.6 CACHE STRING "Client app version")
|
||||
set(AMNEZIAVPN_VERSION 5.1.1.2 CACHE STRING "Client app version")
|
||||
endif()
|
||||
|
||||
set(QT_CREATOR_SKIP_PACKAGE_MANAGER_SETUP ON CACHE BOOL "" FORCE)
|
||||
@@ -34,7 +34,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 2190)
|
||||
set(APP_ANDROID_VERSION_CODE 2192)
|
||||
if(DEFINED APP_ANDROID_VERSION_CODE_OFFSET)
|
||||
math(EXPR APP_ANDROID_VERSION_CODE "${APP_ANDROID_VERSION_CODE} + ${APP_ANDROID_VERSION_CODE_OFFSET}")
|
||||
endif()
|
||||
|
||||
@@ -54,6 +54,7 @@ android {
|
||||
storePassword = providers.environmentVariable("QT_ANDROID_KEYSTORE_STORE_PASS").orNull
|
||||
keyAlias = providers.environmentVariable("QT_ANDROID_KEYSTORE_ALIAS").orNull
|
||||
keyPassword = providers.environmentVariable("QT_ANDROID_KEYSTORE_STORE_PASS").orNull
|
||||
enableV3Signing = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +112,7 @@ android {
|
||||
isEnable = abiList.size > 1
|
||||
reset()
|
||||
include(*abiList.toTypedArray())
|
||||
isUniversalApk = false
|
||||
isUniversalApk = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +123,8 @@ android {
|
||||
outputs.map { it as BaseVariantOutputImpl }
|
||||
.forEach { output ->
|
||||
if (output.outputFileName.endsWith(".apk")) {
|
||||
output.outputFileName = "$outputBaseName-${buildType.name}.apk"
|
||||
output.outputFileName = listOfNotNull(outputBaseName, output.getFilter("ABI"), buildType.name)
|
||||
.joinToString("-", postfix = ".apk")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,14 +145,15 @@ android {
|
||||
val base = outputBaseName.ifEmpty { "android-build" }
|
||||
val unsignedSuffix = if (buildTypeName == "release") "-unsigned" else ""
|
||||
|
||||
val universalOutput = outputs.map { it as BaseVariantOutputImpl }.first { it.getFilter("ABI") == null }
|
||||
|
||||
packageApplicationProvider.configure {
|
||||
doLast {
|
||||
val srcDir = layout.buildDirectory.dir("outputs/apk/oss/$buildTypeName").get().asFile
|
||||
val dstDir = layout.buildDirectory.dir("outputs/apk").get().asFile
|
||||
dstDir.mkdirs()
|
||||
srcDir.listFiles()?.filter { it.name.endsWith(".apk") }?.forEach { apk ->
|
||||
apk.copyTo(File(dstDir, "$base-$buildTypeName$unsignedSuffix.apk"), overwrite = true)
|
||||
}
|
||||
File(srcDir, universalOutput.outputFileName)
|
||||
.copyTo(File(dstDir, "$base-$buildTypeName$unsignedSuffix.apk"), overwrite = true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,14 +32,14 @@ endif()
|
||||
|
||||
# -- signing
|
||||
if(NOT CLIENT_IOS_PROVISIONING_PROFILE_SPECIFIER)
|
||||
set(CLIENT_IOS_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.AmneziaVPN" CACHE STRING "iOS app provisioning profile specifier for release")
|
||||
set(CLIENT_IOS_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.amneziaVPN" CACHE STRING "iOS app provisioning profile specifier for release")
|
||||
endif()
|
||||
if(NOT CLIENT_IOS_PROVISIONING_PROFILE_SPECIFIER_DEBUG)
|
||||
set(CLIENT_IOS_PROVISIONING_PROFILE_SPECIFIER_DEBUG "dev ios.org.amnezia.AmneziaVPN" CACHE STRING "iOS app provisioning profile specifier for debug")
|
||||
set(CLIENT_IOS_PROVISIONING_PROFILE_SPECIFIER_DEBUG "dev ios.org.amnezia.amneziaVPN" CACHE STRING "iOS app provisioning profile specifier for debug")
|
||||
endif()
|
||||
if(NOT CLIENT_IOS_NE_PROVISIONING_PROFILE_SPECIFIER)
|
||||
set(CLIENT_IOS_NE_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.AmneziaVPN" CACHE STRING "iOS network extension provisioning profile specifier for release")
|
||||
set(CLIENT_IOS_NE_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.amneziaVPN.NE" CACHE STRING "iOS network extension provisioning profile specifier for release")
|
||||
endif()
|
||||
if(NOT CLIENT_IOS_NE_PROVISIONING_PROFILE_SPECIFIER_DEBUG)
|
||||
set(CLIENT_IOS_NE_PROVISIONING_PROFILE_SPECIFIER_DEBUG "dev ios.org.amnezia.AmneziaVPN" CACHE STRING "iOS network extension provisioning profile specifier for debug")
|
||||
set(CLIENT_IOS_NE_PROVISIONING_PROFILE_SPECIFIER_DEBUG "dev ios.org.amnezia.amneziaVPN.NE" CACHE STRING "iOS network extension provisioning profile specifier for debug")
|
||||
endif()
|
||||
@@ -24,7 +24,7 @@ endif()
|
||||
|
||||
# -- signing
|
||||
if(NOT CLIENT_MACOS_PROVISIONING_PROFILE_SPECIFIER)
|
||||
set(CLIENT_MACOS_PROVISIONING_PROFILE_SPECIFIER "distr macos.org.amnezia.AmneziaVPN" CACHE STRING "macOS app provisioning profile specifier for release")
|
||||
set(CLIENT_MACOS_PROVISIONING_PROFILE_SPECIFIER "distr macos.org.amnezia.amneziaVPN" CACHE STRING "macOS app provisioning profile specifier for release")
|
||||
endif()
|
||||
if(NOT CLIENT_MACOS_PROVISIONING_PROFILE_SPECIFIER_DEBUG)
|
||||
set(CLIENT_MACOS_PROVISIONING_PROFILE_SPECIFIER_DEBUG "dev macos.org.amnezia.AmneziaVPN" CACHE STRING "macOS app provisioning profile specifier for debug")
|
||||
|
||||
@@ -66,6 +66,8 @@ ProtocolConfig AwgConfigurator::createConfig(const ServerCredentials &credential
|
||||
newClientConfig.nativeConfig = awgConfig;
|
||||
newClientConfig.hostName = wgConfig->clientConfig->hostName;
|
||||
newClientConfig.port = wgConfig->clientConfig->port;
|
||||
newClientConfig.cascadeEndpoint = wgConfig->clientConfig->cascadeEndpoint;
|
||||
newClientConfig.cascadePort = wgConfig->clientConfig->cascadePort;
|
||||
newClientConfig.clientIp = wgConfig->clientConfig->clientIp;
|
||||
newClientConfig.clientPrivateKey = wgConfig->clientConfig->clientPrivateKey;
|
||||
newClientConfig.clientPublicKey = wgConfig->clientConfig->clientPublicKey;
|
||||
|
||||
@@ -266,6 +266,11 @@ ProtocolConfig WireguardConfigurator::createConfig(const ServerCredentials &cred
|
||||
clientConfig.nativeConfig = config;
|
||||
clientConfig.hostName = connData.host;
|
||||
clientConfig.port = connData.port.toInt();
|
||||
if (wireguardServerConfig) {
|
||||
clientConfig.setCascade(wireguardServerConfig->cascadeEndpoint, wireguardServerConfig->cascadePort);
|
||||
} else if (awgServerConfig) {
|
||||
clientConfig.setCascade(awgServerConfig->cascadeEndpoint, awgServerConfig->cascadePort);
|
||||
}
|
||||
clientConfig.clientIp = connData.clientIP;
|
||||
clientConfig.clientPrivateKey = connData.clientPrivKey;
|
||||
clientConfig.clientPublicKey = connData.clientPubKey;
|
||||
|
||||
@@ -289,6 +289,15 @@ QJsonObject ConnectionController::createConnectionConfiguration(const QPair<QStr
|
||||
ContainerUtils::isAwgContainer(container) ? protocols::awg::defaultMtu :
|
||||
protocols::wireguard::defaultMtu;
|
||||
}
|
||||
|
||||
const QString cascadeEndpoint = vpnConfigData.value(configKey::cascadeEndpoint).toString();
|
||||
if (!cascadeEndpoint.isEmpty()) {
|
||||
vpnConfigData[configKey::hostName] = cascadeEndpoint;
|
||||
}
|
||||
const QString cascadePort = vpnConfigData.value(configKey::cascadePort).toString();
|
||||
if (!cascadePort.isEmpty()) {
|
||||
vpnConfigData[configKey::port] = cascadePort.toInt();
|
||||
}
|
||||
}
|
||||
|
||||
vpnConfiguration.insert(ProtocolUtils::key_proto_config_data(proto), vpnConfigData);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "installerBase.h"
|
||||
|
||||
#include <QRandomGenerator>
|
||||
#include <iterator>
|
||||
|
||||
#include "core/utils/containerEnum.h"
|
||||
#include "core/utils/containers/containerUtils.h"
|
||||
#include "core/utils/protocolEnum.h"
|
||||
@@ -85,8 +88,10 @@ ContainerConfig InstallerBase::createBaseConfig(DockerContainer container, int p
|
||||
srv.transport = protocols::xray::defaultTransport;
|
||||
srv.security = protocols::xray::defaultSecurity;
|
||||
srv.flow = protocols::xray::defaultFlow;
|
||||
srv.site = protocols::xray::defaultSite;
|
||||
srv.sni = protocols::xray::defaultSni;
|
||||
const int siteIndex =
|
||||
QRandomGenerator::global()->bounded(static_cast<int>(std::size(protocols::xray::defaultSites)));
|
||||
srv.site = protocols::xray::defaultSites[siteIndex];
|
||||
srv.sni = srv.site;
|
||||
srv.alpn = protocols::xray::defaultAlpn;
|
||||
srv.fingerprint = protocols::xray::defaultFingerprint;
|
||||
config.protocolConfig = xrayConfig;
|
||||
|
||||
@@ -72,6 +72,12 @@ QJsonObject AwgServerConfig::toJson() const
|
||||
if (!port.isEmpty()) {
|
||||
obj[configKey::port] = this->port;
|
||||
}
|
||||
if (!cascadeEndpoint.isEmpty()) {
|
||||
obj[configKey::cascadeEndpoint] = cascadeEndpoint;
|
||||
}
|
||||
if (!cascadePort.isEmpty()) {
|
||||
obj[configKey::cascadePort] = cascadePort;
|
||||
}
|
||||
if (!transportProto.isEmpty()) {
|
||||
obj[configKey::transportProto] = transportProto;
|
||||
}
|
||||
@@ -166,6 +172,8 @@ AwgServerConfig AwgServerConfig::fromJson(const QJsonObject& json)
|
||||
AwgServerConfig config;
|
||||
|
||||
config.port = json.value(configKey::port).toString();
|
||||
config.cascadeEndpoint = json.value(configKey::cascadeEndpoint).toString();
|
||||
config.cascadePort = json.value(configKey::cascadePort).toString();
|
||||
config.transportProto = json.value(configKey::transportProto).toString();
|
||||
config.protocolVersion = json.value(configKey::protocolVersion).toString();
|
||||
config.subnetAddress = json.value(configKey::subnetAddress).toString();
|
||||
@@ -218,6 +226,12 @@ QJsonObject AwgClientConfig::toJson() const
|
||||
if (port > 0) {
|
||||
obj[configKey::port] = port;
|
||||
}
|
||||
if (!cascadeEndpoint.isEmpty()) {
|
||||
obj[configKey::cascadeEndpoint] = cascadeEndpoint;
|
||||
}
|
||||
if (!cascadePort.isEmpty()) {
|
||||
obj[configKey::cascadePort] = cascadePort;
|
||||
}
|
||||
if (!clientIp.isEmpty()) {
|
||||
obj[configKey::clientIp] = clientIp;
|
||||
}
|
||||
@@ -333,6 +347,14 @@ QJsonObject AwgClientConfig::toJson() const
|
||||
return obj;
|
||||
}
|
||||
|
||||
void AwgClientConfig::setCascade(const QString &newEndpoint, const QString &newPort)
|
||||
{
|
||||
cascadeEndpoint = newEndpoint;
|
||||
cascadePort = newPort;
|
||||
nativeConfig = replaceWireGuardEndpoint(nativeConfig, newEndpoint.isEmpty() ? hostName : newEndpoint,
|
||||
newPort.isEmpty() ? port : newPort.toInt());
|
||||
}
|
||||
|
||||
AwgClientConfig AwgClientConfig::fromJson(const QJsonObject& json)
|
||||
{
|
||||
AwgClientConfig config;
|
||||
@@ -340,6 +362,8 @@ AwgClientConfig AwgClientConfig::fromJson(const QJsonObject& json)
|
||||
config.nativeConfig = json.value(configKey::config).toString();
|
||||
config.hostName = json.value(configKey::hostName).toString();
|
||||
config.port = json.value(configKey::port).toInt(0);
|
||||
config.cascadeEndpoint = json.value(configKey::cascadeEndpoint).toString();
|
||||
config.cascadePort = json.value(configKey::cascadePort).toString();
|
||||
config.clientIp = json.value(configKey::clientIp).toString();
|
||||
config.clientPrivateKey = json.value(configKey::clientPrivKey).toString();
|
||||
config.clientPublicKey = json.value(configKey::clientPubKey).toString();
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace AwgConstant
|
||||
|
||||
struct AwgServerConfig {
|
||||
QString port;
|
||||
QString cascadeEndpoint;
|
||||
QString cascadePort;
|
||||
QString transportProto;
|
||||
QString protocolVersion;
|
||||
QString subnetAddress;
|
||||
@@ -61,6 +63,8 @@ struct AwgClientConfig {
|
||||
QString nativeConfig;
|
||||
QString hostName;
|
||||
int port;
|
||||
QString cascadeEndpoint;
|
||||
QString cascadePort;
|
||||
QString clientIp;
|
||||
QString clientPrivateKey;
|
||||
QString clientPublicKey;
|
||||
@@ -95,6 +99,9 @@ struct AwgClientConfig {
|
||||
QString maxHandshakeAttempts;
|
||||
QString randomTrailers;
|
||||
QString disableCookies;
|
||||
|
||||
void setCascade(const QString &newEndpoint, const QString &newPort);
|
||||
|
||||
QJsonObject toJson() const;
|
||||
static AwgClientConfig fromJson(const QJsonObject& json);
|
||||
};
|
||||
|
||||
@@ -20,6 +20,12 @@ QJsonObject WireGuardServerConfig::toJson() const
|
||||
if (!port.isEmpty()) {
|
||||
obj[configKey::port] = port;
|
||||
}
|
||||
if (!cascadeEndpoint.isEmpty()) {
|
||||
obj[configKey::cascadeEndpoint] = cascadeEndpoint;
|
||||
}
|
||||
if (!cascadePort.isEmpty()) {
|
||||
obj[configKey::cascadePort] = cascadePort;
|
||||
}
|
||||
if (!transportProto.isEmpty()) {
|
||||
obj[configKey::transportProto] = transportProto;
|
||||
}
|
||||
@@ -45,6 +51,8 @@ WireGuardServerConfig WireGuardServerConfig::fromJson(const QJsonObject& json)
|
||||
WireGuardServerConfig config;
|
||||
|
||||
config.port = json.value(configKey::port).toString();
|
||||
config.cascadeEndpoint = json.value(configKey::cascadeEndpoint).toString();
|
||||
config.cascadePort = json.value(configKey::cascadePort).toString();
|
||||
config.transportProto = json.value(configKey::transportProto).toString();
|
||||
config.subnetAddress = json.value(configKey::subnetAddress).toString();
|
||||
config.subnetMask = json.value(configKey::subnetMask).toString();
|
||||
@@ -73,6 +81,12 @@ QJsonObject WireGuardClientConfig::toJson() const
|
||||
if (port > 0) {
|
||||
obj[configKey::port] = port;
|
||||
}
|
||||
if (!cascadeEndpoint.isEmpty()) {
|
||||
obj[configKey::cascadeEndpoint] = cascadeEndpoint;
|
||||
}
|
||||
if (!cascadePort.isEmpty()) {
|
||||
obj[configKey::cascadePort] = cascadePort;
|
||||
}
|
||||
if (!clientIp.isEmpty()) {
|
||||
obj[configKey::clientIp] = clientIp;
|
||||
}
|
||||
@@ -119,6 +133,14 @@ QJsonObject WireGuardClientConfig::toJson() const
|
||||
return obj;
|
||||
}
|
||||
|
||||
void WireGuardClientConfig::setCascade(const QString &newEndpoint, const QString &newPort)
|
||||
{
|
||||
cascadeEndpoint = newEndpoint;
|
||||
cascadePort = newPort;
|
||||
nativeConfig = replaceWireGuardEndpoint(nativeConfig, newEndpoint.isEmpty() ? hostName : newEndpoint,
|
||||
newPort.isEmpty() ? port : newPort.toInt());
|
||||
}
|
||||
|
||||
WireGuardClientConfig WireGuardClientConfig::fromJson(const QJsonObject& json)
|
||||
{
|
||||
WireGuardClientConfig config;
|
||||
@@ -126,6 +148,8 @@ WireGuardClientConfig WireGuardClientConfig::fromJson(const QJsonObject& json)
|
||||
config.nativeConfig = json.value(configKey::config).toString();
|
||||
config.hostName = json.value(configKey::hostName).toString();
|
||||
config.port = json.value(configKey::port).toInt(0);
|
||||
config.cascadeEndpoint = json.value(configKey::cascadeEndpoint).toString();
|
||||
config.cascadePort = json.value(configKey::cascadePort).toString();
|
||||
config.clientIp = json.value(configKey::clientIp).toString();
|
||||
config.clientPrivateKey = json.value(configKey::clientPrivKey).toString();
|
||||
config.clientPublicKey = json.value(configKey::clientPubKey).toString();
|
||||
|
||||
@@ -12,6 +12,8 @@ namespace amnezia
|
||||
|
||||
struct WireGuardServerConfig {
|
||||
QString port;
|
||||
QString cascadeEndpoint;
|
||||
QString cascadePort;
|
||||
QString transportProto;
|
||||
QString subnetAddress;
|
||||
QString subnetMask;
|
||||
@@ -28,6 +30,8 @@ struct WireGuardClientConfig {
|
||||
QString nativeConfig;
|
||||
QString hostName;
|
||||
int port;
|
||||
QString cascadeEndpoint;
|
||||
QString cascadePort;
|
||||
QString clientIp;
|
||||
QString clientPrivateKey;
|
||||
QString clientPublicKey;
|
||||
@@ -41,6 +45,8 @@ struct WireGuardClientConfig {
|
||||
QMap<QString, QString> awgParams;
|
||||
bool isObfuscationEnabled = false;
|
||||
|
||||
void setCascade(const QString &newEndpoint, const QString &newPort);
|
||||
|
||||
QJsonObject toJson() const;
|
||||
static WireGuardClientConfig fromJson(const QJsonObject& json);
|
||||
};
|
||||
|
||||
@@ -354,9 +354,6 @@ void XrayServerConfig::applyDefaults(bool fillFlowDefault)
|
||||
transportProto = ProtocolUtils::transportProtoToString(ProtocolUtils::defaultTransportProto(Proto::Xray),
|
||||
Proto::Xray);
|
||||
}
|
||||
if (site.isEmpty()) {
|
||||
site = protocols::xray::defaultSite;
|
||||
}
|
||||
if (transport.isEmpty()) {
|
||||
transport = protocols::xray::defaultTransport;
|
||||
}
|
||||
@@ -367,7 +364,7 @@ void XrayServerConfig::applyDefaults(bool fillFlowDefault)
|
||||
flow = protocols::xray::defaultFlow;
|
||||
}
|
||||
if (sni.isEmpty()) {
|
||||
sni = site.isEmpty() ? QString::fromLatin1(protocols::xray::defaultSni) : site;
|
||||
sni = site;
|
||||
}
|
||||
if (alpn.isEmpty()) {
|
||||
alpn = protocols::xray::defaultAlpn;
|
||||
@@ -569,7 +566,7 @@ QJsonObject XrayServerConfig::streamSettingsJson(XrayStreamSide side) const
|
||||
|
||||
if (securityEff == QLatin1String(px::securityTls)) {
|
||||
QJsonObject tlsSettings;
|
||||
tlsSettings[px::serverName] = sni.isEmpty() ? QString::fromLatin1(px::defaultSni) : sni;
|
||||
tlsSettings[px::serverName] = sni;
|
||||
const QString alpnEff = alpn.isEmpty() ? QString::fromLatin1(px::defaultAlpn) : alpn;
|
||||
QJsonArray alpnArray;
|
||||
for (const QString &a : alpnEff.split(QLatin1Char(','))) {
|
||||
@@ -598,7 +595,7 @@ QJsonObject XrayServerConfig::streamSettingsJson(XrayStreamSide side) const
|
||||
if (clientSide && securityEff == QLatin1String(px::securityReality)) {
|
||||
QJsonObject realitySettings;
|
||||
realitySettings[px::fingerprint] = fingerprintEff;
|
||||
realitySettings[px::serverName] = sni.isEmpty() ? QString::fromLatin1(px::defaultSni) : sni;
|
||||
realitySettings[px::serverName] = sni;
|
||||
streamSettings[px::realitySettings] = realitySettings;
|
||||
}
|
||||
|
||||
@@ -1139,11 +1136,10 @@ QJsonObject XrayServerConfig::toServerInboundJson(const XrayServerInboundInputs
|
||||
|
||||
QJsonObject streamSettings = serverStreamSettings();
|
||||
if (xrayEffective::security(*this) == QLatin1String(px::securityReality)) {
|
||||
const QString siteEff = site.isEmpty() ? QString::fromLatin1(px::defaultSite) : site;
|
||||
QJsonObject realitySettings;
|
||||
realitySettings[px::dest] = siteEff + QStringLiteral(":443");
|
||||
realitySettings[px::dest] = site + QStringLiteral(":443");
|
||||
realitySettings[px::privateKey] = inputs.realityPrivateKey;
|
||||
realitySettings[px::serverNames] = QJsonArray { sni.isEmpty() ? siteEff : sni };
|
||||
realitySettings[px::serverNames] = QJsonArray { sni.isEmpty() ? site : sni };
|
||||
realitySettings[px::shortIds] = QJsonArray { inputs.realityShortId };
|
||||
streamSettings[px::realitySettings] = realitySettings;
|
||||
}
|
||||
@@ -1323,10 +1319,9 @@ QJsonObject XrayServerConfig::serverView() const
|
||||
view[px::streamSettings] = serverStreamSettings();
|
||||
|
||||
if (xrayEffective::security(*this) == QLatin1String(px::securityReality)) {
|
||||
const QString siteEff = site.isEmpty() ? QString::fromLatin1(px::defaultSite) : site;
|
||||
QJsonObject reality;
|
||||
reality[px::dest] = siteEff;
|
||||
reality[px::serverName] = sni.isEmpty() ? siteEff : sni;
|
||||
reality[px::dest] = site;
|
||||
reality[px::serverName] = sni.isEmpty() ? site : sni;
|
||||
view[QStringLiteral("reality")] = reality;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "protocolUtils.h"
|
||||
|
||||
#include <QRandomGenerator>
|
||||
#include <QRegularExpression>
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
|
||||
@@ -216,3 +217,10 @@ QString ProtocolUtils::key_proto_config_path(Proto p)
|
||||
return protoToString(p) + "_config_path";
|
||||
}
|
||||
|
||||
QString ProtocolUtils::replaceWireGuardEndpoint(QString nativeConfig, const QString &host, int port)
|
||||
{
|
||||
static const QRegularExpression endpointLine(QString(R"(^(\s*%1\s*=\s*).*$)").arg(protocols::wireguard::Endpoint),
|
||||
QRegularExpression::MultilineOption);
|
||||
const QString endpointHost = host.contains(':') ? QString("[%1]").arg(host) : host;
|
||||
return nativeConfig.replace(endpointLine, QString("\\1%1:%2").arg(endpointHost).arg(port));
|
||||
}
|
||||
@@ -39,6 +39,8 @@ namespace amnezia
|
||||
QString key_proto_config_data(Proto p);
|
||||
QString key_proto_config_path(Proto p);
|
||||
|
||||
QString replaceWireGuardEndpoint(QString nativeConfig, const QString &host, int port);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace amnezia
|
||||
constexpr QLatin1String password("password");
|
||||
constexpr QLatin1String port("port");
|
||||
constexpr QLatin1String localPort("local_port");
|
||||
constexpr QLatin1String cascadeEndpoint("cascade_endpoint");
|
||||
constexpr QLatin1String cascadePort("cascade_port");
|
||||
|
||||
constexpr QLatin1String dns1("dns1");
|
||||
constexpr QLatin1String dns2("dns2");
|
||||
|
||||
@@ -57,7 +57,22 @@ namespace amnezia
|
||||
constexpr char shortidPath[] = "/opt/amnezia/xray/xray_short_id.key";
|
||||
constexpr char tlsCertPath[] = "/opt/amnezia/xray/tls_cert.pem";
|
||||
constexpr char tlsKeyPath[] = "/opt/amnezia/xray/tls_key.pem";
|
||||
constexpr char defaultSite[] = "www.googletagmanager.com";
|
||||
constexpr const char *defaultSites[] = {
|
||||
"www.microsoft.com",
|
||||
"www.apple.com",
|
||||
"www.icloud.com",
|
||||
"dl.google.com",
|
||||
"www.bing.com",
|
||||
"www.samsung.com",
|
||||
"www.nvidia.com",
|
||||
"www.cisco.com",
|
||||
"www.asus.com",
|
||||
"www.logitech.com",
|
||||
"www.dropbox.com",
|
||||
"www.mozilla.org",
|
||||
"addons.mozilla.org",
|
||||
"www.speedtest.net",
|
||||
};
|
||||
|
||||
constexpr char defaultPort[] = "443";
|
||||
constexpr char defaultLocalProxyPort[] = "10808";
|
||||
@@ -69,8 +84,7 @@ namespace amnezia
|
||||
constexpr char defaultSecurity[] = "reality";
|
||||
constexpr char defaultFlow[] = "xtls-rprx-vision";
|
||||
constexpr char defaultTransport[] = "raw";
|
||||
constexpr char defaultFingerprint[] = "hellochrome_120";
|
||||
constexpr char defaultSni[] = "www.googletagmanager.com";
|
||||
constexpr char defaultFingerprint[] = "firefox";
|
||||
constexpr char defaultAlpn[] = "h2";
|
||||
|
||||
constexpr char defaultXhttpMode[] = "Stream-one";
|
||||
|
||||
@@ -208,7 +208,7 @@ amnezia::ScriptVars amnezia::genXrayVars(const ContainerConfig &containerConfig)
|
||||
if (auto* xrayProtocolConfig = containerConfig.getXrayProtocolConfig()) {
|
||||
const XrayServerConfig& config = xrayProtocolConfig->serverConfig;
|
||||
|
||||
vars.append({ { "$XRAY_SITE_NAME", config.site.isEmpty() ? protocols::xray::defaultSite : config.site } });
|
||||
vars.append({ { "$XRAY_SITE_NAME", config.site } });
|
||||
vars.append({ { "$XRAY_SERVER_PORT", config.port.isEmpty() ? protocols::xray::defaultPort : config.port } });
|
||||
}
|
||||
|
||||
|
||||
@@ -205,10 +205,10 @@ extension PacketTunnelProvider {
|
||||
}
|
||||
}
|
||||
|
||||
func stopXray(completionHandler: () -> Void) {
|
||||
func stopXray(completionHandler: @escaping () -> Void) {
|
||||
Socks5Tunnel.quit()
|
||||
LibXrayStopXray()
|
||||
completionHandler()
|
||||
hevSocksTunnelGroup.notify(queue: .main, execute: completionHandler)
|
||||
}
|
||||
|
||||
func sockCallback(fd: uintptr_t) {
|
||||
@@ -345,10 +345,13 @@ extension PacketTunnelProvider {
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.global().async {
|
||||
hevSocksTunnelGroup.enter()
|
||||
DispatchQueue.global().async { [hevSocksTunnelGroup] in
|
||||
xrayLog(.info, message: "Hev socks started")
|
||||
completionHandler(nil)
|
||||
Socks5Tunnel.run(withConfig: configurationFilePath)
|
||||
let result = Socks5Tunnel.run(withConfig: configurationFilePath)
|
||||
xrayLog(.info, message: "Hev socks stopped: \(result)")
|
||||
hevSocksTunnelGroup.leave()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
var protoType: TunnelProtoType?
|
||||
|
||||
var activeIfaceIdx: UInt32 = 0
|
||||
let hevSocksTunnelGroup = DispatchGroup()
|
||||
|
||||
func openVPNPacketFlow() -> OpenVPNAdapterPacketFlow {
|
||||
openVPNPacketFlowAdapter
|
||||
@@ -316,10 +317,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
updateActiveInterfaceIndex(for: changePath)
|
||||
reasserting = true
|
||||
xrayLog(.info, message: "Applying network change to xray tunnel")
|
||||
stopXray { }
|
||||
startXray { [weak self] error in
|
||||
self?.reasserting = false
|
||||
completion(error)
|
||||
stopXray { [weak self] in
|
||||
self?.startXray { [weak self] error in
|
||||
self?.reasserting = false
|
||||
completion(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ void TestXrayConfigModel::isValidSni_data()
|
||||
{
|
||||
QTest::addColumn<QString>("sni");
|
||||
QTest::addColumn<bool>("valid");
|
||||
QTest::newRow("empty") << "" << true;
|
||||
QTest::newRow("empty") << "" << false;
|
||||
QTest::newRow("domain") << "www.example.com" << true;
|
||||
QTest::newRow("ipv4") << "203.0.113.10" << true;
|
||||
QTest::newRow("star") << "*" << false;
|
||||
|
||||
@@ -58,6 +58,73 @@ void AwgConfigModel::setPort(const QString &value)
|
||||
emit changed();
|
||||
}
|
||||
|
||||
QString AwgConfigModel::cascadeEndpoint() const
|
||||
{
|
||||
return m_protocolConfig.serverConfig.cascadeEndpoint;
|
||||
}
|
||||
|
||||
void AwgConfigModel::setCascadeEndpoint(const QString &value)
|
||||
{
|
||||
if (m_protocolConfig.serverConfig.cascadeEndpoint == value) {
|
||||
return;
|
||||
}
|
||||
m_protocolConfig.serverConfig.cascadeEndpoint = value;
|
||||
emit changed();
|
||||
}
|
||||
|
||||
QString AwgConfigModel::cascadePort() const
|
||||
{
|
||||
return m_protocolConfig.serverConfig.cascadePort;
|
||||
}
|
||||
|
||||
void AwgConfigModel::setCascadePort(const QString &value)
|
||||
{
|
||||
if (m_protocolConfig.serverConfig.cascadePort == value) {
|
||||
return;
|
||||
}
|
||||
m_protocolConfig.serverConfig.cascadePort = value;
|
||||
emit changed();
|
||||
}
|
||||
|
||||
QString AwgConfigModel::clientHostName() const
|
||||
{
|
||||
return m_protocolConfig.clientConfig->hostName;
|
||||
}
|
||||
|
||||
QString AwgConfigModel::clientPort() const
|
||||
{
|
||||
const int port = m_protocolConfig.clientConfig->port;
|
||||
return port > 0 ? QString::number(port) : QString();
|
||||
}
|
||||
|
||||
QString AwgConfigModel::clientCascadeEndpoint() const
|
||||
{
|
||||
return m_protocolConfig.clientConfig->cascadeEndpoint;
|
||||
}
|
||||
|
||||
void AwgConfigModel::setClientCascadeEndpoint(const QString &value)
|
||||
{
|
||||
if (m_protocolConfig.clientConfig->cascadeEndpoint == value) {
|
||||
return;
|
||||
}
|
||||
m_protocolConfig.clientConfig->setCascade(value, m_protocolConfig.clientConfig->cascadePort);
|
||||
emit changed();
|
||||
}
|
||||
|
||||
QString AwgConfigModel::clientCascadePort() const
|
||||
{
|
||||
return m_protocolConfig.clientConfig->cascadePort;
|
||||
}
|
||||
|
||||
void AwgConfigModel::setClientCascadePort(const QString &value)
|
||||
{
|
||||
if (m_protocolConfig.clientConfig->cascadePort == value) {
|
||||
return;
|
||||
}
|
||||
m_protocolConfig.clientConfig->setCascade(m_protocolConfig.clientConfig->cascadeEndpoint, value);
|
||||
emit changed();
|
||||
}
|
||||
|
||||
QString AwgConfigModel::clientMtu() const
|
||||
{
|
||||
return m_protocolConfig.clientConfig->mtu;
|
||||
@@ -709,8 +776,15 @@ amnezia::AwgProtocolConfig AwgConfigModel::getProtocolConfig()
|
||||
{
|
||||
bool serverSettingsChanged = !m_protocolConfig.serverConfig.hasEqualServerSettings(m_originalProtocolConfig.serverConfig);
|
||||
|
||||
const auto &serverConfig = m_protocolConfig.serverConfig;
|
||||
const auto &originalServerConfig = m_originalProtocolConfig.serverConfig;
|
||||
const bool cascadeChanged = serverConfig.cascadeEndpoint != originalServerConfig.cascadeEndpoint
|
||||
|| serverConfig.cascadePort != originalServerConfig.cascadePort;
|
||||
|
||||
if (serverSettingsChanged) {
|
||||
m_protocolConfig.clearClientConfig();
|
||||
} else if (cascadeChanged) {
|
||||
m_protocolConfig.clientConfig->setCascade(serverConfig.cascadeEndpoint, serverConfig.cascadePort);
|
||||
}
|
||||
|
||||
return m_protocolConfig;
|
||||
|
||||
@@ -14,7 +14,13 @@ class AwgConfigModel : public QObject
|
||||
|
||||
Q_PROPERTY(QString subnetAddress READ subnetAddress WRITE setSubnetAddress NOTIFY changed)
|
||||
Q_PROPERTY(QString port READ port WRITE setPort NOTIFY changed)
|
||||
Q_PROPERTY(QString cascadeEndpoint READ cascadeEndpoint WRITE setCascadeEndpoint NOTIFY changed)
|
||||
Q_PROPERTY(QString cascadePort READ cascadePort WRITE setCascadePort NOTIFY changed)
|
||||
Q_PROPERTY(QString clientMtu READ clientMtu WRITE setClientMtu NOTIFY changed)
|
||||
Q_PROPERTY(QString clientHostName READ clientHostName NOTIFY changed)
|
||||
Q_PROPERTY(QString clientPort READ clientPort NOTIFY changed)
|
||||
Q_PROPERTY(QString clientCascadeEndpoint READ clientCascadeEndpoint WRITE setClientCascadeEndpoint NOTIFY changed)
|
||||
Q_PROPERTY(QString clientCascadePort READ clientCascadePort WRITE setClientCascadePort NOTIFY changed)
|
||||
Q_PROPERTY(QString clientJunkPacketCount READ clientJunkPacketCount WRITE setClientJunkPacketCount NOTIFY changed)
|
||||
Q_PROPERTY(QString clientJunkPacketMinSize READ clientJunkPacketMinSize WRITE setClientJunkPacketMinSize NOTIFY changed)
|
||||
Q_PROPERTY(QString clientJunkPacketMaxSize READ clientJunkPacketMaxSize WRITE setClientJunkPacketMaxSize NOTIFY changed)
|
||||
@@ -69,6 +75,21 @@ public:
|
||||
QString port() const;
|
||||
void setPort(const QString &value);
|
||||
|
||||
QString cascadeEndpoint() const;
|
||||
void setCascadeEndpoint(const QString &value);
|
||||
|
||||
QString cascadePort() const;
|
||||
void setCascadePort(const QString &value);
|
||||
|
||||
QString clientHostName() const;
|
||||
QString clientPort() const;
|
||||
|
||||
QString clientCascadeEndpoint() const;
|
||||
void setClientCascadeEndpoint(const QString &value);
|
||||
|
||||
QString clientCascadePort() const;
|
||||
void setClientCascadePort(const QString &value);
|
||||
|
||||
QString clientMtu() const;
|
||||
void setClientMtu(const QString &value);
|
||||
|
||||
|
||||
@@ -40,6 +40,73 @@ void WireGuardConfigModel::setPort(const QString &value)
|
||||
emit changed();
|
||||
}
|
||||
|
||||
QString WireGuardConfigModel::cascadeEndpoint() const
|
||||
{
|
||||
return m_protocolConfig.serverConfig.cascadeEndpoint;
|
||||
}
|
||||
|
||||
void WireGuardConfigModel::setCascadeEndpoint(const QString &value)
|
||||
{
|
||||
if (m_protocolConfig.serverConfig.cascadeEndpoint == value) {
|
||||
return;
|
||||
}
|
||||
m_protocolConfig.serverConfig.cascadeEndpoint = value;
|
||||
emit changed();
|
||||
}
|
||||
|
||||
QString WireGuardConfigModel::cascadePort() const
|
||||
{
|
||||
return m_protocolConfig.serverConfig.cascadePort;
|
||||
}
|
||||
|
||||
void WireGuardConfigModel::setCascadePort(const QString &value)
|
||||
{
|
||||
if (m_protocolConfig.serverConfig.cascadePort == value) {
|
||||
return;
|
||||
}
|
||||
m_protocolConfig.serverConfig.cascadePort = value;
|
||||
emit changed();
|
||||
}
|
||||
|
||||
QString WireGuardConfigModel::clientHostName() const
|
||||
{
|
||||
return m_protocolConfig.clientConfig->hostName;
|
||||
}
|
||||
|
||||
QString WireGuardConfigModel::clientPort() const
|
||||
{
|
||||
const int port = m_protocolConfig.clientConfig->port;
|
||||
return port > 0 ? QString::number(port) : QString();
|
||||
}
|
||||
|
||||
QString WireGuardConfigModel::clientCascadeEndpoint() const
|
||||
{
|
||||
return m_protocolConfig.clientConfig->cascadeEndpoint;
|
||||
}
|
||||
|
||||
void WireGuardConfigModel::setClientCascadeEndpoint(const QString &value)
|
||||
{
|
||||
if (m_protocolConfig.clientConfig->cascadeEndpoint == value) {
|
||||
return;
|
||||
}
|
||||
m_protocolConfig.clientConfig->setCascade(value, m_protocolConfig.clientConfig->cascadePort);
|
||||
emit changed();
|
||||
}
|
||||
|
||||
QString WireGuardConfigModel::clientCascadePort() const
|
||||
{
|
||||
return m_protocolConfig.clientConfig->cascadePort;
|
||||
}
|
||||
|
||||
void WireGuardConfigModel::setClientCascadePort(const QString &value)
|
||||
{
|
||||
if (m_protocolConfig.clientConfig->cascadePort == value) {
|
||||
return;
|
||||
}
|
||||
m_protocolConfig.clientConfig->setCascade(m_protocolConfig.clientConfig->cascadeEndpoint, value);
|
||||
emit changed();
|
||||
}
|
||||
|
||||
QString WireGuardConfigModel::clientMtu() const
|
||||
{
|
||||
if (m_protocolConfig.clientConfig.has_value()) {
|
||||
@@ -96,8 +163,15 @@ amnezia::WireGuardProtocolConfig WireGuardConfigModel::getProtocolConfig()
|
||||
{
|
||||
bool serverSettingsChanged = !m_protocolConfig.serverConfig.hasEqualServerSettings(m_originalProtocolConfig.serverConfig);
|
||||
|
||||
const auto &serverConfig = m_protocolConfig.serverConfig;
|
||||
const auto &originalServerConfig = m_originalProtocolConfig.serverConfig;
|
||||
const bool cascadeChanged = serverConfig.cascadeEndpoint != originalServerConfig.cascadeEndpoint
|
||||
|| serverConfig.cascadePort != originalServerConfig.cascadePort;
|
||||
|
||||
if (serverSettingsChanged) {
|
||||
m_protocolConfig.clearClientConfig();
|
||||
} else if (cascadeChanged) {
|
||||
m_protocolConfig.clientConfig->setCascade(serverConfig.cascadeEndpoint, serverConfig.cascadePort);
|
||||
}
|
||||
|
||||
return m_protocolConfig;
|
||||
|
||||
@@ -14,7 +14,13 @@ class WireGuardConfigModel : public QObject
|
||||
|
||||
Q_PROPERTY(QString subnetAddress READ subnetAddress WRITE setSubnetAddress NOTIFY changed)
|
||||
Q_PROPERTY(QString port READ port WRITE setPort NOTIFY changed)
|
||||
Q_PROPERTY(QString cascadeEndpoint READ cascadeEndpoint WRITE setCascadeEndpoint NOTIFY changed)
|
||||
Q_PROPERTY(QString cascadePort READ cascadePort WRITE setCascadePort NOTIFY changed)
|
||||
Q_PROPERTY(QString clientMtu READ clientMtu WRITE setClientMtu NOTIFY changed)
|
||||
Q_PROPERTY(QString clientHostName READ clientHostName NOTIFY changed)
|
||||
Q_PROPERTY(QString clientPort READ clientPort NOTIFY changed)
|
||||
Q_PROPERTY(QString clientCascadeEndpoint READ clientCascadeEndpoint WRITE setClientCascadeEndpoint NOTIFY changed)
|
||||
Q_PROPERTY(QString clientCascadePort READ clientCascadePort WRITE setClientCascadePort NOTIFY changed)
|
||||
|
||||
public:
|
||||
explicit WireGuardConfigModel(QObject *parent = nullptr);
|
||||
@@ -25,6 +31,21 @@ public:
|
||||
QString port() const;
|
||||
void setPort(const QString &value);
|
||||
|
||||
QString cascadeEndpoint() const;
|
||||
void setCascadeEndpoint(const QString &value);
|
||||
|
||||
QString cascadePort() const;
|
||||
void setCascadePort(const QString &value);
|
||||
|
||||
QString clientHostName() const;
|
||||
QString clientPort() const;
|
||||
|
||||
QString clientCascadeEndpoint() const;
|
||||
void setClientCascadeEndpoint(const QString &value);
|
||||
|
||||
QString clientCascadePort() const;
|
||||
void setClientCascadePort(const QString &value);
|
||||
|
||||
QString clientMtu() const;
|
||||
void setClientMtu(const QString &value);
|
||||
|
||||
|
||||
@@ -759,7 +759,7 @@ QStringList XrayConfigModel::transportOptions()
|
||||
|
||||
QStringList XrayConfigModel::fingerprintOptions()
|
||||
{
|
||||
return { "hellochrome_120", "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random" };
|
||||
return { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random" };
|
||||
}
|
||||
|
||||
QStringList XrayConfigModel::alpnOptions()
|
||||
@@ -833,11 +833,6 @@ QString XrayConfigModel::portDefault()
|
||||
return QString::fromLatin1(protocols::xray::defaultPort);
|
||||
}
|
||||
|
||||
QString XrayConfigModel::sniDefault()
|
||||
{
|
||||
return QString::fromLatin1(protocols::xray::defaultSni);
|
||||
}
|
||||
|
||||
QString XrayConfigModel::xhttpHostDefault()
|
||||
{
|
||||
return QString::fromLatin1(protocols::xray::defaultXhttpHost);
|
||||
@@ -933,10 +928,7 @@ bool XrayConfigModel::isValidHost(const QString &host)
|
||||
bool XrayConfigModel::isValidSni(const QString &sni)
|
||||
{
|
||||
const QString t = sni.trimmed();
|
||||
if (t.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
if (t.contains(QLatin1Char('*'))) {
|
||||
if (t.isEmpty() || t.contains(QLatin1Char('*'))) {
|
||||
return false;
|
||||
}
|
||||
return isValidSingleHost(t);
|
||||
|
||||
@@ -251,7 +251,6 @@ public:
|
||||
Q_INVOKABLE static QString mkcpDefaultCwndMultiplier();
|
||||
|
||||
Q_INVOKABLE static QString portDefault();
|
||||
Q_INVOKABLE static QString sniDefault();
|
||||
Q_INVOKABLE static QString xhttpHostDefault();
|
||||
Q_INVOKABLE static QString xhttpUplinkChunkSizeDefault();
|
||||
Q_INVOKABLE static QString scMaxEachPostBytesMinDefault();
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import MtProxyConfig 1.0
|
||||
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
property string endpoint
|
||||
property string port
|
||||
property string endpointPlaceholder
|
||||
property string portPlaceholder
|
||||
property bool warningVisible: false
|
||||
property var scroller: null
|
||||
|
||||
signal endpointEdited(string text)
|
||||
signal portEdited(string text)
|
||||
|
||||
spacing: 12
|
||||
|
||||
H2TextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 28
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Cascade connection")
|
||||
}
|
||||
|
||||
NotificationType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: root.warningVisible
|
||||
|
||||
variant: NotificationType.Warning
|
||||
text: qsTr("Cascade endpoint and port are the address and port of the entry server that forwards traffic to this server. New client configs will connect to them instead of this server's address and port. Existing configs are not changed")
|
||||
}
|
||||
|
||||
TextFieldType {
|
||||
id: endpointTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Cascade endpoint")
|
||||
textField.placeholderText: root.endpointPlaceholder
|
||||
textField.text: root.endpoint
|
||||
textField.maximumLength: 253
|
||||
textField.validator: PublicHostInputValidator {}
|
||||
|
||||
textField.onTextEdited: root.endpointEdited(textField.text.trim())
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus && root.scroller) {
|
||||
root.scroller.scrollToItem(endpointTextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldType {
|
||||
id: portTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Cascade port")
|
||||
textField.placeholderText: root.portPlaceholder
|
||||
textField.text: root.port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onTextEdited: root.portEdited(textField.text)
|
||||
|
||||
textField.onActiveFocusChanged: {
|
||||
if (textField.activeFocus && root.scroller) {
|
||||
root.scroller.scrollToItem(portTextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,6 +118,7 @@ Item {
|
||||
|
||||
enabled: d.scrolled > 0
|
||||
acceptedButtons: Qt.AllButtons
|
||||
hoverEnabled: true
|
||||
}
|
||||
|
||||
IconButtonType {
|
||||
|
||||
@@ -15,16 +15,6 @@ TabButton {
|
||||
readonly property int maximumHorizontalPadding: root.size === TabButtonType.Large ? 24 : 10
|
||||
|
||||
implicitWidth: implicitContentWidth + 2 * root.maximumHorizontalPadding
|
||||
width: {
|
||||
if (!root.visible) {
|
||||
return 0
|
||||
}
|
||||
const tabBar = root.TabBar.tabBar
|
||||
if (!tabBar || tabBar.visibleTabCount === undefined) {
|
||||
return implicitWidth
|
||||
}
|
||||
return tabBar.availableWidth / Math.max(1, tabBar.visibleTabCount)
|
||||
}
|
||||
implicitHeight: root.size === TabButtonType.Large ? 48 : 36
|
||||
|
||||
horizontalPadding: Math.max(4, Math.min(root.maximumHorizontalPadding, (root.width - implicitContentWidth) / 2))
|
||||
|
||||
@@ -4,17 +4,6 @@ import QtQuick.Controls
|
||||
TabBar {
|
||||
id: root
|
||||
|
||||
readonly property int visibleTabCount: {
|
||||
let visibleCount = 0
|
||||
for (let i = 0; i < root.count; ++i) {
|
||||
const tab = root.itemAt(i)
|
||||
if (tab && tab.visible) {
|
||||
++visibleCount
|
||||
}
|
||||
}
|
||||
return visibleCount
|
||||
}
|
||||
|
||||
spacing: 0
|
||||
background: null
|
||||
}
|
||||
@@ -271,6 +271,20 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
CascadeSettings {
|
||||
Layout.fillWidth: true
|
||||
|
||||
scroller: smartScroll
|
||||
|
||||
endpoint: AwgConfigModel.clientCascadeEndpoint
|
||||
port: AwgConfigModel.clientCascadePort
|
||||
endpointPlaceholder: AwgConfigModel.clientHostName
|
||||
portPlaceholder: AwgConfigModel.clientPort
|
||||
|
||||
onEndpointEdited: (text) => { AwgConfigModel.clientCascadeEndpoint = text }
|
||||
onPortEdited: (text) => { AwgConfigModel.clientCascadePort = text }
|
||||
}
|
||||
|
||||
H2TextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 28
|
||||
|
||||
@@ -464,6 +464,23 @@ PageType {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CascadeSettings {
|
||||
Layout.fillWidth: true
|
||||
|
||||
enabled: root.isEnabled
|
||||
|
||||
warningVisible: true
|
||||
scroller: smartScroll
|
||||
|
||||
endpoint: AwgConfigModel.cascadeEndpoint
|
||||
port: AwgConfigModel.cascadePort
|
||||
endpointPlaceholder: ServersUiController.serverHostName(ServersUiController.processedServerId)
|
||||
portPlaceholder: AwgConfigModel.port
|
||||
|
||||
onEndpointEdited: (text) => { AwgConfigModel.cascadeEndpoint = text }
|
||||
onPortEdited: (text) => { AwgConfigModel.cascadePort = text }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,18 @@ PageType {
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
CascadeSettings {
|
||||
Layout.fillWidth: true
|
||||
|
||||
endpoint: WireGuardConfigModel.clientCascadeEndpoint
|
||||
port: WireGuardConfigModel.clientCascadePort
|
||||
endpointPlaceholder: WireGuardConfigModel.clientHostName
|
||||
portPlaceholder: WireGuardConfigModel.clientPort
|
||||
|
||||
onEndpointEdited: (text) => { WireGuardConfigModel.clientCascadeEndpoint = text }
|
||||
onPortEdited: (text) => { WireGuardConfigModel.clientCascadePort = text }
|
||||
}
|
||||
|
||||
H2TextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
|
||||
@@ -77,6 +77,20 @@ PageType {
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
CascadeSettings {
|
||||
Layout.fillWidth: true
|
||||
|
||||
warningVisible: true
|
||||
|
||||
endpoint: WireGuardConfigModel.cascadeEndpoint
|
||||
port: WireGuardConfigModel.cascadePort
|
||||
endpointPlaceholder: ServersUiController.serverHostName(ServersUiController.processedServerId)
|
||||
portPlaceholder: WireGuardConfigModel.port
|
||||
|
||||
onEndpointEdited: (text) => { WireGuardConfigModel.cascadeEndpoint = text }
|
||||
onPortEdited: (text) => { WireGuardConfigModel.cascadePort = text }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -159,7 +159,6 @@ PageType {
|
||||
id: sniFieldTls
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("Server Name (SNI)")
|
||||
placeholderText: XrayConfigModel.sniDefault()
|
||||
textField.text: XrayConfigModel.sni
|
||||
textField.validator: RegularExpressionValidator { regularExpression: /^[A-Za-z0-9._-]*$/ }
|
||||
textField.onTextEdited: XrayConfigModel.sni = textField.text
|
||||
@@ -216,7 +215,6 @@ PageType {
|
||||
id: sniFieldReality
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("Server Name (SNI)")
|
||||
placeholderText: XrayConfigModel.sniDefault()
|
||||
textField.text: XrayConfigModel.sni
|
||||
textField.validator: RegularExpressionValidator { regularExpression: /^[A-Za-z0-9._-]*$/ }
|
||||
textField.onTextEdited: XrayConfigModel.sni = textField.text
|
||||
|
||||
@@ -21,13 +21,30 @@ PageType {
|
||||
readonly property int pageSettingsServerServices: 1
|
||||
readonly property int pageSettingsServerData: 2
|
||||
|
||||
readonly property var tabTitles: [qsTr("Protocols"), qsTr("Services"), qsTr("Management")]
|
||||
readonly property var availableTabs: {
|
||||
const tabs = []
|
||||
if (protocolsPage.installedProtocolsCount > 0) {
|
||||
tabs.push(root.pageSettingsServerProtocols)
|
||||
}
|
||||
if (servicesPage.installedServicesCount > 0) {
|
||||
tabs.push(root.pageSettingsServerServices)
|
||||
}
|
||||
tabs.push(root.pageSettingsServerData)
|
||||
return tabs
|
||||
}
|
||||
|
||||
property int currentTab: (ServersUiController.isServerFromApi(ServersUiController.processedServerId)
|
||||
&& !ServersUiController.serverHasInstalledContainers(ServersUiController.processedServerId)) ?
|
||||
root.pageSettingsServerData : root.pageSettingsServerProtocols
|
||||
|
||||
property var processedServer
|
||||
|
||||
Connections {
|
||||
target: PageController
|
||||
|
||||
function onGoToPageSettingsServerServices() {
|
||||
tabBar.setCurrentIndex(root.pageSettingsServerServices)
|
||||
root.currentTab = root.pageSettingsServerServices
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,25 +128,18 @@ PageType {
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
currentIndex: (ServersUiController.isServerFromApi(ServersUiController.processedServerId)
|
||||
&& !ServersUiController.serverHasInstalledContainers(ServersUiController.processedServerId)) ?
|
||||
root.pageSettingsServerData : root.pageSettingsServerProtocols
|
||||
currentIndex: root.availableTabs.indexOf(root.currentTab)
|
||||
|
||||
TabButtonType {
|
||||
id: protocolsTab
|
||||
visible: protocolsPage.installedProtocolsCount > 0
|
||||
text: qsTr("Protocols")
|
||||
}
|
||||
Repeater {
|
||||
model: root.availableTabs
|
||||
|
||||
TabButtonType {
|
||||
id: servicesTab
|
||||
visible: servicesPage.installedServicesCount > 0
|
||||
text: qsTr("Services")
|
||||
}
|
||||
TabButtonType {
|
||||
required property int modelData
|
||||
|
||||
TabButtonType {
|
||||
id: dataTab
|
||||
text: qsTr("Management")
|
||||
text: root.tabTitles[modelData]
|
||||
|
||||
onClicked: root.currentTab = modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +150,7 @@ PageType {
|
||||
Layout.fillHeight: true
|
||||
Layout.topMargin: 24
|
||||
|
||||
currentIndex: tabBar.currentIndex
|
||||
currentIndex: root.currentTab
|
||||
|
||||
PageSettingsServerProtocols {
|
||||
id: protocolsPage
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/ui/qml">
|
||||
<file>Components/CopyableValueRow.qml</file>
|
||||
<file>Components/CascadeSettings.qml</file>
|
||||
<file>Components/ConfigContentSheet.qml</file>
|
||||
<file>Components/QrCodeCard.qml</file>
|
||||
<file>Components/ShareClientInfoSheet.qml</file>
|
||||
|
||||
@@ -35,6 +35,7 @@ while [[ $# -gt 0 ]]; do
|
||||
--aab) : ${BUILD_AAB=true}; shift ;;
|
||||
--apk) : ${BUILD_APK=true}; shift ;;
|
||||
--play) : ${BUILD_PLAY=true}; shift ;;
|
||||
--configure-only) : ${CONFIGURE_ONLY=true}; shift ;;
|
||||
--help|-h|?)
|
||||
echo "Usage: $0 [options]"
|
||||
echo " Options:"
|
||||
@@ -49,6 +50,7 @@ while [[ $# -gt 0 ]]; do
|
||||
echo " --aab - whether to build AAB. only applicable to Android"
|
||||
echo " --apk - whether to build APK. use with --play. only applicable to Android"
|
||||
echo " --play - build Play flavor (Google Play Billing). use with --aab or --apk. only applicable to Android"
|
||||
echo " --configure-only - run CMake configure and skip the build"
|
||||
exit 0
|
||||
;;
|
||||
*) echo "Unknown arg \"$1\". Use $0 -h to get help"; exit 1 ;;
|
||||
@@ -217,6 +219,7 @@ if [[ -n "$FORCE" ]]; then
|
||||
fi
|
||||
|
||||
run_traced cmake -S "$SOURCE_PATH" -B "$BUILD_PATH" "${args[@]}"
|
||||
[[ -n "$CONFIGURE_ONLY" ]] && exit 0
|
||||
run_traced cmake --build "$BUILD_PATH" --config "$CMAKE_BUILD_TYPE" --parallel "$JOBS"
|
||||
|
||||
if [[ -n "$BUILD_AAB" ]]; then
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
def archive_build(app_platform)
|
||||
build_app(
|
||||
project: "build/AmneziaVPN.xcodeproj",
|
||||
scheme: "AmneziaVPN",
|
||||
configuration: "Release",
|
||||
export_method: "app-store",
|
||||
output_directory: "build/#{app_platform}",
|
||||
output_name: "AmneziaVPN"
|
||||
)
|
||||
end
|
||||
|
||||
def authorize_app_store_connect
|
||||
key = ENV.fetch("APPSTORE_CONNECT_PRIVATE_KEY")
|
||||
app_store_connect_api_key(
|
||||
key_id: ENV.fetch("APPSTORE_CONNECT_KEY_ID"),
|
||||
issuer_id: ENV.fetch("APPSTORE_CONNECT_ISSUER_ID"),
|
||||
key_content: key,
|
||||
is_key_content_base64: !key.include?("PRIVATE KEY")
|
||||
)
|
||||
end
|
||||
|
||||
platform :ios do
|
||||
lane :archive do
|
||||
archive_build("ios")
|
||||
end
|
||||
|
||||
lane :testflight_upload do
|
||||
authorize_app_store_connect
|
||||
upload_to_testflight(ipa: "build/ios/AmneziaVPN.ipa", skip_waiting_for_build_processing: true)
|
||||
end
|
||||
end
|
||||
|
||||
platform :mac do
|
||||
lane :archive do
|
||||
archive_build("osx")
|
||||
end
|
||||
|
||||
lane :testflight_upload do
|
||||
authorize_app_store_connect
|
||||
upload_to_testflight(app_platform: "osx", pkg: "build/osx/AmneziaVPN.pkg", skip_waiting_for_build_processing: true)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
s/\(_ENDPOINT\\\{0,1\}=\\\{0,1\}"\)[^"]*/\1***/g
|
||||
/-Xcc -DPROD_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }
|
||||
/-Xcc -DPROD_AGW_PUBLIC_KEY/d
|
||||
/-Xcc -DDEV_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }
|
||||
/-Xcc -DDEV_AGW_PUBLIC_KEY/d
|
||||
/-DPROD_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }
|
||||
/-DPROD_AGW_PUBLIC_KEY/d
|
||||
/-DDEV_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }
|
||||
/-DDEV_AGW_PUBLIC_KEY/d
|
||||
@@ -9,7 +9,7 @@ import os
|
||||
|
||||
class AwgApple(ConanFile):
|
||||
name = "awg-apple"
|
||||
version = "3.1.4"
|
||||
version = "3.1.5"
|
||||
settings = "os", "arch", "compiler"
|
||||
|
||||
@property
|
||||
@@ -45,7 +45,7 @@ class AwgApple(ConanFile):
|
||||
|
||||
def source(self):
|
||||
get(self, f"https://github.com/amnezia-vpn/amneziawg-apple/archive/refs/tags/v{self.version}.zip",
|
||||
sha256="09d7b760d18232fdf121ed2286b2f171b501dc31137e5e7d557c1ee3a99ef772", strip_root=True
|
||||
sha256="b3bd3680fa966dae67162c261e1b0a42235a0bd706ec6ea67cc04e707ef3b7a8", strip_root=True
|
||||
)
|
||||
|
||||
def generate(self):
|
||||
|
||||
Reference in new issue
Block a user