Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d798344d2 | |||
| dec1f51fd6 | |||
| 832fea3d50 | |||
| 43b51db132 | |||
| d391abb0cc | |||
| 153e33d5df | |||
| 1cb171698a | |||
| f02128796a | |||
| 71705f435b | |||
| f821bcb01f | |||
| 9a5e437f7b | |||
| 3d2105a439 | |||
| 16d92ddb7c | |||
| e9d4fd8482 | |||
| 9fdcf5ab13 | |||
| a6e6de33c8 | |||
| 53c7fd4d81 | |||
| 2608ea4367 | |||
| d20ed4ad01 | |||
| eae2936449 | |||
| da8ad1f6ba | |||
| 5472347969 | |||
| a43f7a6926 | |||
| 47f917de0b | |||
| dbeb7edd7a | |||
| 6cede712f5 | |||
| d328739192 | |||
| d15c0bd962 | |||
| d53c794936 | |||
| e5dcb25a4a | |||
| f9002b4f43 | |||
| 0531508a75 | |||
| 174e85a20a | |||
| e9abb6f1e2 | |||
| 5be44f9596 | |||
| 90efaaff92 | |||
| 99b554e7c3 | |||
| ac0ce8a6f6 | |||
| 9f9da885b7 | |||
| f51fd2bf3e | |||
| c8378fd32d | |||
| d767214f10 | |||
| e027c504ae | |||
| 669a95d975 | |||
| a96df5d518 | |||
| c5c81735a0 | |||
| c933745707 | |||
| 6710fd18b3 | |||
| 1b78a71529 |
@@ -255,6 +255,20 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
# Keep compat with MacOS 10.15 aka Catalina by Qt 6.4
|
# Keep compat with MacOS 10.15 aka Catalina by Qt 6.4
|
||||||
QT_VERSION: 6.4.3
|
QT_VERSION: 6.4.3
|
||||||
|
|
||||||
|
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
|
||||||
|
|
||||||
|
MAC_APP_CERT_CERT: ${{ secrets.MAC_APP_CERT_CERT }}
|
||||||
|
MAC_SIGNER_ID: ${{ secrets.MAC_SIGNER_ID }}
|
||||||
|
MAC_APP_CERT_PW: ${{ secrets.MAC_APP_CERT_PW }}
|
||||||
|
|
||||||
|
MAC_INSTALLER_SIGNER_CERT: ${{ secrets.MAC_INSTALLER_SIGNER_CERT }}
|
||||||
|
MAC_INSTALLER_SIGNER_ID: ${{ secrets.MAC_INSTALLER_SIGNER_ID }}
|
||||||
|
MAC_INSTALL_CERT_PW: ${{ secrets.MAC_INSTALL_CERT_PW }}
|
||||||
|
|
||||||
|
APPLE_DEV_EMAIL: ${{ secrets.APPLE_DEV_EMAIL }}
|
||||||
|
APPLE_DEV_PASSWORD: ${{ secrets.APPLE_DEV_PASSWORD }}
|
||||||
|
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
@@ -295,7 +309,7 @@ jobs:
|
|||||||
- name: 'Build project'
|
- name: 'Build project'
|
||||||
run: |
|
run: |
|
||||||
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
||||||
bash deploy/build_macos.sh
|
bash deploy/build_macos.sh -n
|
||||||
|
|
||||||
- name: 'Upload installer artifact'
|
- name: 'Upload installer artifact'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@@ -318,6 +332,20 @@ jobs:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
QT_VERSION: 6.8.0
|
QT_VERSION: 6.8.0
|
||||||
|
|
||||||
|
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
|
||||||
|
|
||||||
|
MAC_APP_CERT_CERT: ${{ secrets.MAC_APP_CERT_CERT }}
|
||||||
|
MAC_SIGNER_ID: ${{ secrets.MAC_SIGNER_ID }}
|
||||||
|
MAC_APP_CERT_PW: ${{ secrets.MAC_APP_CERT_PW }}
|
||||||
|
|
||||||
|
MAC_INSTALLER_SIGNER_CERT: ${{ secrets.MAC_INSTALLER_SIGNER_CERT }}
|
||||||
|
MAC_INSTALLER_SIGNER_ID: ${{ secrets.MAC_INSTALLER_SIGNER_ID }}
|
||||||
|
MAC_INSTALL_CERT_PW: ${{ secrets.MAC_INSTALL_CERT_PW }}
|
||||||
|
|
||||||
|
APPLE_DEV_EMAIL: ${{ secrets.APPLE_DEV_EMAIL }}
|
||||||
|
APPLE_DEV_PASSWORD: ${{ secrets.APPLE_DEV_PASSWORD }}
|
||||||
|
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
@@ -330,7 +358,7 @@ jobs:
|
|||||||
- name: 'Setup xcode'
|
- name: 'Setup xcode'
|
||||||
uses: maxim-lobanov/setup-xcode@v1
|
uses: maxim-lobanov/setup-xcode@v1
|
||||||
with:
|
with:
|
||||||
xcode-version: '15.4.0'
|
xcode-version: '16.2.0'
|
||||||
|
|
||||||
- name: 'Install Qt'
|
- name: 'Install Qt'
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v3
|
||||||
@@ -358,7 +386,7 @@ jobs:
|
|||||||
- name: 'Build project'
|
- name: 'Build project'
|
||||||
run: |
|
run: |
|
||||||
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
||||||
bash deploy/build_macos.sh
|
bash deploy/build_macos.sh -n
|
||||||
|
|
||||||
- name: 'Upload installer artifact'
|
- name: 'Upload installer artifact'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@@ -374,6 +402,67 @@ jobs:
|
|||||||
path: deploy/build/client/AmneziaVPN.app
|
path: deploy/build/client/AmneziaVPN.app
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
|
Build-MacOS-NE:
|
||||||
|
runs-on: macos-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
QT_VERSION: 6.8.3
|
||||||
|
|
||||||
|
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
|
||||||
|
|
||||||
|
MAC_APP_CERT_CERT: ${{ secrets.MAC_APP_CERT_CERT }}
|
||||||
|
MAC_SIGNER_ID: ${{ secrets.MAC_SIGNER_ID }}
|
||||||
|
MAC_APP_CERT_PW: ${{ secrets.MAC_APP_CERT_PW }}
|
||||||
|
|
||||||
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
|
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||||
|
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||||
|
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||||
|
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||||
|
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||||
|
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: 'Setup xcode'
|
||||||
|
uses: maxim-lobanov/setup-xcode@v1
|
||||||
|
with:
|
||||||
|
xcode-version: '16.2.0'
|
||||||
|
|
||||||
|
- name: 'Install Qt'
|
||||||
|
uses: jurplel/install-qt-action@v3
|
||||||
|
with:
|
||||||
|
version: ${{ env.QT_VERSION }}
|
||||||
|
host: 'mac'
|
||||||
|
target: 'desktop'
|
||||||
|
arch: 'clang_64'
|
||||||
|
modules: 'qtremoteobjects qt5compat qtshadertools'
|
||||||
|
dir: ${{ runner.temp }}
|
||||||
|
setup-python: 'true'
|
||||||
|
set-env: 'true'
|
||||||
|
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||||
|
|
||||||
|
|
||||||
|
- name: 'Get sources'
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: 'true'
|
||||||
|
fetch-depth: 10
|
||||||
|
|
||||||
|
- name: 'Setup ccache'
|
||||||
|
uses: hendrikmuhs/ccache-action@v1.2
|
||||||
|
|
||||||
|
- name: 'Build project'
|
||||||
|
run: |
|
||||||
|
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
||||||
|
bash deploy/build_macos_ne.sh
|
||||||
|
|
||||||
|
- name: 'Upload unpacked artifact'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: AmneziaVPN_MacOS_unpacked
|
||||||
|
path: deploy/build/client/AmneziaVPN.app
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
Build-Android:
|
Build-Android:
|
||||||
|
|||||||
@@ -138,3 +138,4 @@ CMakeFiles/
|
|||||||
ios-ne-build.sh
|
ios-ne-build.sh
|
||||||
macos-ne-build.sh
|
macos-ne-build.sh
|
||||||
macos-signed-build.sh
|
macos-signed-build.sh
|
||||||
|
macos-with-sign-build.sh
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||||
|
|
||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
|
set(AMNEZIAVPN_VERSION 4.8.9.2)
|
||||||
|
|
||||||
project(${PROJECT} VERSION 4.8.8.1
|
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
|
||||||
DESCRIPTION "AmneziaVPN"
|
DESCRIPTION "AmneziaVPN"
|
||||||
HOMEPAGE_URL "https://amnezia.org/"
|
HOMEPAGE_URL "https://amnezia.org/"
|
||||||
)
|
)
|
||||||
@@ -11,7 +12,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
|||||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||||
|
|
||||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||||
set(APP_ANDROID_VERSION_CODE 2087)
|
set(APP_ANDROID_VERSION_CODE 2092)
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(MZ_PLATFORM_NAME "linux")
|
set(MZ_PLATFORM_NAME "linux")
|
||||||
@@ -31,13 +32,19 @@ set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
|
|||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
if(APPLE AND NOT IOS)
|
if(APPLE)
|
||||||
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
if(IOS)
|
||||||
|
set(CMAKE_OSX_ARCHITECTURES "arm64")
|
||||||
|
elseif(MACOS_NE)
|
||||||
|
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64")
|
||||||
|
else()
|
||||||
|
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(client)
|
add_subdirectory(client)
|
||||||
|
|
||||||
if(NOT IOS AND NOT ANDROID)
|
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
||||||
add_subdirectory(service)
|
add_subdirectory(service)
|
||||||
|
|
||||||
include(${CMAKE_SOURCE_DIR}/deploy/installer/config.cmake)
|
include(${CMAKE_SOURCE_DIR}/deploy/installer/config.cmake)
|
||||||
|
|||||||
@@ -9,17 +9,17 @@
|
|||||||
### [English]([https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md](https://github.com/amnezia-vpn/amnezia-client/tree/dev?tab=readme-ov-file#)) | [Русский](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md)
|
### [English]([https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md](https://github.com/amnezia-vpn/amnezia-client/tree/dev?tab=readme-ov-file#)) | [Русский](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md)
|
||||||
|
|
||||||
|
|
||||||
[Amnezia](https://amnezia.org) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
|
[Amnezia](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
|
||||||
|
|
||||||
[](https://amnezia.org)
|
[](https://amnezia.org)
|
||||||
|
|
||||||
### [Website](https://amnezia.org) | [Alt website link](https://storage.googleapis.com/amnezia/amnezia.org) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting)
|
### [Website](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) | [Alt website link](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en-mirror) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting)
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> If the [Amnezia website](https://amnezia.org) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/amnezia/amnezia.org ).
|
> If the [Amnezia website](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en-mirror).
|
||||||
|
|
||||||
<a href="https://amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
|
<a href="https://amnezia.org/en/downloads?utm_source=github&utm_campaign=amnezia_button-readme-en"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
|
||||||
<a href="https://storage.googleapis.com/amnezia/q9p19109"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a>
|
<a href="https://storage.googleapis.com/amnezia/amnezia.org?m-path=/en/downloads&utm_source=github&utm_campaign=amnezia_button-readme-en-mirrow"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a>
|
||||||
|
|
||||||
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
|
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
|
||||||
|
|
||||||
|
|||||||
@@ -6,16 +6,16 @@
|
|||||||
[](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
|
[](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
|
||||||
|
|
||||||
### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
|
### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
|
||||||
[AmneziaVPN](https://amnezia.org) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
|
[AmneziaVPN](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
|
||||||
|
|
||||||
[](https://amnezia.org)
|
[](https://amnezia.org)
|
||||||
|
|
||||||
### [Сайт](https://amnezia.org) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
|
### [Сайт](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru-mirror) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org).
|
> Если [сайт Amnezia](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru-mirror).
|
||||||
|
|
||||||
<a href="https://storage.googleapis.com/amnezia/q9p19109"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website-ru.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
|
<a href="https://storage.googleapis.com/amnezia/amnezia.org?m-path=/ru/downloads&utm_source=github&utm_campaign=amnezia_button-readme-ru-mirror"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website-ru.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
|
||||||
|
|
||||||
|
|
||||||
[Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases)
|
[Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
|||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
project(${PROJECT})
|
project(${PROJECT})
|
||||||
|
|
||||||
|
|
||||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||||
set_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER "Autogen")
|
set_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER "Autogen")
|
||||||
set_property(GLOBAL PROPERTY AUTOMOC_TARGETS_FOLDER "Autogen")
|
set_property(GLOBAL PROPERTY AUTOMOC_TARGETS_FOLDER "Autogen")
|
||||||
@@ -53,6 +52,9 @@ endif()
|
|||||||
|
|
||||||
qt_standard_project_setup()
|
qt_standard_project_setup()
|
||||||
qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
|
qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
|
||||||
|
target_include_directories(${PROJECT} PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
|
||||||
|
)
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
|
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
|
||||||
@@ -110,6 +112,15 @@ include_directories(
|
|||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(MACOS_NE)
|
||||||
|
message("MACOS_NE is ON")
|
||||||
|
add_definitions(-DQ_OS_MAC)
|
||||||
|
add_definitions(-DMACOS_NE)
|
||||||
|
message("Add macros for MacOS Network Extension")
|
||||||
|
else()
|
||||||
|
message("MACOS_NE is OFF")
|
||||||
|
endif()
|
||||||
|
|
||||||
include_directories(mozilla)
|
include_directories(mozilla)
|
||||||
include_directories(mozilla/shared)
|
include_directories(mozilla/shared)
|
||||||
include_directories(mozilla/models)
|
include_directories(mozilla/models)
|
||||||
@@ -139,7 +150,7 @@ if(WIN32)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
cmake_policy(SET CMP0099 OLD)
|
cmake_policy(SET CMP0099 NEW)
|
||||||
cmake_policy(SET CMP0114 NEW)
|
cmake_policy(SET CMP0114 NEW)
|
||||||
|
|
||||||
if(NOT BUILD_OSX_APP_IDENTIFIER)
|
if(NOT BUILD_OSX_APP_IDENTIFIER)
|
||||||
@@ -158,7 +169,6 @@ if(APPLE)
|
|||||||
set(CMAKE_XCODE_GENERATE_SCHEME FALSE)
|
set(CMAKE_XCODE_GENERATE_SCHEME FALSE)
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${BUILD_VPN_DEVELOPMENT_TEAM})
|
set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${BUILD_VPN_DEVELOPMENT_TEAM})
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_GROUP_ID_IOS ${BUILD_IOS_GROUP_IDENTIFIER})
|
set(CMAKE_XCODE_ATTRIBUTE_GROUP_ID_IOS ${BUILD_IOS_GROUP_IDENTIFIER})
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(LINUX AND NOT ANDROID)
|
if(LINUX AND NOT ANDROID)
|
||||||
@@ -166,8 +176,7 @@ if(LINUX AND NOT ANDROID)
|
|||||||
link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/linux)
|
link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/linux)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
||||||
message("Client desktop build")
|
|
||||||
add_compile_definitions(AMNEZIA_DESKTOP)
|
add_compile_definitions(AMNEZIA_DESKTOP)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -178,7 +187,9 @@ endif()
|
|||||||
if(IOS)
|
if(IOS)
|
||||||
include(cmake/ios.cmake)
|
include(cmake/ios.cmake)
|
||||||
include(cmake/ios-arch-fixup.cmake)
|
include(cmake/ios-arch-fixup.cmake)
|
||||||
elseif(APPLE AND NOT IOS)
|
elseif(APPLE AND MACOS_NE)
|
||||||
|
include(cmake/macos_ne.cmake)
|
||||||
|
elseif(APPLE)
|
||||||
include(cmake/osxtools.cmake)
|
include(cmake/osxtools.cmake)
|
||||||
include(cmake/macos.cmake)
|
include(cmake/macos.cmake)
|
||||||
endif()
|
endif()
|
||||||
@@ -199,7 +210,7 @@ elseif(APPLE AND NOT IOS)
|
|||||||
set(DEPLOY_PLATFORM_PATH "macos")
|
set(DEPLOY_PLATFORM_PATH "macos")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT IOS AND NOT ANDROID)
|
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET ${PROJECT} POST_BUILD
|
TARGET ${PROJECT} POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E $<IF:$<CONFIG:Debug>,copy_directory,true>
|
COMMAND ${CMAKE_COMMAND} -E $<IF:$<CONFIG:Debug>,copy_directory,true>
|
||||||
@@ -214,7 +225,6 @@ if(NOT IOS AND NOT ANDROID)
|
|||||||
$<TARGET_FILE_DIR:${PROJECT}>
|
$<TARGET_FILE_DIR:${PROJECT}>
|
||||||
COMMAND_EXPAND_LISTS
|
COMMAND_EXPAND_LISTS
|
||||||
)
|
)
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
|
#include <QEvent>
|
||||||
|
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "ui/controllers/pageController.h"
|
#include "ui/controllers/pageController.h"
|
||||||
@@ -21,6 +22,8 @@
|
|||||||
#include "platforms/ios/QRCodeReaderBase.h"
|
#include "platforms/ios/QRCodeReaderBase.h"
|
||||||
|
|
||||||
#include "protocols/qml_register_protocols.h"
|
#include "protocols/qml_register_protocols.h"
|
||||||
|
#include <QtQuick/QQuickWindow> // for QQuickWindow
|
||||||
|
#include <QWindow> // for qobject_cast<QWindow*>
|
||||||
|
|
||||||
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
|
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
|
||||||
{
|
{
|
||||||
@@ -48,8 +51,17 @@ AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_C
|
|||||||
|
|
||||||
AmneziaApplication::~AmneziaApplication()
|
AmneziaApplication::~AmneziaApplication()
|
||||||
{
|
{
|
||||||
|
if (m_vpnConnection) {
|
||||||
|
QMetaObject::invokeMethod(m_vpnConnection.get(), "disconnectFromVpn", Qt::QueuedConnection);
|
||||||
|
QMetaObject::invokeMethod(m_vpnConnection.get(), "deleteLater", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
m_vpnConnectionThread.quit();
|
m_vpnConnectionThread.quit();
|
||||||
m_vpnConnectionThread.wait(3000);
|
|
||||||
|
if (!m_vpnConnectionThread.wait(5000)) {
|
||||||
|
m_vpnConnectionThread.terminate();
|
||||||
|
m_vpnConnectionThread.wait();
|
||||||
|
}
|
||||||
|
|
||||||
if (m_engine) {
|
if (m_engine) {
|
||||||
QObject::disconnect(m_engine, 0, 0, 0);
|
QObject::disconnect(m_engine, 0, 0, 0);
|
||||||
@@ -63,15 +75,28 @@ void AmneziaApplication::init()
|
|||||||
|
|
||||||
const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml"));
|
const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml"));
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
m_engine, &QQmlApplicationEngine::objectCreated, this,
|
m_engine, &QQmlApplicationEngine::objectCreated, this,
|
||||||
[url](QObject *obj, const QUrl &objUrl) {
|
[this, url](QObject *obj, const QUrl &objUrl) {
|
||||||
if (!obj && url == objUrl)
|
if (!obj && url == objUrl) {
|
||||||
QCoreApplication::exit(-1);
|
QCoreApplication::exit(-1);
|
||||||
},
|
return;
|
||||||
Qt::QueuedConnection);
|
}
|
||||||
|
// install filter on main window
|
||||||
|
if (auto win = qobject_cast<QQuickWindow*>(obj)) {
|
||||||
|
win->installEventFilter(this);
|
||||||
|
win->show();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
|
||||||
m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance());
|
m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance());
|
||||||
|
|
||||||
|
#ifdef MACOS_NE
|
||||||
|
m_engine->rootContext()->setContextProperty("IsMacOsNeBuild", true);
|
||||||
|
#else
|
||||||
|
m_engine->rootContext()->setContextProperty("IsMacOsNeBuild", false);
|
||||||
|
#endif
|
||||||
|
|
||||||
m_vpnConnection.reset(new VpnConnection(m_settings));
|
m_vpnConnection.reset(new VpnConnection(m_settings));
|
||||||
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
|
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
|
||||||
m_vpnConnectionThread.start();
|
m_vpnConnectionThread.start();
|
||||||
@@ -167,7 +192,7 @@ bool AmneziaApplication::parseCommands()
|
|||||||
|
|
||||||
QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" };
|
QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" };
|
||||||
m_parser.addOption(c_cleanup);
|
m_parser.addOption(c_cleanup);
|
||||||
|
|
||||||
m_parser.process(*this);
|
m_parser.process(*this);
|
||||||
|
|
||||||
if (m_parser.isSet(c_cleanup)) {
|
if (m_parser.isSet(c_cleanup)) {
|
||||||
@@ -179,9 +204,8 @@ bool AmneziaApplication::parseCommands()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
void AmneziaApplication::startLocalServer()
|
void AmneziaApplication::startLocalServer() {
|
||||||
{
|
|
||||||
const QString serverName("AmneziaVPNInstance");
|
const QString serverName("AmneziaVPNInstance");
|
||||||
QLocalServer::removeServer(serverName);
|
QLocalServer::removeServer(serverName);
|
||||||
|
|
||||||
@@ -198,6 +222,22 @@ void AmneziaApplication::startLocalServer()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool AmneziaApplication::eventFilter(QObject *watched, QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::Close) {
|
||||||
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||||
|
quit();
|
||||||
|
#else
|
||||||
|
if (m_coreController && m_coreController->pageController()) {
|
||||||
|
m_coreController->pageController()->hideMainWindow();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return true; // eat the close
|
||||||
|
}
|
||||||
|
// call base QObject::eventFilter
|
||||||
|
return QObject::eventFilter(watched, event);
|
||||||
|
}
|
||||||
|
|
||||||
QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
|
QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
|
||||||
{
|
{
|
||||||
return m_engine;
|
return m_engine;
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
#include <QQmlContext>
|
#include <QQmlContext>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#else
|
#else
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#endif
|
#endif
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
|
|
||||||
@@ -20,9 +20,9 @@
|
|||||||
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
|
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||||
#define AMNEZIA_BASE_CLASS QGuiApplication
|
#define AMNEZIA_BASE_CLASS QGuiApplication
|
||||||
#else
|
#else
|
||||||
#define AMNEZIA_BASE_CLASS QApplication
|
#define AMNEZIA_BASE_CLASS QApplication
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class AmneziaApplication : public AMNEZIA_BASE_CLASS
|
class AmneziaApplication : public AMNEZIA_BASE_CLASS
|
||||||
@@ -37,7 +37,7 @@ public:
|
|||||||
void loadFonts();
|
void loadFonts();
|
||||||
bool parseCommands();
|
bool parseCommands();
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
void startLocalServer();
|
void startLocalServer();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -60,6 +60,8 @@ private:
|
|||||||
QThread m_vpnConnectionThread;
|
QThread m_vpnConnectionThread;
|
||||||
|
|
||||||
QNetworkAccessManager *m_nam;
|
QNetworkAccessManager *m_nam;
|
||||||
|
protected:
|
||||||
|
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // AMNEZIA_APPLICATION_H
|
#endif // AMNEZIA_APPLICATION_H
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import java.nio.channels.FileChannel
|
|||||||
import java.nio.channels.FileLock
|
import java.nio.channels.FileLock
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.time.ZoneOffset
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
import org.amnezia.vpn.util.Log.Priority.D
|
import org.amnezia.vpn.util.Log.Priority.D
|
||||||
import org.amnezia.vpn.util.Log.Priority.E
|
import org.amnezia.vpn.util.Log.Priority.E
|
||||||
@@ -135,8 +137,8 @@ object Log {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun formatLogMsg(tag: String, msg: String, priority: Priority): String {
|
private fun formatLogMsg(tag: String, msg: String, priority: Priority): String {
|
||||||
val date = LocalDateTime.now().format(dateTimeFormat)
|
val utcDate = ZonedDateTime.now(ZoneOffset.UTC).format(dateTimeFormat)
|
||||||
return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
|
return "${utcDate}Z ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
|
||||||
"$tag: $msg\n"
|
"$tag: $msg\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ include(${CLIENT_ROOT_DIR}/3rd/qrcodegen/qrcodegen.cmake)
|
|||||||
|
|
||||||
set(LIBSSH_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/libssh/")
|
set(LIBSSH_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/libssh/")
|
||||||
set(OPENSSL_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/openssl/")
|
set(OPENSSL_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/openssl/")
|
||||||
set(LIBXRAY_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/libxray")
|
|
||||||
|
|
||||||
set(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/lib")
|
set(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/lib")
|
||||||
|
|
||||||
@@ -28,12 +27,15 @@ if(WIN32)
|
|||||||
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib")
|
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib")
|
||||||
endif()
|
endif()
|
||||||
elseif(APPLE AND NOT IOS)
|
elseif(APPLE AND NOT IOS)
|
||||||
set(LIBS ${LIBS} "resolv")
|
if(MACOS_NE)
|
||||||
set(LIBXRAY_LIB_PATH "${LIBXRAY_ROOT_DIR}/macos/x86_64/libxray.a")
|
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/universal2/libssh.a")
|
||||||
set(LIBXRAY_INCLUDE_DIR "${LIBXRAY_ROOT_DIR}/macos/x86_64")
|
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/universal2/libz.a")
|
||||||
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libssh.a")
|
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/universal2")
|
||||||
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libz.a")
|
else()
|
||||||
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64")
|
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libssh.a")
|
||||||
|
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libz.a")
|
||||||
|
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64")
|
||||||
|
endif()
|
||||||
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/macos/include")
|
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/macos/include")
|
||||||
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libssl.a")
|
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libssl.a")
|
||||||
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libcrypto.a")
|
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libcrypto.a")
|
||||||
@@ -76,8 +78,6 @@ set(LIBS ${LIBS}
|
|||||||
${OPENSSL_LIB_CRYPTO_PATH}
|
${OPENSSL_LIB_CRYPTO_PATH}
|
||||||
)
|
)
|
||||||
|
|
||||||
set(LIBS ${LIBS} ${LIBXRAY_LIB_PATH})
|
|
||||||
|
|
||||||
add_compile_definitions(_WINSOCKAPI_)
|
add_compile_definitions(_WINSOCKAPI_)
|
||||||
|
|
||||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||||
@@ -94,5 +94,4 @@ include_directories(
|
|||||||
${CLIENT_ROOT_DIR}/3rd/qtkeychain/qtkeychain
|
${CLIENT_ROOT_DIR}/3rd/qtkeychain/qtkeychain
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/3rd/qtkeychain
|
${CMAKE_CURRENT_BINARY_DIR}/3rd/qtkeychain
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/3rd/libssh/include
|
${CMAKE_CURRENT_BINARY_DIR}/3rd/libssh/include
|
||||||
${LIBXRAY_INCLUDE_DIR}
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,11 +14,15 @@ set(LIBS ${LIBS}
|
|||||||
${FW_SECURITY}
|
${FW_SECURITY}
|
||||||
${FW_COREWLAN}
|
${FW_COREWLAN}
|
||||||
${FW_NETWORK}
|
${FW_NETWORK}
|
||||||
${FW_USERNOTIFICATIONS}
|
${FW_USER_NOTIFICATIONS}
|
||||||
${FW_NETWORK_EXTENSION}
|
${FW_NETWORK_EXTENSION}
|
||||||
)
|
)
|
||||||
|
|
||||||
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
MACOSX_BUNDLE TRUE
|
||||||
|
MACOSX_BUNDLE_SHORT_VERSION_STRING "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}"
|
||||||
|
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||||
|
)
|
||||||
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
|
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||||
|
|
||||||
@@ -31,6 +35,8 @@ set(SOURCES ${SOURCES}
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.mm
|
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.mm
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns)
|
set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns)
|
||||||
set(MACOSX_BUNDLE_ICON_FILE app.icns)
|
set(MACOSX_BUNDLE_ICON_FILE app.icns)
|
||||||
set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||||
@@ -49,4 +55,3 @@ execute_process(
|
|||||||
)
|
)
|
||||||
message("OSX_SDK_PATH is: ${OSX_SDK_PATH}")
|
message("OSX_SDK_PATH is: ${OSX_SDK_PATH}")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,168 @@
|
|||||||
|
message("Client ==> MacOS NE build")
|
||||||
|
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||||
|
|
||||||
|
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||||
|
|
||||||
|
enable_language(OBJC)
|
||||||
|
enable_language(Swift)
|
||||||
|
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS ShaderTools Widgets)
|
||||||
|
# Link Qt Widgets for QWidget, QMenu, QAction etc.
|
||||||
|
set(LIBS ${LIBS} Qt6::ShaderTools Qt6::Widgets)
|
||||||
|
|
||||||
|
find_library(FW_AUTHENTICATIONSERVICES AuthenticationServices)
|
||||||
|
find_library(FW_AVFOUNDATION AVFoundation)
|
||||||
|
find_library(FW_FOUNDATION Foundation)
|
||||||
|
find_library(FW_STOREKIT StoreKit)
|
||||||
|
find_library(FW_SERVICEMGMT ServiceManagement)
|
||||||
|
find_library(FW_USERNOTIFICATIONS UserNotifications)
|
||||||
|
find_library(FW_NETWORKEXTENSION NetworkExtension)
|
||||||
|
|
||||||
|
set(LIBS ${LIBS}
|
||||||
|
${FW_AUTHENTICATIONSERVICES}
|
||||||
|
${FW_AVFOUNDATION}
|
||||||
|
${FW_FOUNDATION}
|
||||||
|
${FW_STOREKIT}
|
||||||
|
${FW_SERVICEMGMT}
|
||||||
|
${FW_USERNOTIFICATIONS}
|
||||||
|
${FW_NETWORKEXTENSION}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
set(HEADERS ${HEADERS}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate-C-Interface.h
|
||||||
|
)
|
||||||
|
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h PROPERTIES OBJECTIVE_CPP_HEADER TRUE)
|
||||||
|
|
||||||
|
|
||||||
|
set(SOURCES ${SOURCES}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.mm
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.mm
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.mm
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm
|
||||||
|
)
|
||||||
|
|
||||||
|
set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns)
|
||||||
|
set(MACOSX_BUNDLE_ICON_FILE app.icns)
|
||||||
|
set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||||
|
set(SOURCES ${SOURCES} ${ICON_FILE})
|
||||||
|
|
||||||
|
|
||||||
|
target_include_directories(${PROJECT} PRIVATE
|
||||||
|
${Qt6Gui_PRIVATE_INCLUDE_DIRS}
|
||||||
|
${Qt6Widgets_PRIVATE_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||||
|
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Info.plist.in
|
||||||
|
MACOSX_BUNDLE_ICON_FILE "AppIcon"
|
||||||
|
MACOSX_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||||
|
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPN"
|
||||||
|
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||||
|
MACOSX_BUNDLE_LONG_VERSION_STRING "${APPLE_PROJECT_VERSION}-${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||||
|
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
|
||||||
|
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/macos/app/app.entitlements"
|
||||||
|
XCODE_ATTRIBUTE_MARKETING_VERSION "${APPLE_PROJECT_VERSION}"
|
||||||
|
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||||
|
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPN"
|
||||||
|
XCODE_ATTRIBUTE_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||||
|
XCODE_GENERATE_SCHEME TRUE
|
||||||
|
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
|
||||||
|
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
|
||||||
|
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
||||||
|
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY "NO"
|
||||||
|
XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY "YES"
|
||||||
|
XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "11.0"
|
||||||
|
|
||||||
|
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||||
|
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks"
|
||||||
|
XCODE_EMBED_APP_EXTENSIONS AmneziaVPNNetworkExtension
|
||||||
|
)
|
||||||
|
|
||||||
|
if(DEPLOY)
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr macos.org.amnezia.AmneziaVPN"
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev macos.org.amnezia.AmneziaVPN"
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||||
|
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_OBJC_INTERFACE_HEADER_NAME "AmneziaVPN-Swift.h"
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_OBJC_INTEROP_MODE "objcxx"
|
||||||
|
)
|
||||||
|
set_target_properties(${PROJECT} PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
|
||||||
|
)
|
||||||
|
target_include_directories(${PROJECT} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
target_compile_options(${PROJECT} PRIVATE
|
||||||
|
-DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\"
|
||||||
|
-DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(WG_APPLE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rd/amneziawg-apple/Sources)
|
||||||
|
|
||||||
|
target_sources(${PROJECT} PRIVATE
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKitC/x25519.c
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/LogController.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/ScreenProtection.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/VPNCController.swift
|
||||||
|
)
|
||||||
|
|
||||||
|
target_sources(${PROJECT} PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Images.xcassets
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Images.xcassets
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(macos/networkextension)
|
||||||
|
add_dependencies(${PROJECT} AmneziaVPNNetworkExtension)
|
||||||
|
|
||||||
|
get_target_property(QtCore_location Qt6::Core LOCATION)
|
||||||
|
message("QtCore_location")
|
||||||
|
message(${QtCore_location})
|
||||||
|
|
||||||
|
get_filename_component(QT_BIN_DIR_DETECTED "${QtCore_location}/../../../../../bin" ABSOLUTE)
|
||||||
|
|
||||||
|
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos/OpenVPNAdapter.framework"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos)
|
||||||
|
target_link_libraries("AmneziaVPNNetworkExtension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos/OpenVPNAdapter.framework")
|
||||||
|
|
||||||
|
add_custom_command(TARGET ${PROJECT} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||||
|
$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks
|
||||||
|
COMMAND /usr/bin/find "$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework" -name "*.sha256" -delete
|
||||||
|
COMMAND /usr/bin/codesign --force --sign "Apple Distribution"
|
||||||
|
"$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework/Versions/Current/OpenVPNAdapter"
|
||||||
|
COMMAND ${QT_BIN_DIR_DETECTED}/macdeployqt $<TARGET_BUNDLE_DIR:AmneziaVPN> -appstore-compliant -qmldir=${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
COMMENT "Signing OpenVPNAdapter framework"
|
||||||
|
)
|
||||||
@@ -39,7 +39,7 @@ set(HEADERS ${HEADERS}
|
|||||||
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h
|
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT IOS)
|
if(NOT IOS AND NOT MACOS_NE)
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.h
|
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.h
|
||||||
)
|
)
|
||||||
@@ -89,12 +89,26 @@ set(SOURCES ${SOURCES}
|
|||||||
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
|
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT IOS)
|
if(NOT IOS AND NOT MACOS_NE)
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.cpp
|
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Include native macOS platform helpers (dock/status-item)
|
||||||
|
if(APPLE AND NOT IOS)
|
||||||
|
list(APPEND HEADERS
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.h
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.h
|
||||||
|
${CLIENT_ROOT_DIR}/ui/macos_util.h
|
||||||
|
)
|
||||||
|
list(APPEND SOURCES
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.mm
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.mm
|
||||||
|
${CLIENT_ROOT_DIR}/ui/macos_util.mm
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT ANDROID)
|
if(NOT ANDROID)
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CLIENT_ROOT_DIR}/ui/notificationhandler.cpp
|
${CLIENT_ROOT_DIR}/ui/notificationhandler.cpp
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
|
|||||||
|
|
||||||
// no redirect-gateway
|
// no redirect-gateway
|
||||||
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
||||||
// Prevent ipv6 leak
|
// Prevent ipv6 leak
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <qtimer.h>
|
#include <qtimer.h>
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#else
|
#else
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
@@ -24,7 +24,7 @@ SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, const QShar
|
|||||||
|
|
||||||
QString SshConfigurator::convertOpenSShKey(const QString &key)
|
QString SshConfigurator::convertOpenSShKey(const QString &key)
|
||||||
{
|
{
|
||||||
#ifndef Q_OS_IOS
|
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
QProcess p;
|
QProcess p;
|
||||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||||
|
|
||||||
@@ -67,9 +67,10 @@ QString SshConfigurator::convertOpenSShKey(const QString &key)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEAD CODE.
|
||||||
void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
|
void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
|
||||||
{
|
{
|
||||||
#ifndef Q_OS_IOS
|
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
QProcess *p = new QProcess();
|
QProcess *p = new QProcess();
|
||||||
p->setProcessChannelMode(QProcess::SeparateChannels);
|
p->setProcessChannelMode(QProcess::SeparateChannels);
|
||||||
|
|
||||||
@@ -101,7 +102,7 @@ QProcessEnvironment SshConfigurator::prepareEnv()
|
|||||||
pathEnvVar.clear();
|
pathEnvVar.clear();
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
|
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;");
|
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;");
|
||||||
#elif defined(Q_OS_MACX)
|
#elif defined(Q_OS_MACX) && !defined(MACOS_NE)
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
|
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -261,6 +261,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
#elif defined(Q_OS_IOS)
|
#elif defined(Q_OS_IOS)
|
||||||
|
// Standard iOS build (without Network Extension limitations)
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case DockerContainer::WireGuard: return true;
|
case DockerContainer::WireGuard: return true;
|
||||||
case DockerContainer::OpenVpn: return true;
|
case DockerContainer::OpenVpn: return true;
|
||||||
@@ -269,7 +270,23 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
|||||||
case DockerContainer::Cloak: return true;
|
case DockerContainer::Cloak: return true;
|
||||||
case DockerContainer::SSXray: return true;
|
case DockerContainer::SSXray: return true;
|
||||||
// case DockerContainer::ShadowSocks: return true;
|
// case DockerContainer::ShadowSocks: return true;
|
||||||
default: return false;
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(MACOS_NE)
|
||||||
|
// macOS build using Network Extension – hide OpenVPN-based containers
|
||||||
|
switch (c) {
|
||||||
|
case DockerContainer::WireGuard: return true;
|
||||||
|
case DockerContainer::Awg: return true;
|
||||||
|
case DockerContainer::Xray: return true;
|
||||||
|
case DockerContainer::SSXray: return true;
|
||||||
|
case DockerContainer::OpenVpn:
|
||||||
|
case DockerContainer::Cloak:
|
||||||
|
case DockerContainer::ShadowSocks:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
#elif defined(Q_OS_MAC)
|
#elif defined(Q_OS_MAC)
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
|||||||
@@ -120,6 +120,9 @@ void CoreController::initControllers()
|
|||||||
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
|
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
|
||||||
&ConnectionController::onCurrentContainerUpdated); // TODO remove this
|
&ConnectionController::onCurrentContainerUpdated); // TODO remove this
|
||||||
|
|
||||||
|
connect(m_installController.get(), &InstallController::profileCleared,
|
||||||
|
m_protocolsModel.get(), &ProtocolsModel::updateModel);
|
||||||
|
|
||||||
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
|
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
|
||||||
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
|
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
|
||||||
|
|
||||||
@@ -151,6 +154,9 @@ void CoreController::initControllers()
|
|||||||
|
|
||||||
m_apiPremV1MigrationController.reset(new ApiPremV1MigrationController(m_serversModel, m_settings, this));
|
m_apiPremV1MigrationController.reset(new ApiPremV1MigrationController(m_serversModel, m_settings, this));
|
||||||
m_engine->rootContext()->setContextProperty("ApiPremV1MigrationController", m_apiPremV1MigrationController.get());
|
m_engine->rootContext()->setContextProperty("ApiPremV1MigrationController", m_apiPremV1MigrationController.get());
|
||||||
|
|
||||||
|
m_transferController.reset(new TransferController(m_settings, m_serversModel, m_exportController.get()));
|
||||||
|
m_engine->rootContext()->setContextProperty("TransferController", m_transferController.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initAndroidController()
|
void CoreController::initAndroidController()
|
||||||
@@ -242,7 +248,10 @@ void CoreController::initNotificationHandler()
|
|||||||
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
|
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
|
||||||
&ConnectionController::closeConnection);
|
&ConnectionController::closeConnection);
|
||||||
connect(this, &CoreController::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
|
connect(this, &CoreController::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
|
||||||
#endif
|
|
||||||
|
auto* trayHandler = qobject_cast<SystemTrayNotificationHandler*>(m_notificationHandler.get());
|
||||||
|
connect(this, &CoreController::websiteUrlChanged, trayHandler, &SystemTrayNotificationHandler::updateWebsiteUrl);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::updateTranslator(const QLocale &locale)
|
void CoreController::updateTranslator(const QLocale &locale)
|
||||||
@@ -279,6 +288,7 @@ void CoreController::updateTranslator(const QLocale &locale)
|
|||||||
m_engine->retranslate();
|
m_engine->retranslate();
|
||||||
|
|
||||||
emit translationsUpdated();
|
emit translationsUpdated();
|
||||||
|
emit websiteUrlChanged(m_languageModel->getCurrentSiteUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initErrorMessagesHandler()
|
void CoreController::initErrorMessagesHandler()
|
||||||
@@ -299,13 +309,10 @@ void CoreController::setQmlRoot()
|
|||||||
|
|
||||||
void CoreController::initApiCountryModelUpdateHandler()
|
void CoreController::initApiCountryModelUpdateHandler()
|
||||||
{
|
{
|
||||||
// TODO
|
|
||||||
connect(m_serversModel.get(), &ServersModel::updateApiCountryModel, this, [this]() {
|
connect(m_serversModel.get(), &ServersModel::updateApiCountryModel, this, [this]() {
|
||||||
m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(),
|
m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(),
|
||||||
m_serversModel->getProcessedServerData("apiServerCountryCode").toString());
|
m_serversModel->getProcessedServerData("apiServerCountryCode").toString());
|
||||||
});
|
});
|
||||||
connect(m_serversModel.get(), &ServersModel::updateApiServicesModel, this,
|
|
||||||
[this]() { m_apiServicesModel->updateModel(m_serversModel->getProcessedServerData("apiConfig").toJsonObject()); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initContainerModelUpdateHandler()
|
void CoreController::initContainerModelUpdateHandler()
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
#include <QQmlContext>
|
#include <QQmlContext>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
|
#ifndef Q_OS_ANDROID
|
||||||
|
#include "ui/systemtray_notificationhandler.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "ui/controllers/api/apiConfigsController.h"
|
#include "ui/controllers/api/apiConfigsController.h"
|
||||||
#include "ui/controllers/api/apiSettingsController.h"
|
#include "ui/controllers/api/apiSettingsController.h"
|
||||||
#include "ui/controllers/api/apiPremV1MigrationController.h"
|
#include "ui/controllers/api/apiPremV1MigrationController.h"
|
||||||
@@ -19,6 +23,7 @@
|
|||||||
#include "ui/controllers/settingsController.h"
|
#include "ui/controllers/settingsController.h"
|
||||||
#include "ui/controllers/sitesController.h"
|
#include "ui/controllers/sitesController.h"
|
||||||
#include "ui/controllers/systemController.h"
|
#include "ui/controllers/systemController.h"
|
||||||
|
#include "ui/controllers/transferController.h"
|
||||||
|
|
||||||
#include "ui/models/allowed_dns_model.h"
|
#include "ui/models/allowed_dns_model.h"
|
||||||
#include "ui/models/containers_model.h"
|
#include "ui/models/containers_model.h"
|
||||||
@@ -61,6 +66,7 @@ public:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void translationsUpdated();
|
void translationsUpdated();
|
||||||
|
void websiteUrlChanged(const QString &newUrl);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initModels();
|
void initModels();
|
||||||
@@ -140,6 +146,7 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
QScopedPointer<SftpConfigModel> m_sftpConfigModel;
|
QScopedPointer<SftpConfigModel> m_sftpConfigModel;
|
||||||
QScopedPointer<Socks5ProxyConfigModel> m_socks5ConfigModel;
|
QScopedPointer<Socks5ProxyConfigModel> m_socks5ConfigModel;
|
||||||
|
QScopedPointer<TransferController> m_transferController;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CORECONTROLLER_H
|
#endif // CORECONTROLLER_H
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
#include "QBlockCipher.h"
|
#include "QBlockCipher.h"
|
||||||
#include "QRsa.h"
|
#include "QRsa.h"
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS)
|
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
@@ -390,7 +390,7 @@ QString NetworkUtilities::getGatewayAndIface()
|
|||||||
close(sock);
|
close(sock);
|
||||||
return gateway_address;
|
return gateway_address;
|
||||||
#endif
|
#endif
|
||||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS)
|
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
QString gateway;
|
QString gateway;
|
||||||
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_FLAGS, RTF_GATEWAY};
|
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_FLAGS, RTF_GATEWAY};
|
||||||
int afinet_type[] = {AF_INET, AF_INET6};
|
int afinet_type[] = {AF_INET, AF_INET6};
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||||
|
<!-- Monitor -->
|
||||||
|
<rect x="2.5" y="3.5" width="14" height="10" rx="2" ry="2" stroke="#D7D8DB" stroke-width="1.8"/>
|
||||||
|
<line x1="9.5" y1="13.5" x2="9.5" y2="16.5" stroke="#D7D8DB" stroke-width="1.8" stroke-linecap="round"/>
|
||||||
|
<rect x="6" y="16.5" width="7" height="1.8" rx="0.9" ry="0.9" fill="#D7D8DB"/>
|
||||||
|
|
||||||
|
<!-- Phone -->
|
||||||
|
<rect x="17.5" y="6" width="4.5" height="9.5" rx="1.2" ry="1.2" stroke="#D7D8DB" stroke-width="1.8"/>
|
||||||
|
<circle cx="19.75" cy="14.2" r="0.6" fill="#D7D8DB"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 628 B |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 336 B After Width: | Height: | Size: 682 B |
|
Before Width: | Height: | Size: 593 B After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 593 B After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 340 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 11 KiB |
@@ -1,6 +1,68 @@
|
|||||||
{
|
{
|
||||||
"info" : {
|
"images": [
|
||||||
"author" : "xcode",
|
{
|
||||||
"version" : 1
|
"idiom": "mac",
|
||||||
|
"size": "16x16",
|
||||||
|
"scale": "1x",
|
||||||
|
"filename": "16.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "16x16",
|
||||||
|
"scale": "2x",
|
||||||
|
"filename": "16@2x.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "32x32",
|
||||||
|
"scale": "1x",
|
||||||
|
"filename": "32.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "32x32",
|
||||||
|
"scale": "2x",
|
||||||
|
"filename": "32@2x.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "128x128",
|
||||||
|
"scale": "1x",
|
||||||
|
"filename": "128.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "128x128",
|
||||||
|
"scale": "2x",
|
||||||
|
"filename": "128@2x.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "256x256",
|
||||||
|
"scale": "1x",
|
||||||
|
"filename": "256.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "256x256",
|
||||||
|
"scale": "2x",
|
||||||
|
"filename": "256@2x.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "512x512",
|
||||||
|
"scale": "1x",
|
||||||
|
"filename": "512.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom": "mac",
|
||||||
|
"size": "512x512",
|
||||||
|
"scale": "2x",
|
||||||
|
"filename": "512@2x.png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info": {
|
||||||
|
"version": 1,
|
||||||
|
"author": "xcode"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
|
||||||
|
|
||||||
<key>CFBundleAllowMixedLocalizations</key>
|
|
||||||
<true/>
|
|
||||||
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>${EXECUTABLE_NAME}</string>
|
|
||||||
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
||||||
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>$(PRODUCT_NAME)</string>
|
|
||||||
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
|
||||||
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>$(MARKETING_VERSION)</string>
|
|
||||||
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
|
||||||
|
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
|
||||||
<false/>
|
|
||||||
|
|
||||||
<key>LSApplicationCategoryType</key>
|
|
||||||
<string>public.app-category.utilities</string>
|
|
||||||
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
|
||||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
|
||||||
|
|
||||||
<key>LSMultipleInstancesProhibited</key>
|
|
||||||
<true/>
|
|
||||||
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string>NSApplication</string>
|
|
||||||
|
|
||||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleAllowMixedLocalizations</key>
|
||||||
|
<true/>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>${QT_INTERNAL_DOLLAR_VAR}{PRODUCT_NAME}</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||||
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
|
<false/>
|
||||||
|
<key>LSApplicationCategoryType</key>
|
||||||
|
<string>public.app-category.utilities</string>
|
||||||
|
|
||||||
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||||
|
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.wireguard.ios.app_group_id</key>
|
||||||
|
<string>group.org.amnezia.AmneziaVPN</string>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>Amnezia VPN needs access to the camera for reading QR-codes.</string>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSAllowsLocalNetworking</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>CFBundleIcons</key>
|
||||||
|
<dict/>
|
||||||
|
<key>UTImportedTypeDeclarations</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.data</string>
|
||||||
|
</array>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>Amnezia VPN config</string>
|
||||||
|
<key>UTTypeIconFiles</key>
|
||||||
|
<array/>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>org.amnezia.AmneziaVPN.amnezia-config</string>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>vpn</string>
|
||||||
|
</array>
|
||||||
|
<key>public.mime-type</key>
|
||||||
|
<array>
|
||||||
|
<string>text/plain</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.data</string>
|
||||||
|
</array>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>WireGuard config</string>
|
||||||
|
<key>UTTypeIconFiles</key>
|
||||||
|
<array/>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>org.amnezia.AmneziaVPN.wireguard-config</string>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>conf</string>
|
||||||
|
<string>cfg</string>
|
||||||
|
</array>
|
||||||
|
<key>public.mime-type</key>
|
||||||
|
<array>
|
||||||
|
<string>text/plain</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.data</string>
|
||||||
|
</array>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>OpenVPN config</string>
|
||||||
|
<key>UTTypeIconFiles</key>
|
||||||
|
<array/>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>org.amnezia.AmneziaVPN.openvpn-config</string>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>ovpn</string>
|
||||||
|
</array>
|
||||||
|
<key>public.mime-type</key>
|
||||||
|
<array>
|
||||||
|
<string>text/plain</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.data</string>
|
||||||
|
</array>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>AmneziaVPN backup file</string>
|
||||||
|
<key>UTTypeIconFiles</key>
|
||||||
|
<array/>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>org.amnezia.AmneziaVPN.backup-config</string>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>backup</string>
|
||||||
|
</array>
|
||||||
|
<key>public.mime-type</key>
|
||||||
|
<array>
|
||||||
|
<string>text/plain</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleDocumentTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>Amnezia VPN config</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Alternate</string>
|
||||||
|
<key>LSItemContentTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>org.amnezia.AmneziaVPN.amnezia-config</string>
|
||||||
|
<string>org.amnezia.AmneziaVPN.wireguard-config</string>
|
||||||
|
<string>org.amnezia.AmneziaVPN.openvpn-config</string>
|
||||||
|
<string>org.amnezia.AmneziaVPN.backup-config</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>NSExtensions</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.networkextension.packet-tunnel</string>
|
||||||
|
<key>NSExtensionPrincipalClass</key>
|
||||||
|
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -2,34 +2,40 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.application-identifier</key>
|
<key>com.apple.developer.networking.custom-protocol</key>
|
||||||
<string>$(DEVELOPMENT_TEAM).$(APP_ID_MACOS)</string>
|
<true/>
|
||||||
|
|
||||||
<key>com.apple.developer.networking.networkextension</key>
|
<key>com.apple.developer.networking.networkextension</key>
|
||||||
<array>
|
<array>
|
||||||
|
<string>app-proxy-provider</string>
|
||||||
<string>packet-tunnel-provider</string>
|
<string>packet-tunnel-provider</string>
|
||||||
|
<string>dns-settings</string>
|
||||||
|
<string>relay</string>
|
||||||
|
<string>content-filter-provider</string>
|
||||||
|
<string>dns-proxy</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>com.apple.developer.system-extension.install</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.developer.networking.vpn.api</key>
|
||||||
|
<array>
|
||||||
|
<string>allow-vpn</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.org.amnezia.AmneziaVPN</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.security.files.user-selected.read-only</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.files.user-selected.read-write</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.network.server</key>
|
||||||
|
<true/>
|
||||||
<key>keychain-access-groups</key>
|
<key>keychain-access-groups</key>
|
||||||
<array>
|
<array>
|
||||||
<string>$(DEVELOPMENT_TEAM).*</string>
|
<string>$(DEVELOPMENT_TEAM).*</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
<key>com.apple.developer.team-identifier</key>
|
|
||||||
<string>$(DEVELOPMENT_TEAM)</string>
|
|
||||||
|
|
||||||
<key>com.apple.security.app-sandbox</key>
|
|
||||||
<true/>
|
|
||||||
|
|
||||||
<key>com.apple.security.application-groups</key>
|
|
||||||
<array>
|
|
||||||
<string>$(DEVELOPMENT_TEAM).$(GROUP_ID_MACOS)</string>
|
|
||||||
</array>
|
|
||||||
|
|
||||||
<key>com.apple.security.network.client</key>
|
|
||||||
<true/>
|
|
||||||
|
|
||||||
<key>com.apple.security.network.server</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -2,41 +2,30 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.application-identifier</key>
|
<key>com.apple.developer.networking.custom-protocol</key>
|
||||||
<string>$(DEVELOPMENT_TEAM).$(NETEXT_ID_MACOS)</string>
|
<true/>
|
||||||
|
|
||||||
<key>com.apple.developer.networking.networkextension</key>
|
<key>com.apple.developer.networking.networkextension</key>
|
||||||
<array>
|
<array>
|
||||||
|
<string>dns-settings</string>
|
||||||
|
<string>relay</string>
|
||||||
<string>packet-tunnel-provider</string>
|
<string>packet-tunnel-provider</string>
|
||||||
|
<string>content-filter-provider</string>
|
||||||
|
<string>dns-proxy</string>
|
||||||
|
<string>app-proxy-provider</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>com.apple.developer.networking.vpn.api</key>
|
||||||
<key>keychain-access-groups</key>
|
|
||||||
<array>
|
<array>
|
||||||
<string>$(DEVELOPMENT_TEAM).*</string>
|
<string>allow-vpn</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
<key>com.apple.developer.team-identifier</key>
|
|
||||||
<string>$(DEVELOPMENT_TEAM)</string>
|
|
||||||
|
|
||||||
<key>com.apple.developer.system-extension.install</key>
|
|
||||||
<true/>
|
|
||||||
|
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
|
||||||
<key>com.apple.security.application-groups</key>
|
<key>com.apple.security.application-groups</key>
|
||||||
<array>
|
<array>
|
||||||
<string>$(DEVELOPMENT_TEAM).$(GROUP_ID_MACOS)</string>
|
<string>group.org.amnezia.AmneziaVPN</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
<key>com.apple.security.network.client</key>
|
<key>com.apple.security.network.client</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
|
||||||
<key>com.apple.security.network.server</key>
|
<key>com.apple.security.network.server</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.private.network.socket-delegate</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
enable_language(Swift)
|
||||||
|
message("Client message >> macos build >> AmneziaVPNNetworkExtension")
|
||||||
|
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
|
||||||
|
|
||||||
|
add_executable(AmneziaVPNNetworkExtension)
|
||||||
|
|
||||||
|
message("executable_path is: @executable_path/../../Frameworks")
|
||||||
|
set_target_properties(AmneziaVPNNetworkExtension PROPERTIES
|
||||||
|
XCODE_PRODUCT_TYPE com.apple.product-type.app-extension
|
||||||
|
# MACOSX_BUNDLE YES
|
||||||
|
BUNDLE_EXTENSION appex
|
||||||
|
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
|
||||||
|
MACOSX_BUNDLE_INFO_STRING "AmneziaVPNNetworkExtension"
|
||||||
|
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPNNetworkExtension"
|
||||||
|
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}.network-extension"
|
||||||
|
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_NAME "${BUILD_IOS_APP_IDENTIFIER}.network-extension"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS ${CMAKE_CURRENT_SOURCE_DIR}/AmneziaVPNNetworkExtension.entitlements
|
||||||
|
XCODE_ATTRIBUTE_MARKETING_VERSION "${APP_MAJOR_VERSION}"
|
||||||
|
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${BUILD_ID}"
|
||||||
|
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPNNetworkExtension"
|
||||||
|
|
||||||
|
XCODE_ATTRIBUTE_APPLICATION_EXTENSION_API_ONLY "YES"
|
||||||
|
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
|
||||||
|
XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "11.0"
|
||||||
|
|
||||||
|
XCODE_ATTRIBUTE_INFOPLIST_FILE ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
|
||||||
|
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../../../Frameworks @loader_path/../../../../Frameworks"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(DEPLOY)
|
||||||
|
message("DEPLOY is ON")
|
||||||
|
set_target_properties(AmneziaVPNNetworkExtension PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr macos.org.amnezia.amneziaVPN.NE"
|
||||||
|
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev macos.org.amnezia.amneziaVPN.NE"
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set_target_properties(AmneziaVPNNetworkExtension PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_target_properties(AmneziaVPNNetworkExtension PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||||
|
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/WireGuardNetworkExtension-Bridging-Header.h"
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_OPTIMIZATION_LEVEL "-Onone"
|
||||||
|
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties("AmneziaVPNNetworkExtension" PROPERTIES
|
||||||
|
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
|
||||||
|
)
|
||||||
|
|
||||||
|
find_library(FW_ASSETS_LIBRARY AssetsLibrary)
|
||||||
|
find_library(FW_MOBILE_CORE MobileCoreServices)
|
||||||
|
find_library(FW_UI_KIT UIKit)
|
||||||
|
find_library(FW_LIBRESOLV libresolv.9.tbd)
|
||||||
|
|
||||||
|
|
||||||
|
# Set the root directory
|
||||||
|
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
|
||||||
|
|
||||||
|
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${FW_LIBRESOLV})
|
||||||
|
|
||||||
|
target_compile_options(AmneziaVPNNetworkExtension PRIVATE -DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\")
|
||||||
|
target_compile_options(AmneziaVPNNetworkExtension PRIVATE -DNETWORK_EXTENSION=1)
|
||||||
|
|
||||||
|
set(WG_APPLE_SOURCE_DIR ${CLIENT_ROOT_DIR}/3rd/amneziawg-apple/Sources)
|
||||||
|
|
||||||
|
message("WG_APPLE_SOURCE_DIR is: ${WG_APPLE_SOURCE_DIR}")
|
||||||
|
message("CLIENT_ROOT_DIR is: ${CLIENT_ROOT_DIR}")
|
||||||
|
|
||||||
|
target_sources(AmneziaVPNNetworkExtension PRIVATE
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/WireGuardAdapter.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PacketTunnelSettingsGenerator.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/DNSResolver.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardNetworkExtension/ErrorNotifier.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/Shared/Keychain.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/Shared/Model/TunnelConfiguration+WgQuickConfig.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/Shared/Model/NETunnelProviderProtocol+Extension.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/Shared/Model/String+ArrayConversion.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/TunnelConfiguration.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/IPAddressRange.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/Endpoint.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/DNSServer.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/InterfaceConfiguration.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PeerConfiguration.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/Shared/FileManager+Extension.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKitC/x25519.c
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/Array+ConcurrentMap.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/IPAddress+AddrInfo.swift
|
||||||
|
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PrivateKey.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/HevSocksTunnel.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/NELogController.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+WireGuard.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+OpenVPN.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+Xray.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/WGConfig.swift
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/iosglue.mm
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/ios/XrayConfig.swift
|
||||||
|
)
|
||||||
|
|
||||||
|
target_sources(AmneziaVPNNetworkExtension PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(TARGET AmneziaVPNNetworkExtension APPEND PROPERTY RESOURCE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
||||||
|
)
|
||||||
|
|
||||||
|
## Build wireguard-go-version.h
|
||||||
|
execute_process(
|
||||||
|
COMMAND go list -m golang.zx2c4.com/wireguard
|
||||||
|
WORKING_DIRECTORY ${CLIENT_ROOT_DIR}/3rd/wireguard-apple/Sources/WireGuardKitGo
|
||||||
|
OUTPUT_VARIABLE WG_VERSION_FULL
|
||||||
|
)
|
||||||
|
string(REGEX REPLACE ".*v\([0-9.]*\).*" "\\1" WG_VERSION_STRING 1.1.1)
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/wireguard-go-version.h.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
|
||||||
|
target_sources(AmneziaVPNNetworkExtension PRIVATE
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
|
||||||
|
|
||||||
|
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR})
|
||||||
|
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/wireguard/macos/universal2/libwg-go.a)
|
||||||
|
|
||||||
|
message(${CLIENT_ROOT_DIR})
|
||||||
|
message(${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/libhev-socks5-tunnel.a)
|
||||||
|
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/libhev-socks5-tunnel.a)
|
||||||
|
|
||||||
|
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/Headers)
|
||||||
@@ -3,27 +3,32 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>en</string>
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>AmneziaVPNNetworkExtension</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>AmneziaVPNNetworkExtension</string>
|
||||||
|
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
<string>org.amnezia.AmneziaVPN.network-extension</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>$(PRODUCT_NAME)</string>
|
<string>AmneziaVPNNetworkExtension</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>$(MARKETING_VERSION)</string>
|
<string>${APPLE_PROJECT_VERSION}</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
<string>${CMAKE_PROJECT_VERSION_TWEAK}</string>
|
||||||
|
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<false/>
|
<false/>
|
||||||
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
|
||||||
|
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>AmneziaVPNNetworkExtension</string>
|
||||||
|
|
||||||
<key>NSExtension</key>
|
<key>NSExtension</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSExtensionPointIdentifier</key>
|
<key>NSExtensionPointIdentifier</key>
|
||||||
@@ -31,5 +36,11 @@
|
|||||||
<key>NSExtensionPrincipalClass</key>
|
<key>NSExtensionPrincipalClass</key>
|
||||||
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
|
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
||||||
|
<key>com.wireguard.ios.app_group_id</key>
|
||||||
|
<string>group.org.amnezia.AmneziaVPN</string>
|
||||||
|
|
||||||
|
<key>com.wireguard.macos.app_group_id</key>
|
||||||
|
<string>${BUILD_VPN_DEVELOPMENT_TEAM}.group.org.amnezia.AmneziaVPN</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>1C8F.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>C617.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "macos/gobridge/wireguard.h"
|
|
||||||
#include "wireguard-go-version.h"
|
#include "wireguard-go-version.h"
|
||||||
#include "3rd/awg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
#include "3rd/amneziawg-apple/Sources/WireGuardKitGo/wireguard.h"
|
||||||
|
#include "3rd/amneziawg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -23,3 +23,8 @@ bool key_from_hex(uint8_t key[WG_KEY_LEN], const char* hex);
|
|||||||
bool key_eq(const uint8_t key1[WG_KEY_LEN], const uint8_t key2[WG_KEY_LEN]);
|
bool key_eq(const uint8_t key1[WG_KEY_LEN], const uint8_t key2[WG_KEY_LEN]);
|
||||||
|
|
||||||
void write_msg_to_log(const char* tag, const char* msg);
|
void write_msg_to_log(const char* tag, const char* msg);
|
||||||
|
|
||||||
|
// init function definition in C
|
||||||
|
void hev_socks5_tunnel_quit(void);
|
||||||
|
// Updated function definition in C
|
||||||
|
int hev_socks5_tunnel_main(const char* configFile, int fd);
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#ifndef WIREGUARD_GO_VERSION
|
||||||
|
#define WIREGUARD_GO_VERSION "@WG_VERSION_STRING@"
|
||||||
|
#endif // WIREGUARD_GO_VERSION
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
#include "platforms/ios/QtAppDelegate-C-Interface.h"
|
#include "platforms/ios/QtAppDelegate-C-Interface.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
bool isAnotherInstanceRunning()
|
bool isAnotherInstanceRunning()
|
||||||
{
|
{
|
||||||
QLocalSocket socket;
|
QLocalSocket socket;
|
||||||
@@ -45,7 +45,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
AmneziaApplication app(argc, argv);
|
AmneziaApplication app(argc, argv);
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
if (isAnotherInstanceRunning()) {
|
if (isAnotherInstanceRunning()) {
|
||||||
QTimer::singleShot(1000, &app, [&]() { app.quit(); });
|
QTimer::singleShot(1000, &app, [&]() { app.quit(); });
|
||||||
return app.exec();
|
return app.exec();
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ extension PacketTunnelProvider {
|
|||||||
startHandler = completionHandler
|
startHandler = completionHandler
|
||||||
ovpnAdapter?.connect(using: packetFlow)
|
ovpnAdapter?.connect(using: packetFlow)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||||
guard let completionHandler = completionHandler else { return }
|
guard let completionHandler = completionHandler else { return }
|
||||||
let bytesin = ovpnAdapter?.transportStatistics.bytesIn
|
let bytesin = ovpnAdapter?.transportStatistics.bytesIn
|
||||||
|
|||||||
@@ -112,9 +112,19 @@ extension PacketTunnelProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let lastHandshakeString = settingsDictionary["last_handshake_time_sec"]
|
||||||
|
let lastHandshake: Int64
|
||||||
|
|
||||||
|
if let lastHandshakeValue = lastHandshakeString, let handshakeValue = Int64(lastHandshakeValue) {
|
||||||
|
lastHandshake = handshakeValue
|
||||||
|
} else {
|
||||||
|
lastHandshake = -2 // Return an error if there is no value for `last_handshake_time_sec`
|
||||||
|
}
|
||||||
|
|
||||||
let response: [String: Any] = [
|
let response: [String: Any] = [
|
||||||
"rx_bytes": settingsDictionary["rx_bytes"] ?? "0",
|
"rx_bytes": settingsDictionary["rx_bytes"] ?? "0",
|
||||||
"tx_bytes": settingsDictionary["tx_bytes"] ?? "0"
|
"tx_bytes": settingsDictionary["tx_bytes"] ?? "0",
|
||||||
|
"last_handshake_time_sec": lastHandshake
|
||||||
]
|
]
|
||||||
|
|
||||||
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
|
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#if !MACOS_NE
|
||||||
#include "QRCodeReaderBase.h"
|
#include "QRCodeReaderBase.h"
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
@@ -108,3 +109,19 @@ void QRCodeReader::startReading() {
|
|||||||
void QRCodeReader::stopReading() {
|
void QRCodeReader::stopReading() {
|
||||||
[m_qrCodeReader stopReading];
|
[m_qrCodeReader stopReading];
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
#include "QRCodeReaderBase.h"
|
||||||
|
|
||||||
|
QRCodeReader::QRCodeReader()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect QRCodeReader::cameraSize() {
|
||||||
|
return QRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QRCodeReader::startReading() {}
|
||||||
|
void QRCodeReader::stopReading() {}
|
||||||
|
void QRCodeReader::setCameraSize(QRect) {}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
#if !MACOS_NE
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
#endif
|
||||||
@interface QIOSApplicationDelegate
|
@interface QIOSApplicationDelegate
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
|
|
||||||
@implementation QIOSApplicationDelegate (AmneziaVPNDelegate)
|
@implementation QIOSApplicationDelegate (AmneziaVPNDelegate)
|
||||||
|
#if !MACOS_NE
|
||||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||||
{
|
{
|
||||||
[application setMinimumBackgroundFetchInterval: UIApplicationBackgroundFetchIntervalMinimum];
|
[application setMinimumBackgroundFetchInterval: UIApplicationBackgroundFetchIntervalMinimum];
|
||||||
@@ -57,5 +57,5 @@
|
|||||||
}
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
#if MACOS_NE
|
||||||
|
public func toggleScreenshots(_ isEnabled: Bool) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScreenProtection {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
#else
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
public func toggleScreenshots(_ isEnabled: Bool) {
|
public func toggleScreenshots(_ isEnabled: Bool) {
|
||||||
@@ -90,3 +100,4 @@ struct ProtectionPair {
|
|||||||
textField.removeFromSuperview()
|
textField.removeFromSuperview()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ public:
|
|||||||
void disconnectVpn();
|
void disconnectVpn();
|
||||||
|
|
||||||
void vpnStatusDidChange(void *pNotification);
|
void vpnStatusDidChange(void *pNotification);
|
||||||
|
|
||||||
void vpnConfigurationDidChange(void *pNotification);
|
void vpnConfigurationDidChange(void *pNotification);
|
||||||
|
|
||||||
void getBackendLogs(std::function<void(const QString &)> &&callback);
|
void getBackendLogs(std::function<void(const QString &)> &&callback);
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const char* MessageKey::isOnDemand = "is-on-demand";
|
|||||||
const char* MessageKey::SplitTunnelType = "SplitTunnelType";
|
const char* MessageKey::SplitTunnelType = "SplitTunnelType";
|
||||||
const char* MessageKey::SplitTunnelSites = "SplitTunnelSites";
|
const char* MessageKey::SplitTunnelSites = "SplitTunnelSites";
|
||||||
|
|
||||||
|
#if !MACOS_NE
|
||||||
static UIViewController* getViewController() {
|
static UIViewController* getViewController() {
|
||||||
NSArray *windows = [[UIApplication sharedApplication]windows];
|
NSArray *windows = [[UIApplication sharedApplication]windows];
|
||||||
for (UIWindow *window in windows) {
|
for (UIWindow *window in windows) {
|
||||||
@@ -36,6 +37,7 @@ static UIViewController* getViewController() {
|
|||||||
}
|
}
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
|
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
@@ -249,6 +251,21 @@ void IosController::checkStatus()
|
|||||||
sendVpnExtensionMessage(message, [&](NSDictionary* response){
|
sendVpnExtensionMessage(message, [&](NSDictionary* response){
|
||||||
uint64_t txBytes = [response[@"tx_bytes"] intValue];
|
uint64_t txBytes = [response[@"tx_bytes"] intValue];
|
||||||
uint64_t rxBytes = [response[@"rx_bytes"] intValue];
|
uint64_t rxBytes = [response[@"rx_bytes"] intValue];
|
||||||
|
|
||||||
|
uint64_t last_handshake_time_sec = 0;
|
||||||
|
#if !MACOS_NE
|
||||||
|
if (response[@"last_handshake_time_sec"] && ![response[@"last_handshake_time_sec"] isKindOfClass:[NSNull class]]) {
|
||||||
|
last_handshake_time_sec = [response[@"last_handshake_time_sec"] intValue];
|
||||||
|
} else {
|
||||||
|
qDebug() << "Key last_handshake_time_sec is missing or null";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_handshake_time_sec < 0) {
|
||||||
|
disconnectVpn();
|
||||||
|
qDebug() << "Invalid handshake time, disconnecting VPN.";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
emit bytesChanged(rxBytes - m_rxBytes, txBytes - m_txBytes);
|
emit bytesChanged(rxBytes - m_rxBytes, txBytes - m_txBytes);
|
||||||
m_rxBytes = rxBytes;
|
m_rxBytes = rxBytes;
|
||||||
m_txBytes = txBytes;
|
m_txBytes = txBytes;
|
||||||
@@ -803,14 +820,14 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
|||||||
NSURL *logFileUrl = [[NSURL alloc] initFileURLWithPath:filesToSend[i].toNSString()];
|
NSURL *logFileUrl = [[NSURL alloc] initFileURLWithPath:filesToSend[i].toNSString()];
|
||||||
[sharingItems addObject:logFileUrl];
|
[sharingItems addObject:logFileUrl];
|
||||||
}
|
}
|
||||||
|
#if !MACOS_NE
|
||||||
UIViewController *qtController = getViewController();
|
UIViewController *qtController = getViewController();
|
||||||
if (!qtController) return;
|
if (!qtController) return;
|
||||||
|
|
||||||
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
|
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
|
||||||
|
#endif
|
||||||
__block bool isAccepted = false;
|
__block bool isAccepted = false;
|
||||||
|
#if !MACOS_NE
|
||||||
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
|
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
|
||||||
isAccepted = completed;
|
isAccepted = completed;
|
||||||
emit finished();
|
emit finished();
|
||||||
@@ -823,6 +840,7 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
|||||||
popController.sourceRect = CGRectMake(100, 100, 100, 100);
|
popController.sourceRect = CGRectMake(100, 100, 100, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
QEventLoop wait;
|
QEventLoop wait;
|
||||||
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
||||||
wait.exec();
|
wait.exec();
|
||||||
@@ -831,6 +849,7 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString IosController::openFile() {
|
QString IosController::openFile() {
|
||||||
|
#if !MACOS_NE
|
||||||
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen];
|
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen];
|
||||||
|
|
||||||
DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init];
|
DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init];
|
||||||
@@ -841,8 +860,9 @@ QString IosController::openFile() {
|
|||||||
|
|
||||||
[qtController presentViewController:documentPicker animated:YES completion:nil];
|
[qtController presentViewController:documentPicker animated:YES completion:nil];
|
||||||
|
|
||||||
|
#endif
|
||||||
__block QString filePath;
|
__block QString filePath;
|
||||||
|
#if !MACOS_NE
|
||||||
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
|
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
|
||||||
if (path) {
|
if (path) {
|
||||||
filePath = QString::fromUtf8(path.UTF8String);
|
filePath = QString::fromUtf8(path.UTF8String);
|
||||||
@@ -851,7 +871,7 @@ QString IosController::openFile() {
|
|||||||
}
|
}
|
||||||
emit finished();
|
emit finished();
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
QEventLoop wait;
|
QEventLoop wait;
|
||||||
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
||||||
wait.exec();
|
wait.exec();
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
#import <NetworkExtension/NetworkExtension.h>
|
#import <NetworkExtension/NetworkExtension.h>
|
||||||
#import <NetworkExtension/NETunnelProviderSession.h>
|
#import <NetworkExtension/NETunnelProviderSession.h>
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#if !MACOS_NE
|
||||||
#include <UIKit/UIKit.h>
|
#include <UIKit/UIKit.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <Security/Security.h>
|
#include <Security/Security.h>
|
||||||
|
|
||||||
class IosController;
|
class IosController;
|
||||||
@@ -17,9 +21,10 @@ class IosController;
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
typedef void (^DocumentPickerClosedCallback)(NSString *path);
|
typedef void (^DocumentPickerClosedCallback)(NSString *path);
|
||||||
|
#if !MACOS_NE
|
||||||
@interface DocumentPickerDelegate : NSObject <UIDocumentPickerDelegate>
|
@interface DocumentPickerDelegate : NSObject <UIDocumentPickerDelegate>
|
||||||
|
|
||||||
@property (nonatomic, copy) DocumentPickerClosedCallback documentPickerClosedCallback;
|
@property (nonatomic, copy) DocumentPickerClosedCallback documentPickerClosedCallback;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -26,7 +26,8 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation DocumentPickerDelegate
|
#if !MACOS_NE
|
||||||
|
@implementation DocumentPickerDelegate
|
||||||
|
|
||||||
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
|
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
|
||||||
for (NSURL *url in urls) {
|
for (NSURL *url in urls) {
|
||||||
@@ -42,4 +43,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#import <UserNotifications/UserNotifications.h>
|
#import <UserNotifications/UserNotifications.h>
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#if !MACOS_NE
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
@interface IOSNotificationDelegate
|
@interface IOSNotificationDelegate
|
||||||
@@ -87,3 +89,86 @@ void IOSNotificationHandler::notify(NotificationHandler::Message type, const QSt
|
|||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
// Removed the UIResponder and UIApplicationDelegate references as these are not available in macOS
|
||||||
|
@interface IOSNotificationDelegate
|
||||||
|
: NSObject <UNUserNotificationCenterDelegate> {
|
||||||
|
IOSNotificationHandler* m_iosNotificationHandler;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation IOSNotificationDelegate
|
||||||
|
|
||||||
|
- (id)initWithObject:(IOSNotificationHandler*)notification {
|
||||||
|
self = [super init]; // Removed `super init` as it refers to UIResponder, which is iOS specific
|
||||||
|
if (self) {
|
||||||
|
m_iosNotificationHandler = notification;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)userNotificationCenter:(UNUserNotificationCenter*)center
|
||||||
|
willPresentNotification:(UNNotification*)notification
|
||||||
|
withCompletionHandler:
|
||||||
|
(void (^)(UNNotificationPresentationOptions options))completionHandler {
|
||||||
|
Q_UNUSED(center)
|
||||||
|
completionHandler(UNNotificationPresentationOptionList | UNNotificationPresentationOptionBanner);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)userNotificationCenter:(UNUserNotificationCenter*)center
|
||||||
|
didReceiveNotificationResponse:(UNNotificationResponse*)response
|
||||||
|
withCompletionHandler:(void (^)())completionHandler {
|
||||||
|
Q_UNUSED(center)
|
||||||
|
Q_UNUSED(response)
|
||||||
|
completionHandler();
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
IOSNotificationHandler::IOSNotificationHandler(QObject* parent) : NotificationHandler(parent) {
|
||||||
|
|
||||||
|
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||||
|
[center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert |
|
||||||
|
UNAuthorizationOptionBadge)
|
||||||
|
completionHandler:^(BOOL granted, NSError* _Nullable error) {
|
||||||
|
Q_UNUSED(granted);
|
||||||
|
if (!error) {
|
||||||
|
m_delegate = [[IOSNotificationDelegate alloc] initWithObject:this];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
IOSNotificationHandler::~IOSNotificationHandler() { }
|
||||||
|
|
||||||
|
void IOSNotificationHandler::notify(NotificationHandler::Message type, const QString& title,
|
||||||
|
const QString& message, int timerMsec) {
|
||||||
|
Q_UNUSED(type);
|
||||||
|
|
||||||
|
if (!m_delegate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
|
||||||
|
content.title = title.toNSString();
|
||||||
|
content.body = message.toNSString();
|
||||||
|
content.sound = [UNNotificationSound defaultSound];
|
||||||
|
|
||||||
|
int timerSec = timerMsec / 1000;
|
||||||
|
UNTimeIntervalNotificationTrigger* trigger =
|
||||||
|
[UNTimeIntervalNotificationTrigger triggerWithTimeInterval:timerSec repeats:NO];
|
||||||
|
|
||||||
|
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:@"amneziavpn"
|
||||||
|
content:content
|
||||||
|
trigger:trigger];
|
||||||
|
|
||||||
|
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||||
|
center.delegate = (id<UNUserNotificationCenterDelegate>)m_delegate;
|
||||||
|
|
||||||
|
[center addNotificationRequest:request
|
||||||
|
withCompletionHandler:^(NSError* _Nullable error) {
|
||||||
|
if (error) {
|
||||||
|
NSLog(@"Local Notification failed");
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ namespace amnezia
|
|||||||
|
|
||||||
constexpr char defaultPort[] = "51820";
|
constexpr char defaultPort[] = "51820";
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
constexpr char defaultMtu[] = "1280";
|
constexpr char defaultMtu[] = "1280";
|
||||||
#else
|
#else
|
||||||
constexpr char defaultMtu[] = "1376";
|
constexpr char defaultMtu[] = "1376";
|
||||||
@@ -212,7 +212,7 @@ namespace amnezia
|
|||||||
namespace awg
|
namespace awg
|
||||||
{
|
{
|
||||||
constexpr char defaultPort[] = "55424";
|
constexpr char defaultPort[] = "55424";
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
constexpr char defaultMtu[] = "1280";
|
constexpr char defaultMtu[] = "1280";
|
||||||
#else
|
#else
|
||||||
constexpr char defaultMtu[] = "1376";
|
constexpr char defaultMtu[] = "1376";
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "core/errorstrings.h"
|
#include "core/errorstrings.h"
|
||||||
#include "vpnprotocol.h"
|
#include "vpnprotocol.h"
|
||||||
|
|
||||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) and !defined MACOS_NE || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||||
#include "openvpnovercloakprotocol.h"
|
#include "openvpnovercloakprotocol.h"
|
||||||
#include "openvpnprotocol.h"
|
#include "openvpnprotocol.h"
|
||||||
#include "shadowsocksvpnprotocol.h"
|
#include "shadowsocksvpnprotocol.h"
|
||||||
@@ -109,7 +109,7 @@ VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject &
|
|||||||
#if defined(Q_OS_WINDOWS)
|
#if defined(Q_OS_WINDOWS)
|
||||||
case DockerContainer::Ipsec: return new Ikev2Protocol(configuration);
|
case DockerContainer::Ipsec: return new Ikev2Protocol(configuration);
|
||||||
#endif
|
#endif
|
||||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) and !defined MACOS_NE || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||||
case DockerContainer::OpenVpn: return new OpenVpnProtocol(configuration);
|
case DockerContainer::OpenVpn: return new OpenVpnProtocol(configuration);
|
||||||
case DockerContainer::Cloak: return new OpenVpnOverCloakProtocol(configuration);
|
case DockerContainer::Cloak: return new OpenVpnOverCloakProtocol(configuration);
|
||||||
case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration);
|
case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration);
|
||||||
|
|||||||
@@ -1,28 +1,12 @@
|
|||||||
#include "xrayprotocol.h"
|
#include "xrayprotocol.h"
|
||||||
|
|
||||||
#include "core/defs.h"
|
|
||||||
#include "core/networkUtilities.h"
|
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QNetworkInterface>
|
#include <QNetworkInterface>
|
||||||
#include <QtCore/qcontainerfwd.h>
|
|
||||||
#include <QtCore/qdebug.h>
|
|
||||||
#include <QtCore/qlogging.h>
|
|
||||||
#include <QtCore/qobject.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <cerrno>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <ifaddrs.h>
|
|
||||||
#include <net/if.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <netinet/ip.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#include "core/ipcclient.h"
|
#include "core/networkUtilities.h"
|
||||||
#include "libxray.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) : VpnProtocol(configuration, parent)
|
XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) : VpnProtocol(configuration, parent)
|
||||||
{
|
{
|
||||||
@@ -31,9 +15,6 @@ XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) :
|
|||||||
m_vpnGateway = amnezia::protocols::xray::defaultLocalAddr;
|
m_vpnGateway = amnezia::protocols::xray::defaultLocalAddr;
|
||||||
m_vpnLocalAddress = amnezia::protocols::xray::defaultLocalAddr;
|
m_vpnLocalAddress = amnezia::protocols::xray::defaultLocalAddr;
|
||||||
m_t2sProcess = IpcClient::InterfaceTun2Socks();
|
m_t2sProcess = IpcClient::InterfaceTun2Socks();
|
||||||
|
|
||||||
amnezia_xray_setloghandler(&XrayProtocol::ctxLogHandler, this);
|
|
||||||
amnezia_xray_setsockcallback(&XrayProtocol::ctxSockCallback, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
XrayProtocol::~XrayProtocol()
|
XrayProtocol::~XrayProtocol()
|
||||||
@@ -44,20 +25,61 @@ XrayProtocol::~XrayProtocol()
|
|||||||
|
|
||||||
ErrorCode XrayProtocol::start()
|
ErrorCode XrayProtocol::start()
|
||||||
{
|
{
|
||||||
qDebug() << "XrayProtocol::start()";
|
qDebug().noquote() << "XrayProtocol xrayExecPath():" << xrayExecPath();
|
||||||
|
|
||||||
auto cfg = QJsonDocument(m_xrayConfig).toJson().toStdString();
|
if (!QFileInfo::exists(xrayExecPath())) {
|
||||||
if (auto err = amnezia_xray_configure(cfg.data()); err != 0) {
|
setLastError(ErrorCode::XrayExecutableMissing);
|
||||||
return ErrorCode::InternalError;
|
return lastError();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto err = amnezia_xray_start(); err != 0) {
|
#ifdef QT_DEBUG
|
||||||
return ErrorCode::NotImplementedError;
|
m_xrayCfgFile.setAutoRemove(false);
|
||||||
|
#endif
|
||||||
|
m_xrayCfgFile.open();
|
||||||
|
QString config = QJsonDocument(m_xrayConfig).toJson();
|
||||||
|
config.replace(m_remoteHost, m_remoteAddress);
|
||||||
|
m_xrayCfgFile.write(config.toUtf8());
|
||||||
|
m_xrayCfgFile.close();
|
||||||
|
|
||||||
|
QStringList args = QStringList() << "-c" << m_xrayCfgFile.fileName() << "-format=json";
|
||||||
|
|
||||||
|
qDebug().noquote() << "XrayProtocol::start()" << xrayExecPath() << args.join(" ");
|
||||||
|
|
||||||
|
m_xrayProcess.setProcessChannelMode(QProcess::MergedChannels);
|
||||||
|
m_xrayProcess.setProgram(xrayExecPath());
|
||||||
|
|
||||||
|
if (Utils::processIsRunning(Utils::executable("xray", false))) {
|
||||||
|
qDebug().noquote() << "kill previos xray";
|
||||||
|
Utils::killProcessByName(Utils::executable("xray", false));
|
||||||
}
|
}
|
||||||
|
|
||||||
setConnectionState(Vpn::ConnectionState::Connecting);
|
m_xrayProcess.setArguments(args);
|
||||||
|
|
||||||
return startTun2Sock();
|
connect(&m_xrayProcess, &QProcess::readyReadStandardOutput, this, [this]() {
|
||||||
|
#ifdef QT_DEBUG
|
||||||
|
qDebug().noquote() << "xray:" << m_xrayProcess.readAllStandardOutput();
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&m_xrayProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this,
|
||||||
|
[this](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||||
|
qDebug().noquote() << "XrayProtocol finished, exitCode, exitStatus" << exitCode << exitStatus;
|
||||||
|
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||||
|
if ((exitStatus != QProcess::NormalExit) || (exitCode != 0)) {
|
||||||
|
emit protocolError(amnezia::ErrorCode::XrayExecutableCrashed);
|
||||||
|
emit setConnectionState(Vpn::ConnectionState::Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
m_xrayProcess.start();
|
||||||
|
m_xrayProcess.waitForStarted();
|
||||||
|
|
||||||
|
if (m_xrayProcess.state() == QProcess::ProcessState::Running) {
|
||||||
|
setConnectionState(Vpn::ConnectionState::Connecting);
|
||||||
|
QThread::msleep(1000);
|
||||||
|
return startTun2Sock();
|
||||||
|
} else
|
||||||
|
return ErrorCode::XrayExecutableMissing;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode XrayProtocol::startTun2Sock()
|
ErrorCode XrayProtocol::startTun2Sock()
|
||||||
@@ -104,7 +126,9 @@ ErrorCode XrayProtocol::startTun2Sock()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (m_routeMode == Settings::RouteMode::VpnAllSites) {
|
if (m_routeMode == Settings::RouteMode::VpnAllSites) {
|
||||||
IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "1.0.0.0/8" << "2.0.0.0/7" << "4.0.0.0/6" << "8.0.0.0/5" << "16.0.0.0/4" << "32.0.0.0/3" << "64.0.0.0/2" << "128.0.0.0/1");
|
IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "0.0.0.0/1");
|
||||||
|
IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "128.0.0.0/1");
|
||||||
|
IpcClient::Interface()->routeAddList(m_routeGateway, QStringList() << m_remoteAddress);
|
||||||
}
|
}
|
||||||
IpcClient::Interface()->StopRoutingIpv6();
|
IpcClient::Interface()->StopRoutingIpv6();
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
@@ -147,9 +171,9 @@ void XrayProtocol::stop()
|
|||||||
IpcClient::Interface()->StartRoutingIpv6();
|
IpcClient::Interface()->StartRoutingIpv6();
|
||||||
#endif
|
#endif
|
||||||
qDebug() << "XrayProtocol::stop()";
|
qDebug() << "XrayProtocol::stop()";
|
||||||
|
m_xrayProcess.disconnect();
|
||||||
amnezia_xray_stop();
|
m_xrayProcess.kill();
|
||||||
|
m_xrayProcess.waitForFinished(3000);
|
||||||
if (m_t2sProcess) {
|
if (m_t2sProcess) {
|
||||||
m_t2sProcess->stop();
|
m_t2sProcess->stop();
|
||||||
}
|
}
|
||||||
@@ -157,6 +181,15 @@ void XrayProtocol::stop()
|
|||||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString XrayProtocol::xrayExecPath()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
return Utils::executable(QString("xray/xray"), true);
|
||||||
|
#else
|
||||||
|
return Utils::executable(QString("xray"), true);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void XrayProtocol::readXrayConfiguration(const QJsonObject &configuration)
|
void XrayProtocol::readXrayConfiguration(const QJsonObject &configuration)
|
||||||
{
|
{
|
||||||
m_configData = configuration;
|
m_configData = configuration;
|
||||||
@@ -172,20 +205,3 @@ void XrayProtocol::readXrayConfiguration(const QJsonObject &configuration)
|
|||||||
m_primaryDNS = configuration.value(amnezia::config_key::dns1).toString();
|
m_primaryDNS = configuration.value(amnezia::config_key::dns1).toString();
|
||||||
m_secondaryDNS = configuration.value(amnezia::config_key::dns2).toString();
|
m_secondaryDNS = configuration.value(amnezia::config_key::dns2).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void XrayProtocol::sockCallback(uintptr_t fd)
|
|
||||||
{
|
|
||||||
// #ifdef Q_OS_DARWIN
|
|
||||||
// const int iface = if_nametoindex("en0");
|
|
||||||
// if (iface > 0)
|
|
||||||
// {
|
|
||||||
// setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface, sizeof(iface));
|
|
||||||
// setsockopt(fd, IPPROTO_IPV6, IPV6_BOUND_IF, &iface, sizeof(iface));
|
|
||||||
// }
|
|
||||||
// #endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void XrayProtocol::logHandler(char* str)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,10 +3,9 @@
|
|||||||
|
|
||||||
#include "QProcess"
|
#include "QProcess"
|
||||||
|
|
||||||
#include "protocols/vpnprotocol.h"
|
#include "containers/containers_defs.h"
|
||||||
#include "rep_ipc_process_tun2socks_replica.h"
|
#include "openvpnprotocol.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
class XrayProtocol : public VpnProtocol
|
class XrayProtocol : public VpnProtocol
|
||||||
{
|
{
|
||||||
@@ -25,16 +24,10 @@ protected:
|
|||||||
QJsonObject m_xrayConfig;
|
QJsonObject m_xrayConfig;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void ctxSockCallback(uintptr_t fd, void* ctx) {
|
static QString xrayExecPath();
|
||||||
reinterpret_cast<XrayProtocol*>(ctx)->sockCallback(fd);
|
static QString tun2SocksExecPath();
|
||||||
}
|
|
||||||
static void ctxLogHandler(char* str, void* ctx) {
|
|
||||||
reinterpret_cast<XrayProtocol*>(ctx)->logHandler(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sockCallback(uintptr_t fd);
|
|
||||||
void logHandler(char* str);
|
|
||||||
|
|
||||||
|
private:
|
||||||
int m_localPort;
|
int m_localPort;
|
||||||
QString m_remoteHost;
|
QString m_remoteHost;
|
||||||
QString m_remoteAddress;
|
QString m_remoteAddress;
|
||||||
|
|||||||
@@ -127,7 +127,7 @@
|
|||||||
<file>ui/qml/Components/SelectLanguageDrawer.qml</file>
|
<file>ui/qml/Components/SelectLanguageDrawer.qml</file>
|
||||||
<file>ui/qml/Components/ServersListView.qml</file>
|
<file>ui/qml/Components/ServersListView.qml</file>
|
||||||
<file>ui/qml/Components/SettingsContainersListView.qml</file>
|
<file>ui/qml/Components/SettingsContainersListView.qml</file>
|
||||||
<file>ui/qml/Components/ShareConnectionDrawer.qml</file>
|
|
||||||
<file>ui/qml/Components/TransportProtoSelector.qml</file>
|
<file>ui/qml/Components/TransportProtoSelector.qml</file>
|
||||||
<file>ui/qml/Components/AddSitePanel.qml</file>
|
<file>ui/qml/Components/AddSitePanel.qml</file>
|
||||||
<file>ui/qml/Config/GlobalConfig.qml</file>
|
<file>ui/qml/Config/GlobalConfig.qml</file>
|
||||||
@@ -228,6 +228,8 @@
|
|||||||
<file>ui/qml/Pages2/PageSetupWizardViewConfig.qml</file>
|
<file>ui/qml/Pages2/PageSetupWizardViewConfig.qml</file>
|
||||||
<file>ui/qml/Pages2/PageShare.qml</file>
|
<file>ui/qml/Pages2/PageShare.qml</file>
|
||||||
<file>ui/qml/Pages2/PageShareFullAccess.qml</file>
|
<file>ui/qml/Pages2/PageShareFullAccess.qml</file>
|
||||||
|
<file>ui/qml/Pages2/PageShareConnection.qml</file>
|
||||||
|
<file>ui/qml/Pages2/PageTransferConfigViaQR.qml</file>
|
||||||
<file>ui/qml/Pages2/PageStart.qml</file>
|
<file>ui/qml/Pages2/PageStart.qml</file>
|
||||||
<file>ui/qml/Components/RenameServerDrawer.qml</file>
|
<file>ui/qml/Components/RenameServerDrawer.qml</file>
|
||||||
<file>ui/qml/Controls2/ListViewType.qml</file>
|
<file>ui/qml/Controls2/ListViewType.qml</file>
|
||||||
@@ -235,11 +237,16 @@
|
|||||||
<file>ui/qml/Pages2/PageSettingsApiInstructions.qml</file>
|
<file>ui/qml/Pages2/PageSettingsApiInstructions.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSettingsApiNativeConfigs.qml</file>
|
<file>ui/qml/Pages2/PageSettingsApiNativeConfigs.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSettingsApiDevices.qml</file>
|
<file>ui/qml/Pages2/PageSettingsApiDevices.qml</file>
|
||||||
|
<file>ui/qml/Pages2/PageSettingsApiDevicesLimit.qml</file>
|
||||||
|
<file>ui/qml/Pages2/PageSettingsApiAddDeviceScan.qml</file>
|
||||||
|
<file>ui/qml/Pages2/PageSettingsApiAddDeviceConfirm.qml</file>
|
||||||
<file>images/controls/monitor.svg</file>
|
<file>images/controls/monitor.svg</file>
|
||||||
|
<file>images/controls/monitor-with-phone.svg</file>
|
||||||
<file>ui/qml/Components/ApiPremV1MigrationDrawer.qml</file>
|
<file>ui/qml/Components/ApiPremV1MigrationDrawer.qml</file>
|
||||||
<file>ui/qml/Components/ApiPremV1SubListDrawer.qml</file>
|
<file>ui/qml/Components/ApiPremV1SubListDrawer.qml</file>
|
||||||
<file>ui/qml/Components/OtpCodeDrawer.qml</file>
|
<file>ui/qml/Components/OtpCodeDrawer.qml</file>
|
||||||
<file>ui/qml/Components/AwgTextField.qml</file>
|
<file>ui/qml/Components/AwgTextField.qml</file>
|
||||||
|
<file>ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/countriesFlags">
|
<qresource prefix="/countriesFlags">
|
||||||
<file>images/flagKit/ZW.svg</file>
|
<file>images/flagKit/ZW.svg</file>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
FROM alpine:3.15
|
FROM alpine:3.15
|
||||||
LABEL maintainer="AmneziaVPN"
|
LABEL maintainer="AmneziaVPN"
|
||||||
|
|
||||||
ARG XRAY_RELEASE="v1.8.6"
|
ARG XRAY_RELEASE="v25.8.3"
|
||||||
|
|
||||||
RUN apk add --no-cache curl unzip bash openssl netcat-openbsd dumb-init rng-tools xz
|
RUN apk add --no-cache curl unzip bash openssl netcat-openbsd dumb-init rng-tools xz
|
||||||
RUN apk --update upgrade --no-cache
|
RUN apk --update upgrade --no-cache
|
||||||
|
|||||||
@@ -248,10 +248,10 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
|
|||||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||||
serverCountryCode,
|
serverCountryCode,
|
||||||
apiConfigObject.value(configKey::serviceType).toString(),
|
apiConfigObject.value(configKey::serviceType).toString(),
|
||||||
m_apiServicesModel->getSelectedServiceProtocol(),
|
configKey::awg, // apiConfigObject.value(configKey::serviceProtocol).toString(),
|
||||||
serverConfigObject.value(configKey::authData).toObject() };
|
serverConfigObject.value(configKey::authData).toObject() };
|
||||||
|
|
||||||
QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
|
QString protocol = gatewayRequestData.serviceProtocol;
|
||||||
ProtocolData protocolData = generateProtocolData(protocol);
|
ProtocolData protocolData = generateProtocolData(protocol);
|
||||||
|
|
||||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||||
@@ -283,7 +283,7 @@ bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
|
|||||||
apiConfigObject.value(configKey::userCountryCode).toString(),
|
apiConfigObject.value(configKey::userCountryCode).toString(),
|
||||||
serverCountryCode,
|
serverCountryCode,
|
||||||
apiConfigObject.value(configKey::serviceType).toString(),
|
apiConfigObject.value(configKey::serviceType).toString(),
|
||||||
m_apiServicesModel->getSelectedServiceProtocol(),
|
configKey::awg, // apiConfigObject.value(configKey::serviceProtocol).toString(),
|
||||||
serverConfigObject.value(configKey::authData).toObject() };
|
serverConfigObject.value(configKey::authData).toObject() };
|
||||||
|
|
||||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "connectionController.h"
|
#include "connectionController.h"
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#else
|
#else
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
@@ -32,8 +32,9 @@ ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &s
|
|||||||
|
|
||||||
void ConnectionController::openConnection()
|
void ConnectionController::openConnection()
|
||||||
{
|
{
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true)) {
|
if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true))
|
||||||
|
{
|
||||||
emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning);
|
emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,12 @@
|
|||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
#include <QQuickWindow>
|
#include <QQuickWindow>
|
||||||
|
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
Logger logger("FocusController");
|
||||||
|
}
|
||||||
|
|
||||||
FocusController::FocusController(QQmlApplicationEngine *engine, QObject *parent)
|
FocusController::FocusController(QQmlApplicationEngine *engine, QObject *parent)
|
||||||
: QObject { parent },
|
: QObject { parent },
|
||||||
m_engine { engine },
|
m_engine { engine },
|
||||||
@@ -85,7 +91,7 @@ void FocusController::dropRootObject(QObject *object)
|
|||||||
dropListView();
|
dropListView();
|
||||||
setFocusOnDefaultItem();
|
setFocusOnDefaultItem();
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "===>> TRY TO DROP WRONG ROOT OBJECT: " << m_rootObjects.top() << " SHOULD BE: " << object;
|
logger.warning() << "TRY TO DROP WRONG ROOT OBJECT: " << m_rootObjects.top() << " SHOULD BE: " << object;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +107,7 @@ void FocusController::reload(Direction direction)
|
|||||||
QObject *rootObject = (m_rootObjects.empty() ? m_engine->rootObjects().value(0) : m_rootObjects.top());
|
QObject *rootObject = (m_rootObjects.empty() ? m_engine->rootObjects().value(0) : m_rootObjects.top());
|
||||||
|
|
||||||
if (!rootObject) {
|
if (!rootObject) {
|
||||||
qCritical() << "No ROOT OBJECT found!";
|
logger.error() << "No ROOT OBJECT found!";
|
||||||
resetRootObject();
|
resetRootObject();
|
||||||
dropListView();
|
dropListView();
|
||||||
return;
|
return;
|
||||||
@@ -113,7 +119,7 @@ void FocusController::reload(Direction direction)
|
|||||||
direction == Direction::Forward ? FocusControl::isLess : FocusControl::isMore);
|
direction == Direction::Forward ? FocusControl::isLess : FocusControl::isMore);
|
||||||
|
|
||||||
if (m_focusChain.empty()) {
|
if (m_focusChain.empty()) {
|
||||||
qWarning() << "Focus chain is empty!";
|
logger.warning() << "Focus chain is empty!";
|
||||||
resetRootObject();
|
resetRootObject();
|
||||||
dropListView();
|
dropListView();
|
||||||
return;
|
return;
|
||||||
@@ -131,7 +137,7 @@ void FocusController::nextItem(Direction direction)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_focusChain.empty()) {
|
if (m_focusChain.empty()) {
|
||||||
qWarning() << "There are no items to navigate";
|
logger.warning() << "There are no items to navigate";
|
||||||
setFocusOnDefaultItem();
|
setFocusOnDefaultItem();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -149,7 +155,7 @@ void FocusController::nextItem(Direction direction)
|
|||||||
const auto focusedItem = qobject_cast<QQuickItem *>(m_focusChain.at(focusedItemIndex));
|
const auto focusedItem = qobject_cast<QQuickItem *>(m_focusChain.at(focusedItemIndex));
|
||||||
|
|
||||||
if (focusedItem == nullptr) {
|
if (focusedItem == nullptr) {
|
||||||
qWarning() << "Failed to get item to focus on. Setting focus on default";
|
logger.warning() << "Failed to get item to focus on. Setting focus on default";
|
||||||
setFocusOnDefaultItem();
|
setFocusOnDefaultItem();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
#include "platforms/android/android_controller.h"
|
#include "platforms/android/android_controller.h"
|
||||||
#endif
|
#endif
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -584,7 +584,26 @@ bool ImportController::decodeQrCode(const QString &code)
|
|||||||
mInstance->m_totalQrCodeChunksCount = 0;
|
mInstance->m_totalQrCodeChunksCount = 0;
|
||||||
mInstance->m_receivedQrCodeChunksCount = 0;
|
mInstance->m_receivedQrCodeChunksCount = 0;
|
||||||
}
|
}
|
||||||
return mInstance->parseQrCodeChunk(code);
|
|
||||||
|
// First, try transferController QR (plain JSON payload).
|
||||||
|
QJsonParseError err;
|
||||||
|
const QJsonDocument doc = QJsonDocument::fromJson(code.toUtf8(), &err);
|
||||||
|
if (err.error == QJsonParseError::NoError && doc.isObject()) {
|
||||||
|
const QJsonObject obj = doc.object();
|
||||||
|
if (obj.contains(QStringLiteral("gw"))
|
||||||
|
&& (obj.contains(QStringLiteral("uuid")) || obj.contains(QStringLiteral("u")))) {
|
||||||
|
// это наш QR для передачи устройства
|
||||||
|
mInstance->m_isQrCodeProcessed = false;
|
||||||
|
emit mInstance->transferQrDecoded(code);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mInstance->parseQrCodeChunk(code)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -595,7 +614,7 @@ void ImportController::startDecodingQr()
|
|||||||
m_totalQrCodeChunksCount = 0;
|
m_totalQrCodeChunksCount = 0;
|
||||||
m_receivedQrCodeChunksCount = 0;
|
m_receivedQrCodeChunksCount = 0;
|
||||||
|
|
||||||
#if defined Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
m_isQrCodeProcessed = true;
|
m_isQrCodeProcessed = true;
|
||||||
#endif
|
#endif
|
||||||
#if defined Q_OS_ANDROID
|
#if defined Q_OS_ANDROID
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ signals:
|
|||||||
void importErrorOccurred(ErrorCode errorCode, bool goToPageHome);
|
void importErrorOccurred(ErrorCode errorCode, bool goToPageHome);
|
||||||
|
|
||||||
void qrDecodingFinished();
|
void qrDecodingFinished();
|
||||||
|
void transferQrDecoded(const QString &code);
|
||||||
|
|
||||||
void restoreAppConfig(const QByteArray &data);
|
void restoreAppConfig(const QByteArray &data);
|
||||||
|
|
||||||
|
|||||||
@@ -403,9 +403,19 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
|
|||||||
QJsonObject config;
|
QJsonObject config;
|
||||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
Proto mainProto = ContainerProps::defaultProtocol(container);
|
||||||
const auto &protocols = ContainerProps::protocolsForContainer(container);
|
const auto &protocols = ContainerProps::protocolsForContainer(container);
|
||||||
|
|
||||||
for (const auto &protocol : protocols) {
|
for (const auto &protocol : protocols) {
|
||||||
QJsonObject containerConfig;
|
QJsonObject containerConfig;
|
||||||
if (protocol == mainProto) {
|
|
||||||
|
// for Multiprotocols (OpenVPN over SS, OpenVPN over Cloak)
|
||||||
|
bool shouldProcessProtocol = false;
|
||||||
|
if (container == DockerContainer::ShadowSocks || container == DockerContainer::Cloak) {
|
||||||
|
shouldProcessProtocol = true;
|
||||||
|
} else {
|
||||||
|
shouldProcessProtocol = (protocol == mainProto);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldProcessProtocol) {
|
||||||
containerConfig.insert(config_key::port, port);
|
containerConfig.insert(config_key::port, port);
|
||||||
containerConfig.insert(config_key::transport_proto, transportProto);
|
containerConfig.insert(config_key::transport_proto, transportProto);
|
||||||
|
|
||||||
@@ -550,14 +560,97 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
|
|||||||
qDebug() << siteName;
|
qDebug() << siteName;
|
||||||
|
|
||||||
containerConfig.insert(config_key::site, siteName);
|
containerConfig.insert(config_key::site, siteName);
|
||||||
|
} else if (protocol == Proto::OpenVpn) {
|
||||||
|
QString serverConfig = serverController->getTextFileFromContainer(container, credentials,
|
||||||
|
protocols::openvpn::serverConfigPath, errorCode);
|
||||||
|
|
||||||
|
QMap<QString, QString> serverConfigMap;
|
||||||
|
auto serverConfigLines = serverConfig.split("\n");
|
||||||
|
for (auto &line : serverConfigLines) {
|
||||||
|
auto trimmedLine = line.trimmed();
|
||||||
|
if (trimmedLine.startsWith("#") || trimmedLine.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
QStringList parts = trimmedLine.split(" ");
|
||||||
|
if (parts.count() >= 2) {
|
||||||
|
QString key = parts[0];
|
||||||
|
QString value = parts.mid(1).join(" ");
|
||||||
|
serverConfigMap.insert(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString serverValue = serverConfigMap.value("server");
|
||||||
|
|
||||||
|
if (!serverValue.isEmpty()) {
|
||||||
|
QStringList serverParts = serverValue.split(" ");
|
||||||
|
if (serverParts.count() >= 1) {
|
||||||
|
containerConfig[config_key::subnet_address] = serverParts[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ncpDisable = serverConfig.contains("ncp-disable");
|
||||||
|
containerConfig[config_key::ncp_disable] = ncpDisable;
|
||||||
|
|
||||||
|
bool tlsAuth = serverConfig.contains("tls-auth");
|
||||||
|
containerConfig[config_key::tls_auth] = tlsAuth;
|
||||||
|
|
||||||
|
bool blockOutsideDns = serverConfig.contains("block-outside-dns");
|
||||||
|
|
||||||
|
containerConfig[config_key::block_outside_dns] = blockOutsideDns;
|
||||||
|
|
||||||
|
QString cipher = serverConfigMap.value("cipher");
|
||||||
|
if (!cipher.isEmpty()) {
|
||||||
|
containerConfig[config_key::cipher] = cipher;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString hash = serverConfigMap.value("auth");
|
||||||
|
if (!hash.isEmpty()) {
|
||||||
|
containerConfig[config_key::hash] = hash;
|
||||||
|
}
|
||||||
|
} else if (protocol == Proto::Cloak) {
|
||||||
|
QString cloakConfig = serverController->getTextFileFromContainer(container, credentials,
|
||||||
|
"/opt/amnezia/cloak/ck-config.json", errorCode);
|
||||||
|
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(cloakConfig.toUtf8());
|
||||||
|
|
||||||
|
if (!doc.isNull() && doc.isObject()) {
|
||||||
|
QJsonObject cloakConfigObj = doc.object();
|
||||||
|
|
||||||
|
QString site = cloakConfigObj.value("RedirAddr").toString();
|
||||||
|
if (!site.isEmpty()) {
|
||||||
|
containerConfig[config_key::site] = site;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "Failed to parse main loop Cloak JSON config";
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (protocol == Proto::ShadowSocks) {
|
||||||
|
QString shadowsocksConfig = serverController->getTextFileFromContainer(container, credentials,
|
||||||
|
"/opt/amnezia/shadowsocks/ss-config.json", errorCode);
|
||||||
|
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(shadowsocksConfig.toUtf8());
|
||||||
|
|
||||||
|
if (!doc.isNull() && doc.isObject()) {
|
||||||
|
QJsonObject ssConfigObj = doc.object();
|
||||||
|
QString cipher = ssConfigObj.value("method").toString();
|
||||||
|
if (!cipher.isEmpty()) {
|
||||||
|
containerConfig[config_key::cipher] = cipher;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "Failed to parse main loop Shadowsocks JSON config";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
config.insert(config_key::container, ContainerProps::containerToString(container));
|
config.insert(config_key::container, ContainerProps::containerToString(container));
|
||||||
}
|
}
|
||||||
config.insert(ProtocolProps::protoToString(protocol), containerConfig);
|
if (shouldProcessProtocol) {
|
||||||
|
config.insert(ProtocolProps::protoToString(protocol), containerConfig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
installedContainers.insert(container, config);
|
installedContainers.insert(container, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
const static QRegularExpression torOrDnsRegExp("(amnezia-(?:torwebsite|dns)).*?([0-9]*)/(udp|tcp).*");
|
const static QRegularExpression torOrDnsRegExp("(amnezia-(?:torwebsite|dns)).*?([0-9]*)/(udp|tcp).*");
|
||||||
QRegularExpressionMatch torOrDnsRegMatch = torOrDnsRegExp.match(containerInfo);
|
QRegularExpressionMatch torOrDnsRegMatch = torOrDnsRegExp.match(containerInfo);
|
||||||
if (torOrDnsRegMatch.hasMatch()) {
|
if (torOrDnsRegMatch.hasMatch()) {
|
||||||
@@ -721,6 +814,8 @@ void InstallController::clearCachedProfile(QSharedPointer<ServerController> serv
|
|||||||
m_clientManagementModel->revokeClient(containerConfig, container, serverCredentials, serverIndex, serverController);
|
m_clientManagementModel->revokeClient(containerConfig, container, serverCredentials, serverIndex, serverController);
|
||||||
|
|
||||||
emit cachedProfileCleared(tr("%1 cached profile cleared").arg(ContainerProps::containerHumanNames().value(container)));
|
emit cachedProfileCleared(tr("%1 cached profile cleared").arg(ContainerProps::containerHumanNames().value(container)));
|
||||||
|
QJsonObject updatedConfig = m_settings->containerConfig(serverIndex, container);
|
||||||
|
emit profileCleared(updatedConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
QRegularExpression InstallController::ipAddressPortRegExp()
|
QRegularExpression InstallController::ipAddressPortRegExp()
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ signals:
|
|||||||
|
|
||||||
void noInstalledContainers();
|
void noInstalledContainers();
|
||||||
|
|
||||||
|
void profileCleared(const QJsonObject &config);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void installServer(const DockerContainer container, const QMap<DockerContainer, QJsonObject> &installedContainers,
|
void installServer(const DockerContainer container, const QMap<DockerContainer, QJsonObject> &installedContainers,
|
||||||
const ServerCredentials &serverCredentials, const QSharedPointer<ServerController> &serverController,
|
const ServerCredentials &serverCredentials, const QSharedPointer<ServerController> &serverController,
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ void ListViewFocusController::viewAtCurrentIndex() const
|
|||||||
}
|
}
|
||||||
case Section::Delegate: {
|
case Section::Delegate: {
|
||||||
QMetaObject::invokeMethod(m_listView, "positionViewAtIndex", Q_ARG(int, m_delegateIndex), // Index
|
QMetaObject::invokeMethod(m_listView, "positionViewAtIndex", Q_ARG(int, m_delegateIndex), // Index
|
||||||
Q_ARG(int, 2)); // PositionMode (0 = Visible)
|
Q_ARG(int, 6)); // PositionMode (0 = Beginning; 1 = Center; 2 = End; 3 = Visible; 4 = Contain; 5 = SnapPosition)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Section::Footer: {
|
case Section::Footer: {
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
#include "pageController.h"
|
#include "pageController.h"
|
||||||
#include "utils/converter.h"
|
#include "utils/converter.h"
|
||||||
#include "core/errorstrings.h"
|
#include "core/errorstrings.h"
|
||||||
|
#if defined(MACOS_NE)
|
||||||
|
#include "platforms/ios/ios_controller.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#else
|
#else
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
@@ -25,8 +28,12 @@ PageController::PageController(const QSharedPointer<ServersModel> &serversModel,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined Q_OS_MACX
|
#if defined Q_OS_MACX
|
||||||
connect(this, &PageController::raiseMainWindow, []() { setDockIconVisible(true); });
|
connect(this, &PageController::raiseMainWindow, []() {
|
||||||
connect(this, &PageController::hideMainWindow, []() { setDockIconVisible(false); });
|
setDockIconVisible(true);
|
||||||
|
});
|
||||||
|
connect(this, &PageController::hideMainWindow, []() {
|
||||||
|
setDockIconVisible(false);
|
||||||
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
connect(this, qOverload<ErrorCode>(&PageController::showErrorMessage), this, &PageController::onShowErrorMessage);
|
connect(this, qOverload<ErrorCode>(&PageController::showErrorMessage), this, &PageController::onShowErrorMessage);
|
||||||
@@ -56,14 +63,11 @@ QString PageController::getPagePath(PageLoader::PageEnum page)
|
|||||||
|
|
||||||
void PageController::closeWindow()
|
void PageController::closeWindow()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_ANDROID
|
// On mobile platforms, quit app on close; on desktop, just hide window
|
||||||
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||||
qApp->quit();
|
qApp->quit();
|
||||||
#else
|
#else
|
||||||
if (m_serversModel->getServersCount() == 0) {
|
emit hideMainWindow();
|
||||||
qApp->quit();
|
|
||||||
} else {
|
|
||||||
emit hideMainWindow();
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +118,7 @@ void PageController::showOnStartup()
|
|||||||
} else {
|
} else {
|
||||||
#if defined(Q_OS_WIN) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
#if defined(Q_OS_WIN) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||||
emit hideMainWindow();
|
emit hideMainWindow();
|
||||||
#elif defined Q_OS_MACX
|
#elif defined(Q_OS_MACX)
|
||||||
setDockIconVisible(false);
|
setDockIconVisible(false);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ namespace PageLoader
|
|||||||
PageSettingsApiInstructions,
|
PageSettingsApiInstructions,
|
||||||
PageSettingsApiNativeConfigs,
|
PageSettingsApiNativeConfigs,
|
||||||
PageSettingsApiDevices,
|
PageSettingsApiDevices,
|
||||||
|
PageSettingsApiDevicesLimit,
|
||||||
|
PageSettingsApiAddDeviceConfirm,
|
||||||
|
PageSettingsApiAddDeviceScan,
|
||||||
|
PageSettingsApiSubscriptionKey,
|
||||||
PageSettingsKillSwitchExceptions,
|
PageSettingsKillSwitchExceptions,
|
||||||
|
|
||||||
PageServiceSftpSettings,
|
PageServiceSftpSettings,
|
||||||
@@ -71,6 +75,8 @@ namespace PageLoader
|
|||||||
PageProtocolAwgClientSettings,
|
PageProtocolAwgClientSettings,
|
||||||
|
|
||||||
PageShareFullAccess,
|
PageShareFullAccess,
|
||||||
|
PageShareConnection,
|
||||||
|
PageTransferConfigViaQR,
|
||||||
|
|
||||||
PageDevMenu
|
PageDevMenu
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#include "platforms/android/android_controller.h"
|
#include "platforms/android/android_controller.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <AmneziaVPN-Swift.h>
|
#include <AmneziaVPN-Swift.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -35,6 +35,23 @@ SettingsController::SettingsController(const QSharedPointer<ServersModel> &serve
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString getPlatformName()
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_WINDOWS)
|
||||||
|
return "Windows";
|
||||||
|
#elif defined(Q_OS_ANDROID)
|
||||||
|
return "Android";
|
||||||
|
#elif defined(Q_OS_LINUX)
|
||||||
|
return "Linux";
|
||||||
|
#elif defined(Q_OS_MACX)
|
||||||
|
return "MacOS";
|
||||||
|
#elif defined(Q_OS_IOS)
|
||||||
|
return "iOS";
|
||||||
|
#else
|
||||||
|
return "Unknown";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsController::toggleAmneziaDns(bool enable)
|
void SettingsController::toggleAmneziaDns(bool enable)
|
||||||
{
|
{
|
||||||
m_settings->setUseAmneziaDns(enable);
|
m_settings->setUseAmneziaDns(enable);
|
||||||
@@ -76,7 +93,7 @@ bool SettingsController::isLoggingEnabled()
|
|||||||
void SettingsController::toggleLogging(bool enable)
|
void SettingsController::toggleLogging(bool enable)
|
||||||
{
|
{
|
||||||
m_settings->setSaveLogs(enable);
|
m_settings->setSaveLogs(enable);
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS)
|
||||||
AmneziaVPN::toggleLogging(enable);
|
AmneziaVPN::toggleLogging(enable);
|
||||||
#endif
|
#endif
|
||||||
if (enable == true) {
|
if (enable == true) {
|
||||||
@@ -130,15 +147,22 @@ void SettingsController::backupAppConfig(const QString &fileName)
|
|||||||
QJsonDocument doc = QJsonDocument::fromJson(data);
|
QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||||
QJsonObject config = doc.object();
|
QJsonObject config = doc.object();
|
||||||
|
|
||||||
|
config["AppPlatform"] = getPlatformName();
|
||||||
config["Conf/autoStart"] = Autostart::isAutostart();
|
config["Conf/autoStart"] = Autostart::isAutostart();
|
||||||
|
config["Conf/killSwitchEnabled"] = isKillSwitchEnabled();
|
||||||
|
config["Conf/strictKillSwitchEnabled"] = isStrictKillSwitchEnabled();
|
||||||
|
|
||||||
SystemController::saveFile(fileName, QJsonDocument(config).toJson());
|
SystemController::saveFile(fileName, QJsonDocument(config).toJson());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsController::restoreAppConfig(const QString &fileName)
|
void SettingsController::restoreAppConfig(const QString &fileName)
|
||||||
{
|
{
|
||||||
QByteArray data;
|
QFile file(fileName);
|
||||||
SystemController::readFile(fileName, data);
|
|
||||||
|
file.open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
QByteArray data = file.readAll();
|
||||||
|
|
||||||
restoreAppConfigFromData(data);
|
restoreAppConfigFromData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,21 +179,41 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
|
|||||||
}
|
}
|
||||||
toggleAutoStart(autoStart);
|
toggleAutoStart(autoStart);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_serversModel->resetModel();
|
m_serversModel->resetModel();
|
||||||
m_languageModel->changeLanguage(
|
m_languageModel->changeLanguage(
|
||||||
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
|
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
|
||||||
|
|
||||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
|
#if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
|
||||||
int appSplitTunnelingRouteMode = newConfigData.value("Conf/appsRouteMode").toInt();
|
int appSplitTunnelingRouteMode = newConfigData.value("Conf/appsRouteMode").toInt();
|
||||||
bool appSplittunnelingEnabled = newConfigData.value("Conf/appsSplitTunnelingEnabled").toBool();
|
bool appSplittunnelingEnabled = newConfigData.value("Conf/appsSplitTunnelingEnabled").toString().toLower() == "true";
|
||||||
m_appSplitTunnelingModel->setRouteMode(appSplitTunnelingRouteMode);
|
m_appSplitTunnelingModel->setRouteMode(appSplitTunnelingRouteMode);
|
||||||
|
|
||||||
|
#if defined(Q_OS_WINDOWS)
|
||||||
|
m_appSplitTunnelingModel->setRouteMode(static_cast<int>(Settings::AppsRouteMode::VpnAllExceptApps));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (newConfigData.contains("AppPlatform")) { //if backup is from a new version
|
||||||
|
if (newConfigData.value("AppPlatform").toString() != getPlatformName()) {
|
||||||
|
m_appSplitTunnelingModel->clearAppsList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_appSplitTunnelingModel->toggleSplitTunneling(appSplittunnelingEnabled);
|
m_appSplitTunnelingModel->toggleSplitTunneling(appSplittunnelingEnabled);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt();
|
int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt();
|
||||||
bool siteSplittunnelingEnabled = newConfigData.value("Conf/sitesSplitTunnelingEnabled").toBool();
|
bool siteSplittunnelingEnabled = newConfigData.value("Conf/sitesSplitTunnelingEnabled").toString().toLower() == "true";
|
||||||
m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode);
|
m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode);
|
||||||
m_sitesModel->toggleSplitTunneling(siteSplittunnelingEnabled);
|
m_sitesModel->toggleSplitTunneling(siteSplittunnelingEnabled);
|
||||||
|
|
||||||
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||||
|
m_settings->setAutoConnect(false);
|
||||||
|
m_settings->setStartMinimized(false);
|
||||||
|
m_settings->setKillSwitchEnabled(false);
|
||||||
|
m_settings->setStrictKillSwitchEnabled(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
emit restoreBackupFinished();
|
emit restoreBackupFinished();
|
||||||
} else {
|
} else {
|
||||||
emit changeSettingsErrorOccurred(tr("Backup file is corrupted"));
|
emit changeSettingsErrorOccurred(tr("Backup file is corrupted"));
|
||||||
@@ -185,8 +229,7 @@ void SettingsController::clearSettings()
|
|||||||
{
|
{
|
||||||
m_settings->clearSettings();
|
m_settings->clearSettings();
|
||||||
m_serversModel->resetModel();
|
m_serversModel->resetModel();
|
||||||
m_languageModel->changeLanguage(
|
m_languageModel->changeLanguage(m_languageModel->getSystemLanguageEnum());
|
||||||
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
|
|
||||||
|
|
||||||
m_sitesModel->setRouteMode(Settings::RouteMode::VpnOnlyForwardSites);
|
m_sitesModel->setRouteMode(Settings::RouteMode::VpnOnlyForwardSites);
|
||||||
m_sitesModel->toggleSplitTunneling(false);
|
m_sitesModel->toggleSplitTunneling(false);
|
||||||
@@ -198,7 +241,7 @@ void SettingsController::clearSettings()
|
|||||||
|
|
||||||
emit changeSettingsFinished(tr("All settings have been reset to default values"));
|
emit changeSettingsFinished(tr("All settings have been reset to default values"));
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
AmneziaVPN::clearSettings();
|
AmneziaVPN::clearSettings();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,6 +136,8 @@ private:
|
|||||||
|
|
||||||
QString m_appVersion;
|
QString m_appVersion;
|
||||||
|
|
||||||
|
QString getPlatform();
|
||||||
|
|
||||||
QDateTime m_loggingDisableDate;
|
QDateTime m_loggingDisableDate;
|
||||||
|
|
||||||
bool m_isDevModeEnabled = false;
|
bool m_isDevModeEnabled = false;
|
||||||
|
|||||||
@@ -78,6 +78,13 @@ void SitesController::removeSite(int index)
|
|||||||
emit finished(tr("Site removed: %1").arg(hostname));
|
emit finished(tr("Site removed: %1").arg(hostname));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SitesController::removeSites()
|
||||||
|
{
|
||||||
|
m_sitesModel->removeSites();
|
||||||
|
|
||||||
|
emit finished(tr("Site list cleared!"));
|
||||||
|
}
|
||||||
|
|
||||||
void SitesController::importSites(const QString &fileName, bool replaceExisting)
|
void SitesController::importSites(const QString &fileName, bool replaceExisting)
|
||||||
{
|
{
|
||||||
QByteArray jsonData;
|
QByteArray jsonData;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public slots:
|
|||||||
void addSite(QString hostname);
|
void addSite(QString hostname);
|
||||||
void removeSite(int index);
|
void removeSite(int index);
|
||||||
|
|
||||||
|
void removeSites();
|
||||||
void importSites(const QString &fileName, bool replaceExisting);
|
void importSites(const QString &fileName, bool replaceExisting);
|
||||||
void exportSites(const QString &fileName);
|
void exportSites(const QString &fileName);
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
#include "platforms/android/android_controller.h"
|
#include "platforms/android/android_controller.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include "platforms/ios/ios_controller.h"
|
#include "platforms/ios/ios_controller.h"
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -58,8 +58,10 @@ void SystemController::saveFile(const QString &fileName, const QString &data)
|
|||||||
const auto url = fi.absoluteDir().absolutePath();
|
const auto url = fi.absoluteDir().absolutePath();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef MACOS_NE
|
||||||
QDesktopServices::openUrl(url);
|
QDesktopServices::openUrl(url);
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemController::readFile(const QString &fileName, QByteArray &data)
|
bool SystemController::readFile(const QString &fileName, QByteArray &data)
|
||||||
|
|||||||
@@ -0,0 +1,344 @@
|
|||||||
|
#include "transferController.h"
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QJsonParseError>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <qeventloop.h>
|
||||||
|
#include <QNetworkProxyFactory>
|
||||||
|
#include <QNetworkProxyQuery>
|
||||||
|
#include <QUrl>
|
||||||
|
#include "core/api/apiUtils.h"
|
||||||
|
#include "core/qrCodeUtils.h"
|
||||||
|
|
||||||
|
#include "amnezia_application.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "ui/models/servers_model.h"
|
||||||
|
#include "ui/controllers/exportController.h"
|
||||||
|
#include "ui/controllers/importController.h"
|
||||||
|
#include "core/api/apiDefs.h"
|
||||||
|
#include "core/controllers/gatewayController.h"
|
||||||
|
#include "core/errorstrings.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void logSystemProxiesForUrl(const QString &urlStr)
|
||||||
|
{
|
||||||
|
const QUrl url(urlStr);
|
||||||
|
const QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery(QNetworkProxyQuery(url));
|
||||||
|
QStringList proxyDesc;
|
||||||
|
proxyDesc.reserve(proxies.size());
|
||||||
|
for (const auto &p : proxies) {
|
||||||
|
proxyDesc << QStringLiteral("%1 %2:%3")
|
||||||
|
.arg(p.type() == QNetworkProxy::NoProxy ? QStringLiteral("NoProxy")
|
||||||
|
: p.type() == QNetworkProxy::HttpProxy ? QStringLiteral("HttpProxy")
|
||||||
|
: p.type() == QNetworkProxy::Socks5Proxy ? QStringLiteral("Socks5Proxy")
|
||||||
|
: QStringLiteral("Proxy"))
|
||||||
|
.arg(p.hostName())
|
||||||
|
.arg(p.port());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TransferController::TransferController(const std::shared_ptr<Settings> &settings,
|
||||||
|
const QSharedPointer<ServersModel> &serversModel,
|
||||||
|
ExportController *exportController,
|
||||||
|
QObject *parent)
|
||||||
|
: QObject(parent), m_settings(settings), m_serversModel(serversModel), m_exportController(exportController)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransferController::handleImportControllerDestroyed()
|
||||||
|
{
|
||||||
|
m_importController = nullptr;
|
||||||
|
stopWaitForConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
TransferController::~TransferController() {
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TransferController::buildQrPayloadJson(const QString &gatewayUrl, const QString &uuid) const
|
||||||
|
{
|
||||||
|
QJsonObject obj;
|
||||||
|
obj["gw"] = gatewayUrl;
|
||||||
|
obj["uuid"] = uuid;
|
||||||
|
// Used on the sender side for human-friendly notifications (same style as "Active Devices" list).
|
||||||
|
#if defined(Q_OS_ANDROID)
|
||||||
|
obj["name"] = QStringLiteral("Android");
|
||||||
|
#elif defined(Q_OS_IOS)
|
||||||
|
obj["name"] = QStringLiteral("iOS");
|
||||||
|
#elif defined(Q_OS_WIN)
|
||||||
|
obj["name"] = QStringLiteral("Windows");
|
||||||
|
#elif defined(Q_OS_MACOS)
|
||||||
|
obj["name"] = QStringLiteral("macOS");
|
||||||
|
#elif defined(Q_OS_LINUX)
|
||||||
|
obj["name"] = QStringLiteral("Linux");
|
||||||
|
#else
|
||||||
|
obj["name"] = QStringLiteral("Device");
|
||||||
|
#endif
|
||||||
|
return QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransferController::generateNewQrCode()
|
||||||
|
{
|
||||||
|
QString gw = m_settings->getGatewayEndpoint();
|
||||||
|
if (!gw.endsWith('/')) {
|
||||||
|
gw.append('/');
|
||||||
|
}
|
||||||
|
m_currentUuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
||||||
|
|
||||||
|
m_currentPayload = buildQrPayloadJson(gw, m_currentUuid);
|
||||||
|
|
||||||
|
auto qr = qrCodeUtils::generateQrCode(m_currentPayload.toUtf8());
|
||||||
|
const QString svg = QString::fromStdString(toSvgString(qr, 1));
|
||||||
|
m_qrCodeUrl = qrCodeUtils::svgToBase64(svg);
|
||||||
|
emit qrCodeUpdated();
|
||||||
|
emit currentUuidChanged();
|
||||||
|
emit currentPayloadChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransferController::stopScanner()
|
||||||
|
{
|
||||||
|
emit scannerShouldStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TransferController::getCurrentApiKey(QString *vpnKeyOut) const
|
||||||
|
{
|
||||||
|
const int idx = m_serversModel ? m_serversModel->getProcessedServerIndex() : -1;
|
||||||
|
if (idx < 0 || !m_serversModel) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QJsonObject server = m_serversModel->getServerConfig(idx);
|
||||||
|
|
||||||
|
const QJsonObject apiConfig = server.value(apiDefs::key::apiConfig).toObject();
|
||||||
|
const QJsonObject authData = server.value(QStringLiteral("auth_data")).toObject();
|
||||||
|
|
||||||
|
const QString apiKey = authData.value(QStringLiteral("api_key")).toString();
|
||||||
|
|
||||||
|
if (vpnKeyOut) {
|
||||||
|
QString vpnKey = apiConfig.value(apiDefs::key::vpnKey).toString();
|
||||||
|
if (vpnKey.isEmpty()) {
|
||||||
|
vpnKey = apiUtils::getPremiumV1VpnKey(server);
|
||||||
|
}
|
||||||
|
*vpnKeyOut = vpnKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransferController::onTransferQrScanned(const QString &code)
|
||||||
|
{
|
||||||
|
QJsonParseError err;
|
||||||
|
const QJsonDocument doc = QJsonDocument::fromJson(code.toUtf8(), &err);
|
||||||
|
if (err.error != QJsonParseError::NoError || !doc.isObject()) {
|
||||||
|
qWarning() << "TransferController::onTransferQrScanned: invalid QR JSON " << err.errorString();
|
||||||
|
emit postFailed(QStringLiteral("Invalid QR JSON"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QJsonObject obj = doc.object();
|
||||||
|
QString gw = obj.value("gw").toString();
|
||||||
|
const QString uuid = obj.value("uuid").toString();
|
||||||
|
|
||||||
|
if (gw.isEmpty() || uuid.isEmpty()) {
|
||||||
|
qWarning() << "TransferController::onTransferQrScanned: QR missing gw or uuid";
|
||||||
|
emit postFailed(QStringLiteral("QR missing gw or uuid"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!gw.endsWith('/')) {
|
||||||
|
gw.append('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
int chosenServerIdx = -1;
|
||||||
|
QString apiKey;
|
||||||
|
QString vpnKey;
|
||||||
|
|
||||||
|
auto tryServerIndex = [&](int idx) -> bool {
|
||||||
|
if (!m_serversModel || idx < 0 || idx >= m_serversModel->getServersCount()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QJsonObject server = m_serversModel->getServerConfig(idx);
|
||||||
|
const QJsonObject apiConfig = server.value(apiDefs::key::apiConfig).toObject();
|
||||||
|
const QJsonObject authData = server.value(QStringLiteral("auth_data")).toObject();
|
||||||
|
|
||||||
|
const QString candidateApiKey = authData.value(QStringLiteral("api_key")).toString();
|
||||||
|
QString candidateVpnKey = apiConfig.value(apiDefs::key::vpnKey).toString();
|
||||||
|
if (candidateVpnKey.isEmpty()) {
|
||||||
|
// Fallback for older Premium V1 configs where vpn_key may be derived.
|
||||||
|
candidateVpnKey = apiUtils::getPremiumV1VpnKey(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool candidateIsPremium = apiUtils::isPremiumServer(server);
|
||||||
|
const bool candidateIsFromGatewayApi = m_serversModel->data(idx, ServersModel::IsServerFromGatewayApiRole).toBool();
|
||||||
|
|
||||||
|
if (candidateApiKey.isEmpty() || candidateVpnKey.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!candidateIsPremium && !candidateIsFromGatewayApi) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
chosenServerIdx = idx;
|
||||||
|
apiKey = candidateApiKey;
|
||||||
|
vpnKey = candidateVpnKey;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (m_serversModel) {
|
||||||
|
tryServerIndex(m_serversModel->getProcessedServerIndex());
|
||||||
|
if (chosenServerIdx < 0) {
|
||||||
|
tryServerIndex(m_serversModel->getDefaultServerIndex());
|
||||||
|
}
|
||||||
|
if (chosenServerIdx < 0) {
|
||||||
|
const int n = m_serversModel->getServersCount();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (tryServerIndex(i)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chosenServerIdx < 0) {
|
||||||
|
qWarning() << "TransferController::onTransferQrScanned: no suitable subscription key/config found to send";
|
||||||
|
emit postFailed(QStringLiteral("No subscription key or config to send"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit postStarted();
|
||||||
|
|
||||||
|
const int sendTimeoutMs = 60000;
|
||||||
|
GatewayController gatewayController(gw,
|
||||||
|
m_settings->isDevGatewayEnv(),
|
||||||
|
sendTimeoutMs,
|
||||||
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
|
||||||
|
QJsonObject payload;
|
||||||
|
payload.insert(QStringLiteral("uuid"), uuid);
|
||||||
|
payload.insert(QStringLiteral("api_key"), apiKey);
|
||||||
|
payload.insert(QStringLiteral("config"), vpnKey);
|
||||||
|
|
||||||
|
const QString endpoint = QStringLiteral("%1v1/sendConfig");
|
||||||
|
QByteArray responseBody;
|
||||||
|
const QString fullUrl = endpoint.arg(gw);
|
||||||
|
qDebug() << "TransferController::onTransferQrScanned: POST" << fullUrl
|
||||||
|
<< "uuid:" << uuid;
|
||||||
|
logSystemProxiesForUrl(fullUrl);
|
||||||
|
const auto errorCode = gatewayController.post(endpoint, payload, responseBody);
|
||||||
|
qDebug() << "TransferController::onTransferQrScanned: sendConfig finished with code"
|
||||||
|
<< static_cast<int>(errorCode)
|
||||||
|
<< "response size:" << responseBody.size();
|
||||||
|
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
qWarning() << "TransferController::onTransferQrScanned: sendConfig failed with code" << static_cast<int>(errorCode);
|
||||||
|
emit postFailed(QStringLiteral("sendConfig failed: %1").arg(errorString(errorCode)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonParseError parseErr;
|
||||||
|
const QJsonDocument respDoc = QJsonDocument::fromJson(responseBody, &parseErr);
|
||||||
|
if (parseErr.error == QJsonParseError::NoError && respDoc.isObject()
|
||||||
|
&& respDoc.object().value(QStringLiteral("status")).toString() == QStringLiteral("success")) {
|
||||||
|
emit postSucceeded();
|
||||||
|
stopScanner();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qWarning() << "TransferController::onTransferQrScanned: unexpected gateway response:" << responseBody;
|
||||||
|
emit postFailed(QStringLiteral("Gateway response error"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TransferController::qrCodeUrl() const
|
||||||
|
{
|
||||||
|
return m_qrCodeUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransferController::startWaitForConfig(ImportController *importController)
|
||||||
|
{
|
||||||
|
QString gw = m_settings->getGatewayEndpoint();
|
||||||
|
if (!gw.endsWith(QLatin1Char('/'))) {
|
||||||
|
gw.append(QLatin1Char('/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString uuid = m_currentUuid;
|
||||||
|
|
||||||
|
if (uuid.isEmpty()) {
|
||||||
|
qWarning() << "TransferController::startWaitForConfig: no uuid";
|
||||||
|
emit waitError(QStringLiteral("No UUID"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_importController = importController;
|
||||||
|
if (m_importController) {
|
||||||
|
connect(m_importController, &ImportController::destroyed,
|
||||||
|
this,
|
||||||
|
&TransferController::handleImportControllerDestroyed,
|
||||||
|
Qt::UniqueConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int waitTimeoutMs = 60000;
|
||||||
|
|
||||||
|
QJsonObject payload;
|
||||||
|
payload.insert(QStringLiteral("uuid"), uuid);
|
||||||
|
|
||||||
|
GatewayController gatewayController(gw,
|
||||||
|
m_settings->isDevGatewayEnv(),
|
||||||
|
waitTimeoutMs,
|
||||||
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
|
||||||
|
const QString endpoint = QStringLiteral("%1v1/waitConfig");
|
||||||
|
QByteArray responseBody;
|
||||||
|
const QString fullUrl = endpoint.arg(gw);
|
||||||
|
logSystemProxiesForUrl(fullUrl);
|
||||||
|
const auto errorCode = gatewayController.post(endpoint, payload, responseBody);
|
||||||
|
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
qWarning() << "TransferController::startWaitForConfig: waitConfig failed with code" << static_cast<int>(errorCode);
|
||||||
|
emit waitError(QStringLiteral("waitConfig failed (%1)").arg(static_cast<int>(errorCode)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_importController) {
|
||||||
|
qWarning() << "TransferController::startWaitForConfig: import controller is null";
|
||||||
|
emit waitError(QStringLiteral("Import Controller destroyed"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonParseError parseErr;
|
||||||
|
const QJsonDocument respDoc = QJsonDocument::fromJson(responseBody, &parseErr);
|
||||||
|
if (parseErr.error != QJsonParseError::NoError || !respDoc.isObject()) {
|
||||||
|
qWarning() << "TransferController::startWaitForConfig: invalid JSON response:" << responseBody;
|
||||||
|
emit waitError(QStringLiteral("Invalid gateway response"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QJsonObject respObj = respDoc.object();
|
||||||
|
const QString status = respObj.value(QStringLiteral("status")).toString();
|
||||||
|
const QString configStr = respObj.value(QStringLiteral("config")).toString();
|
||||||
|
if (status != QStringLiteral("success")) {
|
||||||
|
qWarning() << "TransferController::startWaitForConfig: gateway status not success:" << status;
|
||||||
|
emit waitError(QStringLiteral("Gateway error"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (configStr.isEmpty()) {
|
||||||
|
emit waitError(QStringLiteral("Empty config"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (configStr == QStringLiteral("timeout")) {
|
||||||
|
emit waitError(QStringLiteral("Timeout"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_importController->extractConfigFromData(configStr)) {
|
||||||
|
qWarning() << "TransferController::startWaitForConfig: failed to parse config string";
|
||||||
|
emit waitError(QStringLiteral("Invalid config payload"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_importController->importConfig();
|
||||||
|
emit configApplied();
|
||||||
|
}
|
||||||
|
void TransferController::stopWaitForConfig()
|
||||||
|
{
|
||||||
|
qDebug() << "TransferController::stopWaitForConfig: stop flag set";
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
#ifndef TRANSFERCONTROLLER_H
|
||||||
|
#define TRANSFERCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
|
class Settings;
|
||||||
|
class ServersModel;
|
||||||
|
class ExportController;
|
||||||
|
class ImportController;
|
||||||
|
|
||||||
|
class TransferController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QString qrCodeUrl READ qrCodeUrl NOTIFY qrCodeUpdated)
|
||||||
|
Q_PROPERTY(QString pendingQrCode READ pendingQrCode WRITE setPendingQrCode NOTIFY pendingQrCodeChanged)
|
||||||
|
Q_PROPERTY(QString currentUuid READ currentUuid NOTIFY currentUuidChanged)
|
||||||
|
Q_PROPERTY(QString currentPayload READ currentPayload NOTIFY currentPayloadChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TransferController(const std::shared_ptr<Settings> &settings,
|
||||||
|
const QSharedPointer<ServersModel> &serversModel,
|
||||||
|
ExportController *exportController,
|
||||||
|
QObject *parent = nullptr);
|
||||||
|
~TransferController() override;
|
||||||
|
|
||||||
|
Q_INVOKABLE void generateNewQrCode();
|
||||||
|
|
||||||
|
Q_INVOKABLE void stopScanner();
|
||||||
|
Q_INVOKABLE void onTransferQrScanned(const QString &code);
|
||||||
|
|
||||||
|
Q_INVOKABLE void setPendingQrCode(const QString &code) { m_pendingQrCode = code; emit pendingQrCodeChanged(); }
|
||||||
|
QString pendingQrCode() const { return m_pendingQrCode; }
|
||||||
|
|
||||||
|
Q_INVOKABLE void startWaitForConfig(ImportController *importController);
|
||||||
|
Q_INVOKABLE void stopWaitForConfig();
|
||||||
|
|
||||||
|
QString qrCodeUrl() const;
|
||||||
|
QString currentUuid() const { return m_currentUuid; }
|
||||||
|
QString currentPayload() const { return m_currentPayload; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void qrCodeUpdated();
|
||||||
|
void scannerShouldStop();
|
||||||
|
void pendingQrCodeChanged();
|
||||||
|
void currentUuidChanged();
|
||||||
|
void currentPayloadChanged();
|
||||||
|
|
||||||
|
void waitError(const QString &message);
|
||||||
|
void configApplied();
|
||||||
|
|
||||||
|
void postStarted();
|
||||||
|
void postSucceeded();
|
||||||
|
void postFailed(const QString &message);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void handleImportControllerDestroyed();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString buildQrPayloadJson(const QString &gatewayUrl, const QString &uuid) const;
|
||||||
|
QString m_pendingQrCode;
|
||||||
|
QString getCurrentApiKey(QString *vpnKeyOut = nullptr) const;
|
||||||
|
std::shared_ptr<Settings> m_settings;
|
||||||
|
QSharedPointer<ServersModel> m_serversModel;
|
||||||
|
ExportController *m_exportController { nullptr };
|
||||||
|
ImportController *m_importController { nullptr };
|
||||||
|
|
||||||
|
QString m_qrCodeUrl;
|
||||||
|
QString m_currentUuid;
|
||||||
|
QString m_currentPayload;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TRANSFERCONTROLLER_H
|
||||||
@@ -112,7 +112,7 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
|
|||||||
} else {
|
} else {
|
||||||
return tr("VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. "
|
return tr("VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. "
|
||||||
"Other sites will be opened from your real IP address, "
|
"Other sites will be opened from your real IP address, "
|
||||||
"<a href=\"%1/free\" style=\"color: #FBB26A;\">more details on the website.</a>");
|
"<a href=\"%1\" style=\"color: #FBB26A;\">more details on the website.</a>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case PriceRole: {
|
case PriceRole: {
|
||||||
|
|||||||
@@ -26,12 +26,12 @@ QVariant AppSplitTunnelingModel::data(const QModelIndex &index, int role) const
|
|||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case AppPathRole: {
|
case AppPathRole: {
|
||||||
return m_apps.at(index.row()).appName;
|
return m_apps.at(index.row()).appName;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
@@ -59,6 +59,13 @@ void AppSplitTunnelingModel::removeApp(QModelIndex index)
|
|||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AppSplitTunnelingModel::clearAppsList() {
|
||||||
|
beginResetModel();
|
||||||
|
m_apps.clear();
|
||||||
|
m_settings->setVpnApps(m_currentRouteMode, m_apps);
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
int AppSplitTunnelingModel::getRouteMode()
|
int AppSplitTunnelingModel::getRouteMode()
|
||||||
{
|
{
|
||||||
return m_currentRouteMode;
|
return m_currentRouteMode;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
bool addApp(const InstalledAppInfo &appInfo);
|
bool addApp(const InstalledAppInfo &appInfo);
|
||||||
void removeApp(QModelIndex index);
|
void removeApp(QModelIndex index);
|
||||||
|
void clearAppsList();
|
||||||
|
|
||||||
int getRouteMode();
|
int getRouteMode();
|
||||||
void setRouteMode(int routeMode);
|
void setRouteMode(int routeMode);
|
||||||
|
|||||||
@@ -101,6 +101,23 @@ QString LanguageModel::getCurrentLanguageName()
|
|||||||
return m_availableLanguages[getCurrentLanguageIndex()].name;
|
return m_availableLanguages[getCurrentLanguageIndex()].name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LanguageSettings::AvailableLanguageEnum LanguageModel::getSystemLanguageEnum()
|
||||||
|
{
|
||||||
|
QLocale locale = QLocale::system();
|
||||||
|
switch (locale.language()) {
|
||||||
|
case QLocale::Russian: return LanguageSettings::AvailableLanguageEnum::Russian;
|
||||||
|
case QLocale::Chinese: return LanguageSettings::AvailableLanguageEnum::China_cn;
|
||||||
|
case QLocale::Ukrainian: return LanguageSettings::AvailableLanguageEnum::Ukrainian;
|
||||||
|
case QLocale::Persian: return LanguageSettings::AvailableLanguageEnum::Persian;
|
||||||
|
case QLocale::Arabic: return LanguageSettings::AvailableLanguageEnum::Arabic;
|
||||||
|
case QLocale::Burmese: return LanguageSettings::AvailableLanguageEnum::Burmese;
|
||||||
|
case QLocale::Urdu: return LanguageSettings::AvailableLanguageEnum::Urdu;
|
||||||
|
case QLocale::Hindi: return LanguageSettings::AvailableLanguageEnum::Hindi;
|
||||||
|
case QLocale::English: return LanguageSettings::AvailableLanguageEnum::English;
|
||||||
|
default: return LanguageSettings::AvailableLanguageEnum::English;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString LanguageModel::getCurrentSiteUrl(const QString &path)
|
QString LanguageModel::getCurrentSiteUrl(const QString &path)
|
||||||
{
|
{
|
||||||
auto language = static_cast<LanguageSettings::AvailableLanguageEnum>(getCurrentLanguageIndex());
|
auto language = static_cast<LanguageSettings::AvailableLanguageEnum>(getCurrentLanguageIndex());
|
||||||
@@ -110,3 +127,13 @@ QString LanguageModel::getCurrentSiteUrl(const QString &path)
|
|||||||
default: return QString("https://amnezia.org") + (path.isEmpty() ? "" : (QString("/%1").arg(path)));
|
default: return QString("https://amnezia.org") + (path.isEmpty() ? "" : (QString("/%1").arg(path)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString LanguageModel::getCurrentDocsUrl(const QString &path)
|
||||||
|
{
|
||||||
|
auto language = static_cast<LanguageSettings::AvailableLanguageEnum>(getCurrentLanguageIndex());
|
||||||
|
switch (language) {
|
||||||
|
case LanguageSettings::AvailableLanguageEnum::Russian:
|
||||||
|
return "https://storage.googleapis.com/amnezia/docs" + (path.isEmpty() ? "" : (QString("?m-path=/%1").arg(path)));
|
||||||
|
default: return QString("https://docs.amnezia.org") + (path.isEmpty() ? "" : (QString("/%1").arg(path)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
LanguageModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
LanguageModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||||
|
LanguageSettings::AvailableLanguageEnum getSystemLanguageEnum();
|
||||||
|
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
@@ -60,6 +61,7 @@ public slots:
|
|||||||
int getLineHeightAppend();
|
int getLineHeightAppend();
|
||||||
QString getCurrentLanguageName();
|
QString getCurrentLanguageName();
|
||||||
QString getCurrentSiteUrl(const QString &path = "");
|
QString getCurrentSiteUrl(const QString &path = "");
|
||||||
|
QString getCurrentDocsUrl(const QString &path = "");
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void updateTranslations(const QLocale &locale);
|
void updateTranslations(const QLocale &locale);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "core/controllers/serverController.h"
|
#include "core/controllers/serverController.h"
|
||||||
#include "core/networkUtilities.h"
|
#include "core/networkUtilities.h"
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <AmneziaVPN-Swift.h>
|
#include <AmneziaVPN-Swift.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -782,7 +782,7 @@ void ServersModel::removeApiConfig(const int serverIndex)
|
|||||||
{
|
{
|
||||||
auto serverConfig = getServerConfig(serverIndex);
|
auto serverConfig = getServerConfig(serverIndex);
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
QString vpncName = QString("%1 (%2) %3")
|
QString vpncName = QString("%1 (%2) %3")
|
||||||
.arg(serverConfig[config_key::description].toString())
|
.arg(serverConfig[config_key::description].toString())
|
||||||
.arg(serverConfig[config_key::hostName].toString())
|
.arg(serverConfig[config_key::hostName].toString())
|
||||||
|
|||||||
@@ -83,6 +83,16 @@ void SitesModel::removeSite(QModelIndex index)
|
|||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SitesModel::removeSites()
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
|
||||||
|
m_settings->removeAllVpnSites(m_currentRouteMode);
|
||||||
|
fillSites();
|
||||||
|
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
int SitesModel::getRouteMode()
|
int SitesModel::getRouteMode()
|
||||||
{
|
{
|
||||||
return m_currentRouteMode;
|
return m_currentRouteMode;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ public slots:
|
|||||||
bool addSite(const QString &hostname, const QString &ip);
|
bool addSite(const QString &hostname, const QString &ip);
|
||||||
void addSites(const QMap<QString, QString> &sites, bool replaceExisting);
|
void addSites(const QMap<QString, QString> &sites, bool replaceExisting);
|
||||||
void removeSite(QModelIndex index);
|
void removeSite(QModelIndex index);
|
||||||
|
void removeSites();
|
||||||
|
|
||||||
int getRouteMode();
|
int getRouteMode();
|
||||||
void setRouteMode(int routeMode);
|
void setRouteMode(int routeMode);
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#ifndef NE_NOTIFICATION_HANDLER_H
|
||||||
|
#define NE_NOTIFICATION_HANDLER_H
|
||||||
|
|
||||||
|
#include "notificationhandler.h"
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QAction>
|
||||||
|
|
||||||
|
class MacOSStatusIcon;
|
||||||
|
|
||||||
|
class NEStatusBarNotificationHandler : public NotificationHandler {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit NEStatusBarNotificationHandler(QObject* parent);
|
||||||
|
~NEStatusBarNotificationHandler() override;
|
||||||
|
|
||||||
|
void setConnectionState(Vpn::ConnectionState state) override;
|
||||||
|
void onTranslationsUpdated() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void notify(Message type, const QString& title,
|
||||||
|
const QString& message, int timerMsec) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void buildMenu();
|
||||||
|
|
||||||
|
QMenu m_menu;
|
||||||
|
MacOSStatusIcon* m_statusIcon;
|
||||||
|
|
||||||
|
QAction* m_actionShow;
|
||||||
|
QAction* m_actionConnect;
|
||||||
|
QAction* m_actionDisconnect;
|
||||||
|
QAction* m_actionVisitWebsite;
|
||||||
|
QAction* m_actionQuit;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NE_NOTIFICATION_HANDLER_H
|
||||||
@@ -11,18 +11,12 @@
|
|||||||
# include "systemtray_notificationhandler.h"
|
# include "systemtray_notificationhandler.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
NotificationHandler* NotificationHandler::create(QObject* parent) {
|
NotificationHandler* NotificationHandler::create(QObject* parent) {
|
||||||
#if defined(Q_OS_IOS)
|
#if defined(Q_OS_IOS)
|
||||||
return new IOSNotificationHandler(parent);
|
return new IOSNotificationHandler(parent);
|
||||||
#else
|
#else
|
||||||
|
|
||||||
# if defined(Q_OS_LINUX)
|
|
||||||
//if (LinuxSystemTrayNotificationHandler::requiredCustomImpl()) {
|
|
||||||
// return new LinuxSystemTrayNotificationHandler(parent);
|
|
||||||
//}
|
|
||||||
# endif
|
|
||||||
|
|
||||||
return new SystemTrayNotificationHandler(parent);
|
return new SystemTrayNotificationHandler(parent);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ import ProtocolEnum 1.0
|
|||||||
import "../Controls2"
|
import "../Controls2"
|
||||||
import "../Controls2/TextTypes"
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
|
ListViewType {
|
||||||
ListView {
|
|
||||||
id: menuContent
|
id: menuContent
|
||||||
|
|
||||||
property var rootWidth
|
property var rootWidth
|
||||||
@@ -21,13 +20,6 @@ ListView {
|
|||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
clip: true
|
|
||||||
snapMode: ListView.SnapToItem
|
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBarType {}
|
|
||||||
|
|
||||||
property bool isFocusable: true
|
|
||||||
|
|
||||||
ButtonGroup {
|
ButtonGroup {
|
||||||
id: containersRadioButtonGroup
|
id: containersRadioButtonGroup
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,97 +1,97 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import PageEnum 1.0
|
import PageEnum 1.0
|
||||||
|
|
||||||
import "../Controls2"
|
import "../Controls2"
|
||||||
import "../Controls2/TextTypes"
|
import "../Controls2/TextTypes"
|
||||||
import "../Config"
|
import "../Config"
|
||||||
|
|
||||||
DrawerType2 {
|
DrawerType2 {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android"
|
property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android"
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
expandedHeight: parent.height * 0.9
|
expandedHeight: parent.height * 0.9
|
||||||
|
|
||||||
expandedStateContent: ColumnLayout {
|
expandedStateContent: ColumnLayout {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Header2Type {
|
Header2Type {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 24
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.bottomMargin: 16
|
Layout.bottomMargin: 16
|
||||||
|
|
||||||
headerText: qsTr("Split tunneling")
|
headerText: qsTr("Split tunneling")
|
||||||
descriptionText: qsTr("Allows you to connect to some sites or applications through a VPN connection and bypass others")
|
descriptionText: qsTr("Allows you to connect to some sites or applications through a VPN connection and bypass others")
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
id: splitTunnelingSwitch
|
id: splitTunnelingSwitch
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
|
|
||||||
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
|
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
|
||||||
|
|
||||||
text: qsTr("Split tunneling on the server")
|
text: qsTr("Split tunneling on the server")
|
||||||
descriptionText: qsTr("Enabled \nCan't be disabled for current server")
|
descriptionText: qsTr("Enabled \nCan't be disabled for current server")
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
||||||
root.closeTriggered()
|
root.closeTriggered()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {
|
DividerType {
|
||||||
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
|
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
id: siteBasedSplitTunnelingSwitch
|
id: siteBasedSplitTunnelingSwitch
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
|
|
||||||
text: qsTr("Site-based split tunneling")
|
text: qsTr("Site-based split tunneling")
|
||||||
descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
||||||
root.closeTriggered()
|
root.closeTriggered()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {
|
DividerType {
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
id: appSplitTunnelingSwitch
|
id: appSplitTunnelingSwitch
|
||||||
visible: isAppSplitTinnelingEnabled
|
visible: isAppSplitTinnelingEnabled
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
text: qsTr("App-based split tunneling")
|
text: qsTr("App-based split tunneling")
|
||||||
descriptionText: AppSplitTunnelingModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
descriptionText: AppSplitTunnelingModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
|
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
|
||||||
root.closeTriggered()
|
root.closeTriggered()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {
|
DividerType {
|
||||||
visible: isAppSplitTinnelingEnabled
|
visible: isAppSplitTinnelingEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ DrawerType2 {
|
|||||||
headerText: qsTr("Choose application")
|
headerText: qsTr("Choose application")
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ListViewType {
|
||||||
id: listView
|
id: listView
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
@@ -66,11 +66,6 @@ DrawerType2 {
|
|||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
clip: true
|
|
||||||
interactive: true
|
|
||||||
|
|
||||||
property bool isFocusable: true
|
|
||||||
|
|
||||||
model: SortFilterProxyModel {
|
model: SortFilterProxyModel {
|
||||||
id: proxyInstalledAppsModel
|
id: proxyInstalledAppsModel
|
||||||
sourceModel: installedAppsModel
|
sourceModel: installedAppsModel
|
||||||
@@ -81,44 +76,35 @@ DrawerType2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBarType {}
|
|
||||||
|
|
||||||
ButtonGroup {
|
ButtonGroup {
|
||||||
id: buttonGroup
|
id: buttonGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: Item {
|
delegate: ColumnLayout {
|
||||||
implicitWidth: root.width
|
width: listView.width
|
||||||
implicitHeight: delegateContent.implicitHeight
|
|
||||||
|
|
||||||
ColumnLayout {
|
RowLayout {
|
||||||
id: delegateContent
|
CheckBoxType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
anchors.fill: parent
|
text: appName
|
||||||
|
checked: isAppSelected
|
||||||
RowLayout {
|
onCheckedChanged: {
|
||||||
CheckBoxType {
|
installedAppsModel.selectedStateChanged(proxyInstalledAppsModel.mapToSource(index), checked)
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: appName
|
|
||||||
checked: isAppSelected
|
|
||||||
onCheckedChanged: {
|
|
||||||
installedAppsModel.selectedStateChanged(proxyInstalledAppsModel.mapToSource(index), checked)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
source: "image://installedAppImage/" + appIcon
|
|
||||||
|
|
||||||
sourceSize.width: 24
|
|
||||||
sourceSize.height: 24
|
|
||||||
|
|
||||||
Layout.rightMargin: 48
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
Image {
|
||||||
|
source: "image://installedAppImage/" + appIcon
|
||||||
|
|
||||||
|
sourceSize.width: 24
|
||||||
|
sourceSize.height: 24
|
||||||
|
|
||||||
|
Layout.rightMargin: 48
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ DrawerType2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ListViewType {
|
||||||
id: listView
|
id: listView
|
||||||
|
|
||||||
anchors.top: backButtonLayout.bottom
|
anchors.top: backButtonLayout.bottom
|
||||||
@@ -57,14 +57,8 @@ DrawerType2 {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
property bool isFocusable: true
|
|
||||||
property int selectedIndex: LanguageModel.currentLanguageIndex
|
property int selectedIndex: LanguageModel.currentLanguageIndex
|
||||||
|
|
||||||
clip: true
|
|
||||||
reuseItems: true
|
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBarType {}
|
|
||||||
|
|
||||||
model: LanguageModel
|
model: LanguageModel
|
||||||
|
|
||||||
ButtonGroup {
|
ButtonGroup {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import "../Controls2"
|
|||||||
import "../Controls2/TextTypes"
|
import "../Controls2/TextTypes"
|
||||||
import "../Config"
|
import "../Config"
|
||||||
|
|
||||||
ListView {
|
ListViewType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property int selectedIndex: ServersModel.defaultIndex
|
property int selectedIndex: ServersModel.defaultIndex
|
||||||
@@ -28,10 +28,6 @@ ListView {
|
|||||||
|
|
||||||
model: ServersModel
|
model: ServersModel
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBarType {}
|
|
||||||
|
|
||||||
property bool isFocusable: true
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: ServersModel
|
target: ServersModel
|
||||||
function onDefaultServerIndexChanged(serverIndex) {
|
function onDefaultServerIndexChanged(serverIndex) {
|
||||||
@@ -39,9 +35,6 @@ ListView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clip: true
|
|
||||||
reuseItems: true
|
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
id: menuContentDelegate
|
id: menuContentDelegate
|
||||||
objectName: "menuContentDelegate"
|
objectName: "menuContentDelegate"
|
||||||
|
|||||||
@@ -13,78 +13,64 @@ import "../Controls2"
|
|||||||
import "../Controls2/TextTypes"
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
|
|
||||||
ListView {
|
ListViewType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
width: parent.width
|
anchors.fill: parent
|
||||||
height: root.contentItem.height
|
|
||||||
|
|
||||||
clip: true
|
delegate: ColumnLayout {
|
||||||
reuseItems: true
|
width: root.width
|
||||||
|
|
||||||
property bool isFocusable: false
|
LabelWithButtonType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
delegate: Item {
|
text: name
|
||||||
implicitWidth: root.width
|
descriptionText: description
|
||||||
implicitHeight: delegateContent.implicitHeight
|
rightImageSource: isInstalled ? "qrc:/images/controls/chevron-right.svg" : "qrc:/images/controls/download.svg"
|
||||||
|
|
||||||
ColumnLayout {
|
clickedFunction: function() {
|
||||||
id: delegateContent
|
if (isInstalled) {
|
||||||
|
var containerIndex = root.model.mapToSource(index)
|
||||||
|
ContainersModel.setProcessedContainerIndex(containerIndex)
|
||||||
|
|
||||||
anchors.fill: parent
|
if (serviceType !== ProtocolEnum.Other) {
|
||||||
|
if (config[ContainerProps.containerTypeToString(containerIndex)]["isThirdPartyConfig"]) {
|
||||||
LabelWithButtonType {
|
|
||||||
id: containerRadioButton
|
|
||||||
implicitWidth: parent.width
|
|
||||||
|
|
||||||
text: name
|
|
||||||
descriptionText: description
|
|
||||||
rightImageSource: isInstalled ? "qrc:/images/controls/chevron-right.svg" : "qrc:/images/controls/download.svg"
|
|
||||||
|
|
||||||
clickedFunction: function() {
|
|
||||||
if (isInstalled) {
|
|
||||||
var containerIndex = root.model.mapToSource(index)
|
|
||||||
ContainersModel.setProcessedContainerIndex(containerIndex)
|
|
||||||
|
|
||||||
if (serviceType !== ProtocolEnum.Other) {
|
|
||||||
if (config[ContainerProps.containerTypeToString(containerIndex)]["isThirdPartyConfig"]) {
|
|
||||||
ProtocolsModel.updateModel(config)
|
|
||||||
PageController.goToPage(PageEnum.PageProtocolRaw)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (containerIndex) {
|
|
||||||
case ContainerEnum.Ipsec: {
|
|
||||||
ProtocolsModel.updateModel(config)
|
ProtocolsModel.updateModel(config)
|
||||||
PageController.goToPage(PageEnum.PageProtocolRaw)
|
PageController.goToPage(PageEnum.PageProtocolRaw)
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
case ContainerEnum.Dns: {
|
|
||||||
PageController.goToPage(PageEnum.PageServiceDnsSettings)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
ProtocolsModel.updateModel(config)
|
|
||||||
PageController.goToPage(PageEnum.PageSettingsServerProtocol)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
ContainersModel.setProcessedContainerIndex(root.model.mapToSource(index))
|
|
||||||
InstallController.setShouldCreateServer(false)
|
|
||||||
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
switch (containerIndex) {
|
||||||
anchors.fill: parent
|
case ContainerEnum.Ipsec: {
|
||||||
cursorShape: Qt.PointingHandCursor
|
ProtocolsModel.updateModel(config)
|
||||||
enabled: false
|
PageController.goToPage(PageEnum.PageProtocolRaw)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case ContainerEnum.Dns: {
|
||||||
|
PageController.goToPage(PageEnum.PageServiceDnsSettings)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
ProtocolsModel.updateModel(config)
|
||||||
|
PageController.goToPage(PageEnum.PageSettingsServerProtocol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ContainersModel.setProcessedContainerIndex(root.model.mapToSource(index))
|
||||||
|
InstallController.setShouldCreateServer(false)
|
||||||
|
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,375 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import QtQuick.Dialogs
|
|
||||||
|
|
||||||
import QtCore
|
|
||||||
|
|
||||||
import SortFilterProxyModel 0.2
|
|
||||||
|
|
||||||
import PageEnum 1.0
|
|
||||||
import ContainerProps 1.0
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
import "./"
|
|
||||||
import "../Controls2"
|
|
||||||
import "../Controls2/TextTypes"
|
|
||||||
import "../Config"
|
|
||||||
import "../Components"
|
|
||||||
|
|
||||||
DrawerType2 {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property string headerText
|
|
||||||
property string configContentHeaderText
|
|
||||||
property string shareButtonText: qsTr("Share")
|
|
||||||
property string copyButtonText: qsTr("Copy")
|
|
||||||
property bool isSelfHostedConfig: true
|
|
||||||
|
|
||||||
property string configExtension: ".vpn"
|
|
||||||
property string configCaption: qsTr("Save AmneziaVPN config")
|
|
||||||
property string configFileName: "amnezia_config"
|
|
||||||
|
|
||||||
expandedHeight: parent.height * 0.9
|
|
||||||
|
|
||||||
onClosed: {
|
|
||||||
configExtension = ".vpn"
|
|
||||||
configCaption = qsTr("Save AmneziaVPN config")
|
|
||||||
configFileName = "amnezia_config"
|
|
||||||
}
|
|
||||||
|
|
||||||
expandedStateContent: Item {
|
|
||||||
implicitHeight: root.expandedHeight
|
|
||||||
|
|
||||||
Header2Type {
|
|
||||||
id: header
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.topMargin: 20
|
|
||||||
anchors.leftMargin: 16
|
|
||||||
anchors.rightMargin: 16
|
|
||||||
|
|
||||||
headerText: root.headerText
|
|
||||||
}
|
|
||||||
|
|
||||||
ListView {
|
|
||||||
id: listView
|
|
||||||
|
|
||||||
anchors.top: header.bottom
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
property bool isFocusable: true
|
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBarType {}
|
|
||||||
|
|
||||||
model: 1
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
reuseItems: true
|
|
||||||
|
|
||||||
header: ColumnLayout {
|
|
||||||
width: listView.width
|
|
||||||
|
|
||||||
BasicButtonType {
|
|
||||||
id: shareButton
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 16
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
|
|
||||||
text: root.shareButtonText
|
|
||||||
leftImageSource: "qrc:/images/controls/share-2.svg"
|
|
||||||
|
|
||||||
clickedFunc: function() {
|
|
||||||
var fileName = ""
|
|
||||||
if (GC.isMobile()) {
|
|
||||||
fileName = configFileName + configExtension
|
|
||||||
} else {
|
|
||||||
fileName = SystemController.getFileName(configCaption,
|
|
||||||
qsTr("Config files (*" + configExtension + ")"),
|
|
||||||
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + configFileName,
|
|
||||||
true,
|
|
||||||
configExtension)
|
|
||||||
}
|
|
||||||
if (fileName !== "") {
|
|
||||||
PageController.showBusyIndicator(true)
|
|
||||||
ExportController.exportConfig(fileName)
|
|
||||||
PageController.showBusyIndicator(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BasicButtonType {
|
|
||||||
id: copyConfigTextButton
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 8
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
|
|
||||||
defaultColor: AmneziaStyle.color.transparent
|
|
||||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
|
||||||
pressedColor: AmneziaStyle.color.sheerWhite
|
|
||||||
disabledColor: AmneziaStyle.color.mutedGray
|
|
||||||
textColor: AmneziaStyle.color.paleGray
|
|
||||||
borderWidth: 1
|
|
||||||
|
|
||||||
text: root.copyButtonText
|
|
||||||
leftImageSource: "qrc:/images/controls/copy.svg"
|
|
||||||
|
|
||||||
Keys.onReturnPressed: { copyConfigTextButton.clicked() }
|
|
||||||
Keys.onEnterPressed: { copyConfigTextButton.clicked() }
|
|
||||||
}
|
|
||||||
|
|
||||||
BasicButtonType {
|
|
||||||
id: copyNativeConfigStringButton
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 8
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
defaultColor: AmneziaStyle.color.transparent
|
|
||||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
|
||||||
pressedColor: AmneziaStyle.color.sheerWhite
|
|
||||||
disabledColor: AmneziaStyle.color.mutedGray
|
|
||||||
textColor: AmneziaStyle.color.paleGray
|
|
||||||
borderWidth: 1
|
|
||||||
|
|
||||||
text: qsTr("Copy config string")
|
|
||||||
leftImageSource: "qrc:/images/controls/copy.svg"
|
|
||||||
|
|
||||||
KeyNavigation.tab: showSettingsButton
|
|
||||||
}
|
|
||||||
|
|
||||||
BasicButtonType {
|
|
||||||
id: showSettingsButton
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 24
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
|
|
||||||
visible: root.isSelfHostedConfig
|
|
||||||
|
|
||||||
defaultColor: AmneziaStyle.color.transparent
|
|
||||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
|
||||||
pressedColor: AmneziaStyle.color.sheerWhite
|
|
||||||
disabledColor: AmneziaStyle.color.mutedGray
|
|
||||||
textColor: AmneziaStyle.color.paleGray
|
|
||||||
borderWidth: 1
|
|
||||||
|
|
||||||
text: qsTr("Show connection settings")
|
|
||||||
|
|
||||||
clickedFunc: function() {
|
|
||||||
configContentDrawer.openTriggered()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawerType2 {
|
|
||||||
id: configContentDrawer
|
|
||||||
|
|
||||||
parent: root.parent
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
expandedHeight: parent.height * 0.9
|
|
||||||
|
|
||||||
expandedStateContent: Item {
|
|
||||||
id: configContentContainer
|
|
||||||
|
|
||||||
implicitHeight: configContentDrawer.expandedHeight
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: copyNativeConfigStringButton
|
|
||||||
function onClicked() {
|
|
||||||
nativeConfigString.selectAll()
|
|
||||||
nativeConfigString.copy()
|
|
||||||
nativeConfigString.select(0, 0)
|
|
||||||
PageController.showNotificationMessage(qsTr("Copied"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: copyConfigTextButton
|
|
||||||
function onClicked() {
|
|
||||||
configText.selectAll()
|
|
||||||
configText.copy()
|
|
||||||
configText.select(0, 0)
|
|
||||||
PageController.showNotificationMessage(qsTr("Copied"))
|
|
||||||
header.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BackButtonType {
|
|
||||||
id: backButton
|
|
||||||
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.topMargin: 16
|
|
||||||
|
|
||||||
backButtonFunction: function() { configContentDrawer.closeTriggered() }
|
|
||||||
}
|
|
||||||
|
|
||||||
FlickableType {
|
|
||||||
anchors.top: backButton.bottom
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
contentHeight: configContent.implicitHeight + configContent.anchors.topMargin + configContent.anchors.bottomMargin
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: configContent
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.rightMargin: 16
|
|
||||||
anchors.leftMargin: 16
|
|
||||||
|
|
||||||
Header2Type {
|
|
||||||
id: configContentHeader
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 16
|
|
||||||
|
|
||||||
headerText: root.configContentHeaderText
|
|
||||||
}
|
|
||||||
|
|
||||||
TextField {
|
|
||||||
id: nativeConfigString
|
|
||||||
visible: false
|
|
||||||
text: ExportController.nativeConfigString
|
|
||||||
|
|
||||||
onTextChanged: {
|
|
||||||
copyNativeConfigStringButton.visible = nativeConfigString.text !== ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextArea {
|
|
||||||
id: configText
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 16
|
|
||||||
Layout.bottomMargin: 16
|
|
||||||
|
|
||||||
padding: 0
|
|
||||||
leftPadding: 0
|
|
||||||
height: 24
|
|
||||||
|
|
||||||
readOnly: true
|
|
||||||
activeFocusOnTab: false
|
|
||||||
|
|
||||||
color: AmneziaStyle.color.paleGray
|
|
||||||
selectionColor: AmneziaStyle.color.richBrown
|
|
||||||
selectedTextColor: AmneziaStyle.color.paleGray
|
|
||||||
|
|
||||||
font.pixelSize: 16
|
|
||||||
font.weight: Font.Medium
|
|
||||||
font.family: "PT Root UI VF"
|
|
||||||
|
|
||||||
text: ExportController.config
|
|
||||||
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: ColumnLayout {
|
|
||||||
width: listView.width
|
|
||||||
|
|
||||||
property bool isQrCodeVisible: root.isSelfHostedConfig ? ExportController.qrCodesCount > 0 : ApiConfigsController.qrCodesCount > 0
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: qrCodeContainer
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: width
|
|
||||||
Layout.topMargin: 20
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
|
|
||||||
visible: isQrCodeVisible
|
|
||||||
|
|
||||||
color: "white"
|
|
||||||
|
|
||||||
Image {
|
|
||||||
anchors.fill: parent
|
|
||||||
smooth: false
|
|
||||||
|
|
||||||
source: root.isSelfHostedConfig ? (isQrCodeVisible ? ExportController.qrCodes[0] : "") :
|
|
||||||
(isQrCodeVisible ? ApiConfigsController.qrCodes[0] : "")
|
|
||||||
|
|
||||||
property bool isFocusable: true
|
|
||||||
|
|
||||||
Keys.onTabPressed: {
|
|
||||||
FocusController.nextKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onBacktabPressed: {
|
|
||||||
FocusController.previousKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onUpPressed: {
|
|
||||||
FocusController.nextKeyUpItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onDownPressed: {
|
|
||||||
FocusController.nextKeyDownItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onLeftPressed: {
|
|
||||||
FocusController.nextKeyLeftItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onRightPressed: {
|
|
||||||
FocusController.nextKeyRightItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
property int index: 0
|
|
||||||
interval: 1000
|
|
||||||
running: isQrCodeVisible
|
|
||||||
repeat: true
|
|
||||||
onTriggered: {
|
|
||||||
if (isQrCodeVisible) {
|
|
||||||
index++
|
|
||||||
let qrCodesCount = root.isSelfHostedConfig ? ExportController.qrCodesCount : ApiConfigsController.qrCodesCount
|
|
||||||
if (index >= qrCodesCount) {
|
|
||||||
index = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
parent.source = root.isSelfHostedConfig ? ExportController.qrCodes[index] : ApiConfigsController.qrCodes[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on source {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ParagraphTextType {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 24
|
|
||||||
Layout.bottomMargin: 32
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
|
|
||||||
visible: isQrCodeVisible
|
|
||||||
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: qsTr("To read the QR code in the Amnezia app, select \"Add server\" → \"I have data to connect\" → \"QR code, key or settings file\"")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,220 +1,210 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Qt5Compat.GraphicalEffects
|
import Qt5Compat.GraphicalEffects
|
||||||
|
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
|
||||||
import "TextTypes"
|
import "TextTypes"
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string hoveredColor: AmneziaStyle.color.lightGray
|
property string hoveredColor: AmneziaStyle.color.lightGray
|
||||||
property string defaultColor: AmneziaStyle.color.paleGray
|
property string defaultColor: AmneziaStyle.color.paleGray
|
||||||
property string disabledColor: AmneziaStyle.color.charcoalGray
|
property string disabledColor: AmneziaStyle.color.charcoalGray
|
||||||
property string pressedColor: AmneziaStyle.color.mutedGray
|
property string pressedColor: AmneziaStyle.color.mutedGray
|
||||||
|
|
||||||
property string textColor: AmneziaStyle.color.midnightBlack
|
property string textColor: AmneziaStyle.color.midnightBlack
|
||||||
|
|
||||||
property string borderColor: AmneziaStyle.color.paleGray
|
property string borderColor: AmneziaStyle.color.paleGray
|
||||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||||
property int borderWidth: 0
|
property int borderWidth: 0
|
||||||
property int borderFocusedWidth: 1
|
property int borderFocusedWidth: 1
|
||||||
|
|
||||||
property string leftImageSource
|
property string leftImageSource
|
||||||
property string rightImageSource
|
property string rightImageSource
|
||||||
property string leftImageColor: textColor
|
property string leftImageColor: textColor
|
||||||
property bool changeLeftImageSize: true
|
property bool changeLeftImageSize: true
|
||||||
|
|
||||||
property bool squareLeftSide: false
|
property bool squareLeftSide: false
|
||||||
|
|
||||||
property FlickableType parentFlickable
|
property var clickedFunc
|
||||||
|
|
||||||
property var clickedFunc
|
property alias buttonTextLabel: buttonText
|
||||||
|
|
||||||
property alias buttonTextLabel: buttonText
|
property bool isFocusable: true
|
||||||
|
|
||||||
property bool isFocusable: true
|
Keys.onTabPressed: {
|
||||||
|
FocusController.nextKeyTabItem()
|
||||||
Keys.onTabPressed: {
|
}
|
||||||
FocusController.nextKeyTabItem()
|
|
||||||
}
|
Keys.onBacktabPressed: {
|
||||||
|
FocusController.previousKeyTabItem()
|
||||||
Keys.onBacktabPressed: {
|
}
|
||||||
FocusController.previousKeyTabItem()
|
|
||||||
}
|
Keys.onUpPressed: {
|
||||||
|
FocusController.nextKeyUpItem()
|
||||||
Keys.onUpPressed: {
|
}
|
||||||
FocusController.nextKeyUpItem()
|
|
||||||
}
|
Keys.onDownPressed: {
|
||||||
|
FocusController.nextKeyDownItem()
|
||||||
Keys.onDownPressed: {
|
}
|
||||||
FocusController.nextKeyDownItem()
|
|
||||||
}
|
Keys.onLeftPressed: {
|
||||||
|
FocusController.nextKeyLeftItem()
|
||||||
Keys.onLeftPressed: {
|
}
|
||||||
FocusController.nextKeyLeftItem()
|
|
||||||
}
|
Keys.onRightPressed: {
|
||||||
|
FocusController.nextKeyRightItem()
|
||||||
Keys.onRightPressed: {
|
}
|
||||||
FocusController.nextKeyRightItem()
|
|
||||||
}
|
implicitHeight: 56
|
||||||
|
|
||||||
implicitHeight: 56
|
hoverEnabled: true
|
||||||
|
|
||||||
hoverEnabled: true
|
background: Rectangle {
|
||||||
|
id: focusBorder
|
||||||
onFocusChanged: {
|
|
||||||
if (root.activeFocus) {
|
color: AmneziaStyle.color.transparent
|
||||||
if (root.parentFlickable) {
|
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
|
||||||
root.parentFlickable.ensureVisible(this)
|
border.width: root.activeFocus ? root.borderFocusedWidth : 0
|
||||||
}
|
|
||||||
}
|
anchors.fill: parent
|
||||||
}
|
|
||||||
|
radius: 16
|
||||||
background: Rectangle {
|
|
||||||
id: focusBorder
|
Rectangle {
|
||||||
|
id: background
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
|
anchors.fill: focusBorder
|
||||||
border.width: root.activeFocus ? root.borderFocusedWidth : 0
|
anchors.margins: root.activeFocus ? 2 : 0
|
||||||
|
|
||||||
anchors.fill: parent
|
radius: root.activeFocus ? 14 : 16
|
||||||
|
color: {
|
||||||
radius: 16
|
if (root.enabled) {
|
||||||
|
if (root.pressed) {
|
||||||
Rectangle {
|
return pressedColor
|
||||||
id: background
|
}
|
||||||
|
return root.hovered ? hoveredColor : defaultColor
|
||||||
anchors.fill: focusBorder
|
} else {
|
||||||
anchors.margins: root.activeFocus ? 2 : 0
|
return disabledColor
|
||||||
|
}
|
||||||
radius: root.activeFocus ? 14 : 16
|
}
|
||||||
color: {
|
border.color: borderColor
|
||||||
if (root.enabled) {
|
border.width: borderWidth
|
||||||
if (root.pressed) {
|
|
||||||
return pressedColor
|
Behavior on color {
|
||||||
}
|
PropertyAnimation { duration: 200 }
|
||||||
return root.hovered ? hoveredColor : defaultColor
|
}
|
||||||
} else {
|
|
||||||
return disabledColor
|
Rectangle {
|
||||||
}
|
visible: root.squareLeftSide
|
||||||
}
|
|
||||||
border.color: borderColor
|
z: 1
|
||||||
border.width: borderWidth
|
|
||||||
|
width: parent.radius
|
||||||
Behavior on color {
|
height: parent.radius
|
||||||
PropertyAnimation { duration: 200 }
|
anchors.top: parent.top
|
||||||
}
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
Rectangle {
|
color: {
|
||||||
visible: root.squareLeftSide
|
if (root.enabled) {
|
||||||
|
if (root.pressed) {
|
||||||
z: 1
|
return pressedColor
|
||||||
|
}
|
||||||
width: parent.radius
|
return root.hovered ? hoveredColor : defaultColor
|
||||||
height: parent.radius
|
} else {
|
||||||
anchors.top: parent.top
|
return disabledColor
|
||||||
anchors.bottom: parent.bottom
|
}
|
||||||
anchors.left: parent.left
|
}
|
||||||
color: {
|
|
||||||
if (root.enabled) {
|
Behavior on color {
|
||||||
if (root.pressed) {
|
PropertyAnimation { duration: 200 }
|
||||||
return pressedColor
|
}
|
||||||
}
|
}
|
||||||
return root.hovered ? hoveredColor : defaultColor
|
}
|
||||||
} else {
|
}
|
||||||
return disabledColor
|
|
||||||
}
|
MouseArea {
|
||||||
}
|
anchors.fill: focusBorder
|
||||||
|
enabled: false
|
||||||
Behavior on color {
|
cursorShape: Qt.PointingHandCursor
|
||||||
PropertyAnimation { duration: 200 }
|
}
|
||||||
}
|
|
||||||
}
|
contentItem: Item {
|
||||||
}
|
anchors.fill: focusBorder
|
||||||
}
|
|
||||||
|
implicitWidth: content.implicitWidth
|
||||||
MouseArea {
|
implicitHeight: content.implicitHeight
|
||||||
anchors.fill: focusBorder
|
|
||||||
enabled: false
|
RowLayout {
|
||||||
cursorShape: Qt.PointingHandCursor
|
id: content
|
||||||
}
|
anchors.centerIn: parent
|
||||||
|
|
||||||
contentItem: Item {
|
Image {
|
||||||
anchors.fill: focusBorder
|
id: leftImage
|
||||||
|
source: root.leftImageSource
|
||||||
implicitWidth: content.implicitWidth
|
visible: root.leftImageSource === "" ? false : true
|
||||||
implicitHeight: content.implicitHeight
|
|
||||||
|
layer {
|
||||||
RowLayout {
|
enabled: leftImageColor !== "" ? true : false
|
||||||
id: content
|
effect: ColorOverlay {
|
||||||
anchors.centerIn: parent
|
color: leftImageColor
|
||||||
|
}
|
||||||
Image {
|
}
|
||||||
id: leftImage
|
|
||||||
source: root.leftImageSource
|
Component.onCompleted: {
|
||||||
visible: root.leftImageSource === "" ? false : true
|
if (root.changeLeftImageSize) {
|
||||||
|
leftImage.Layout.preferredHeight = 20
|
||||||
layer {
|
leftImage.Layout.preferredWidth = 20
|
||||||
enabled: leftImageColor !== "" ? true : false
|
}
|
||||||
effect: ColorOverlay {
|
}
|
||||||
color: leftImageColor
|
}
|
||||||
}
|
|
||||||
}
|
ButtonTextType {
|
||||||
|
id: buttonText
|
||||||
Component.onCompleted: {
|
|
||||||
if (root.changeLeftImageSize) {
|
color: root.textColor
|
||||||
leftImage.Layout.preferredHeight = 20
|
text: root.text
|
||||||
leftImage.Layout.preferredWidth = 20
|
visible: root.text === "" ? false : true
|
||||||
}
|
|
||||||
}
|
horizontalAlignment: Text.AlignLeft
|
||||||
}
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
ButtonTextType {
|
|
||||||
id: buttonText
|
Image {
|
||||||
|
Layout.preferredHeight: 20
|
||||||
color: root.textColor
|
Layout.preferredWidth: 20
|
||||||
text: root.text
|
|
||||||
visible: root.text === "" ? false : true
|
source: root.rightImageSource
|
||||||
|
visible: root.rightImageSource === "" ? false : true
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
layer {
|
||||||
}
|
enabled: true
|
||||||
|
effect: ColorOverlay {
|
||||||
Image {
|
color: textColor
|
||||||
Layout.preferredHeight: 20
|
}
|
||||||
Layout.preferredWidth: 20
|
}
|
||||||
|
}
|
||||||
source: root.rightImageSource
|
}
|
||||||
visible: root.rightImageSource === "" ? false : true
|
}
|
||||||
|
|
||||||
layer {
|
Keys.onEnterPressed: {
|
||||||
enabled: true
|
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||||
effect: ColorOverlay {
|
root.clickedFunc()
|
||||||
color: textColor
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
Keys.onReturnPressed: {
|
||||||
}
|
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||||
}
|
root.clickedFunc()
|
||||||
|
}
|
||||||
Keys.onEnterPressed: {
|
}
|
||||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
|
||||||
root.clickedFunc()
|
onClicked: {
|
||||||
}
|
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||||
}
|
root.clickedFunc()
|
||||||
|
}
|
||||||
Keys.onReturnPressed: {
|
}
|
||||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
}
|
||||||
root.clickedFunc()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
|
||||||
root.clickedFunc()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,203 +1,185 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
|
||||||
import "TextTypes"
|
import "TextTypes"
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string headerText
|
property string headerText
|
||||||
property string bodyText
|
property string bodyText
|
||||||
property string footerText
|
property string footerText
|
||||||
|
|
||||||
property string hoveredColor: AmneziaStyle.color.slateGray
|
property string hoveredColor: AmneziaStyle.color.slateGray
|
||||||
property string defaultColor: AmneziaStyle.color.onyxBlack
|
property string defaultColor: AmneziaStyle.color.onyxBlack
|
||||||
|
|
||||||
property string textColor: AmneziaStyle.color.midnightBlack
|
property string textColor: AmneziaStyle.color.midnightBlack
|
||||||
|
|
||||||
property string rightImageSource
|
property string rightImageSource
|
||||||
property string rightImageColor: AmneziaStyle.color.paleGray
|
property string rightImageColor: AmneziaStyle.color.paleGray
|
||||||
|
|
||||||
property string leftImageSource
|
property string leftImageSource
|
||||||
|
|
||||||
property real textOpacity: 1.0
|
property real textOpacity: 1.0
|
||||||
|
|
||||||
property alias focusItem: rightImage
|
property alias focusItem: rightImage
|
||||||
|
|
||||||
property FlickableType parentFlickable
|
hoverEnabled: true
|
||||||
|
|
||||||
hoverEnabled: true
|
background: Rectangle {
|
||||||
|
id: backgroundRect
|
||||||
background: Rectangle {
|
|
||||||
id: backgroundRect
|
anchors.fill: parent
|
||||||
|
radius: 16
|
||||||
anchors.fill: parent
|
|
||||||
radius: 16
|
color: defaultColor
|
||||||
|
|
||||||
color: defaultColor
|
Behavior on color {
|
||||||
|
PropertyAnimation { duration: 200 }
|
||||||
Behavior on color {
|
}
|
||||||
PropertyAnimation { duration: 200 }
|
}
|
||||||
}
|
|
||||||
}
|
contentItem: Item {
|
||||||
|
anchors.left: parent.left
|
||||||
function ensureVisible(item) {
|
anchors.right: parent.right
|
||||||
if (item.activeFocus) {
|
|
||||||
if (root.parentFlickable) {
|
implicitHeight: content.implicitHeight
|
||||||
root.parentFlickable.ensureVisible(root)
|
|
||||||
}
|
RowLayout {
|
||||||
}
|
id: content
|
||||||
}
|
|
||||||
|
anchors.fill: parent
|
||||||
onFocusChanged: {
|
|
||||||
ensureVisible(root)
|
Image {
|
||||||
}
|
id: leftImage
|
||||||
|
source: leftImageSource
|
||||||
focusItem.onFocusChanged: {
|
|
||||||
root.ensureVisible(focusItem)
|
visible: leftImageSource !== ""
|
||||||
}
|
|
||||||
|
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||||
contentItem: Item {
|
Layout.topMargin: 24
|
||||||
anchors.left: parent.left
|
Layout.bottomMargin: 24
|
||||||
anchors.right: parent.right
|
Layout.leftMargin: 24
|
||||||
|
}
|
||||||
implicitHeight: content.implicitHeight
|
|
||||||
|
ColumnLayout {
|
||||||
RowLayout {
|
|
||||||
id: content
|
ListItemTitleType {
|
||||||
|
text: root.headerText
|
||||||
anchors.fill: parent
|
visible: text !== ""
|
||||||
|
|
||||||
Image {
|
Layout.fillWidth: true
|
||||||
id: leftImage
|
Layout.rightMargin: 16
|
||||||
source: leftImageSource
|
Layout.leftMargin: 16
|
||||||
|
Layout.topMargin: 16
|
||||||
visible: leftImageSource !== ""
|
Layout.bottomMargin: root.bodyText !== "" ? 0 : 16
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
opacity: root.textOpacity
|
||||||
Layout.topMargin: 24
|
}
|
||||||
Layout.bottomMargin: 24
|
|
||||||
Layout.leftMargin: 24
|
CaptionTextType {
|
||||||
}
|
text: root.bodyText
|
||||||
|
visible: text !== ""
|
||||||
ColumnLayout {
|
|
||||||
|
color: AmneziaStyle.color.mutedGray
|
||||||
ListItemTitleType {
|
textFormat: Text.RichText
|
||||||
text: root.headerText
|
|
||||||
visible: text !== ""
|
Layout.fillWidth: true
|
||||||
|
Layout.rightMargin: 16
|
||||||
Layout.fillWidth: true
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.bottomMargin: root.footerText !== "" ? 0 : 16
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.topMargin: 16
|
opacity: root.textOpacity
|
||||||
Layout.bottomMargin: root.bodyText !== "" ? 0 : 16
|
}
|
||||||
|
|
||||||
opacity: root.textOpacity
|
ButtonTextType {
|
||||||
}
|
text: root.footerText
|
||||||
|
visible: text !== ""
|
||||||
CaptionTextType {
|
|
||||||
text: root.bodyText
|
color: AmneziaStyle.color.mutedGray
|
||||||
visible: text !== ""
|
|
||||||
|
Layout.fillWidth: true
|
||||||
color: AmneziaStyle.color.mutedGray
|
Layout.rightMargin: 16
|
||||||
textFormat: Text.RichText
|
Layout.leftMargin: 16
|
||||||
|
Layout.topMargin: 16
|
||||||
Layout.fillWidth: true
|
Layout.bottomMargin: 16
|
||||||
Layout.rightMargin: 16
|
|
||||||
Layout.leftMargin: 16
|
opacity: root.textOpacity
|
||||||
Layout.bottomMargin: root.footerText !== "" ? 0 : 16
|
}
|
||||||
|
}
|
||||||
opacity: root.textOpacity
|
|
||||||
}
|
ImageButtonType {
|
||||||
|
id: rightImage
|
||||||
ButtonTextType {
|
|
||||||
text: root.footerText
|
implicitWidth: 40
|
||||||
visible: text !== ""
|
implicitHeight: 40
|
||||||
|
|
||||||
color: AmneziaStyle.color.mutedGray
|
hoverEnabled: false
|
||||||
|
image: rightImageSource
|
||||||
Layout.fillWidth: true
|
imageColor: rightImageColor
|
||||||
Layout.rightMargin: 16
|
visible: rightImageSource ? true : false
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.topMargin: 16
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
Layout.bottomMargin: 16
|
Layout.topMargin: 16
|
||||||
|
Layout.bottomMargin: 16
|
||||||
opacity: root.textOpacity
|
Layout.rightMargin: 16
|
||||||
}
|
|
||||||
}
|
Rectangle {
|
||||||
|
id: rightImageBackground
|
||||||
ImageButtonType {
|
|
||||||
id: rightImage
|
anchors.fill: parent
|
||||||
|
radius: 12
|
||||||
implicitWidth: 40
|
color: "transparent"
|
||||||
implicitHeight: 40
|
|
||||||
|
Behavior on color {
|
||||||
hoverEnabled: false
|
PropertyAnimation { duration: 200 }
|
||||||
image: rightImageSource
|
}
|
||||||
imageColor: rightImageColor
|
}
|
||||||
visible: rightImageSource ? true : false
|
|
||||||
|
onClicked: {
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
root.clicked()
|
||||||
Layout.topMargin: 16
|
}
|
||||||
Layout.bottomMargin: 16
|
}
|
||||||
Layout.rightMargin: 16
|
}
|
||||||
|
}
|
||||||
Rectangle {
|
|
||||||
id: rightImageBackground
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
anchors.fill: parent
|
|
||||||
radius: 12
|
cursorShape: Qt.PointingHandCursor
|
||||||
color: "transparent"
|
hoverEnabled: true
|
||||||
|
enabled: root.enabled
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
onEntered: {
|
||||||
}
|
backgroundRect.color = root.hoveredColor
|
||||||
}
|
|
||||||
|
if (rightImageSource) {
|
||||||
onClicked: {
|
rightImageBackground.color = rightImage.hoveredColor
|
||||||
root.clicked()
|
}
|
||||||
}
|
root.textOpacity = 0.8
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
onExited: {
|
||||||
|
backgroundRect.color = root.defaultColor
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
if (rightImageSource) {
|
||||||
|
rightImageBackground.color = rightImage.defaultColor
|
||||||
cursorShape: Qt.PointingHandCursor
|
}
|
||||||
hoverEnabled: true
|
root.textOpacity = 1
|
||||||
enabled: root.enabled
|
}
|
||||||
|
|
||||||
onEntered: {
|
onPressedChanged: {
|
||||||
backgroundRect.color = root.hoveredColor
|
if (rightImageSource) {
|
||||||
|
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
||||||
if (rightImageSource) {
|
}
|
||||||
rightImageBackground.color = rightImage.hoveredColor
|
root.textOpacity = 0.7
|
||||||
}
|
}
|
||||||
root.textOpacity = 0.8
|
|
||||||
}
|
onClicked: {
|
||||||
|
root.clicked()
|
||||||
onExited: {
|
}
|
||||||
backgroundRect.color = root.defaultColor
|
}
|
||||||
|
}
|
||||||
if (rightImageSource) {
|
|
||||||
rightImageBackground.color = rightImage.defaultColor
|
|
||||||
}
|
|
||||||
root.textOpacity = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
onPressedChanged: {
|
|
||||||
if (rightImageSource) {
|
|
||||||
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
|
||||||
}
|
|
||||||
root.textOpacity = 0.7
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
root.clicked()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,170 +1,189 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Qt5Compat.GraphicalEffects
|
import Qt5Compat.GraphicalEffects
|
||||||
|
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
|
||||||
import "TextTypes"
|
import "TextTypes"
|
||||||
|
|
||||||
CheckBox {
|
CheckBox {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string descriptionText
|
property string descriptionText
|
||||||
property string descriptionTextColor: AmneziaStyle.color.mutedGray
|
property string descriptionTextColor: AmneziaStyle.color.mutedGray
|
||||||
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
|
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
|
||||||
|
|
||||||
property string textColor: AmneziaStyle.color.paleGray
|
property string textColor: AmneziaStyle.color.paleGray
|
||||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
||||||
|
|
||||||
property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite
|
property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite
|
||||||
property string defaultColor: AmneziaStyle.color.transparent
|
property string defaultColor: AmneziaStyle.color.transparent
|
||||||
property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite
|
property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite
|
||||||
|
|
||||||
property string defaultBorderColor: AmneziaStyle.color.paleGray
|
property string defaultBorderColor: AmneziaStyle.color.paleGray
|
||||||
property string checkedBorderColor: AmneziaStyle.color.goldenApricot
|
property string checkedBorderColor: AmneziaStyle.color.goldenApricot
|
||||||
property string checkedBorderDisabledColor: AmneziaStyle.color.deepBrown
|
property string checkedBorderDisabledColor: AmneziaStyle.color.deepBrown
|
||||||
|
|
||||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||||
|
|
||||||
property string checkedImageColor: AmneziaStyle.color.goldenApricot
|
property string checkedImageColor: AmneziaStyle.color.goldenApricot
|
||||||
property string pressedImageColor: AmneziaStyle.color.burntOrange
|
property string pressedImageColor: AmneziaStyle.color.burntOrange
|
||||||
property string defaultImageColor: AmneziaStyle.color.transparent
|
property string defaultImageColor: AmneziaStyle.color.transparent
|
||||||
property string checkedDisabledImageColor: AmneziaStyle.color.mutedBrown
|
property string checkedDisabledImageColor: AmneziaStyle.color.mutedBrown
|
||||||
|
|
||||||
property string imageSource: "qrc:/images/controls/check.svg"
|
property string imageSource: "qrc:/images/controls/check.svg"
|
||||||
|
|
||||||
property var parentFlickable
|
property bool isFocusable: true
|
||||||
onFocusChanged: {
|
|
||||||
if (root.activeFocus) {
|
Keys.onTabPressed: {
|
||||||
if (root.parentFlickable) {
|
FocusController.nextKeyTabItem()
|
||||||
root.parentFlickable.ensureVisible(root)
|
}
|
||||||
}
|
|
||||||
}
|
Keys.onBacktabPressed: {
|
||||||
}
|
FocusController.previousKeyTabItem()
|
||||||
|
}
|
||||||
hoverEnabled: enabled ? true : false
|
|
||||||
focusPolicy: Qt.NoFocus
|
Keys.onUpPressed: {
|
||||||
|
FocusController.nextKeyUpItem()
|
||||||
background: Rectangle {
|
}
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
border.color: root.focus ? borderFocusedColor : AmneziaStyle.color.transparent
|
Keys.onDownPressed: {
|
||||||
border.width: 1
|
FocusController.nextKeyDownItem()
|
||||||
radius: 16
|
}
|
||||||
}
|
|
||||||
|
Keys.onLeftPressed: {
|
||||||
indicator: Rectangle {
|
FocusController.nextKeyLeftItem()
|
||||||
id: background
|
}
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
Keys.onRightPressed: {
|
||||||
|
FocusController.nextKeyRightItem()
|
||||||
implicitWidth: 56
|
}
|
||||||
implicitHeight: 56
|
|
||||||
radius: 16
|
hoverEnabled: enabled ? true : false
|
||||||
|
focusPolicy: Qt.NoFocus
|
||||||
color: {
|
|
||||||
if (root.hovered) {
|
background: Rectangle {
|
||||||
return hoveredColor
|
color: AmneziaStyle.color.transparent
|
||||||
}
|
border.color: root.focus ? borderFocusedColor : AmneziaStyle.color.transparent
|
||||||
return defaultColor
|
border.width: 1
|
||||||
}
|
radius: 16
|
||||||
|
}
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
indicator: Rectangle {
|
||||||
}
|
id: background
|
||||||
|
|
||||||
Rectangle {
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
id: imageBorder
|
|
||||||
|
implicitWidth: 56
|
||||||
anchors.centerIn: parent
|
implicitHeight: 56
|
||||||
width: 24
|
radius: 16
|
||||||
height: 24
|
|
||||||
color: AmneziaStyle.color.transparent
|
color: {
|
||||||
border.color: root.checked ?
|
if (root.hovered) {
|
||||||
(root.enabled ?
|
return hoveredColor
|
||||||
checkedBorderColor :
|
}
|
||||||
checkedBorderDisabledColor) :
|
return defaultColor
|
||||||
defaultBorderColor
|
}
|
||||||
border.width: 1
|
|
||||||
radius: 4
|
Behavior on color {
|
||||||
|
PropertyAnimation { duration: 200 }
|
||||||
Image {
|
}
|
||||||
anchors.centerIn: parent
|
|
||||||
|
Rectangle {
|
||||||
source: root.pressed ? imageSource : root.checked ? imageSource : ""
|
id: imageBorder
|
||||||
layer {
|
|
||||||
enabled: true
|
anchors.centerIn: parent
|
||||||
effect: ColorOverlay {
|
width: 24
|
||||||
color: {
|
height: 24
|
||||||
if (root.pressed) {
|
color: AmneziaStyle.color.transparent
|
||||||
return root.pressedImageColor
|
border.color: root.checked ?
|
||||||
} else if (root.checked) {
|
(root.enabled ?
|
||||||
if (root.enabled) {
|
checkedBorderColor :
|
||||||
return root.checkedImageColor
|
checkedBorderDisabledColor) :
|
||||||
} else {
|
defaultBorderColor
|
||||||
return root.checkedDisabledImageColor
|
border.width: 1
|
||||||
}
|
radius: 4
|
||||||
} else {
|
|
||||||
return root.defaultImageColor
|
Image {
|
||||||
}
|
anchors.centerIn: parent
|
||||||
}
|
|
||||||
}
|
source: root.pressed ? imageSource : root.checked ? imageSource : ""
|
||||||
}
|
layer {
|
||||||
}
|
enabled: true
|
||||||
}
|
effect: ColorOverlay {
|
||||||
}
|
color: {
|
||||||
|
if (root.pressed) {
|
||||||
contentItem: Item {
|
return root.pressedImageColor
|
||||||
anchors.left: parent.left
|
} else if (root.checked) {
|
||||||
anchors.right: parent.right
|
if (root.enabled) {
|
||||||
anchors.leftMargin: 8 + background.width
|
return root.checkedImageColor
|
||||||
|
} else {
|
||||||
implicitHeight: content.implicitHeight
|
return root.checkedDisabledImageColor
|
||||||
|
}
|
||||||
ColumnLayout {
|
} else {
|
||||||
id: content
|
return root.defaultImageColor
|
||||||
|
}
|
||||||
anchors.left: parent.left
|
}
|
||||||
anchors.right: parent.right
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
}
|
||||||
|
}
|
||||||
spacing: 4
|
}
|
||||||
|
}
|
||||||
ListItemTitleType {
|
|
||||||
Layout.fillWidth: true
|
contentItem: Item {
|
||||||
|
anchors.left: parent.left
|
||||||
text: root.text
|
anchors.right: parent.right
|
||||||
color: root.enabled ? root.textColor : root.textDisabledColor
|
anchors.leftMargin: 8 + background.width
|
||||||
}
|
|
||||||
|
implicitHeight: content.implicitHeight
|
||||||
CaptionTextType {
|
|
||||||
id: description
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
Layout.fillWidth: true
|
|
||||||
|
anchors.left: parent.left
|
||||||
text: root.descriptionText
|
anchors.right: parent.right
|
||||||
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
visible: root.descriptionText !== ""
|
spacing: 4
|
||||||
}
|
|
||||||
}
|
ListItemTitleType {
|
||||||
}
|
Layout.fillWidth: true
|
||||||
|
|
||||||
MouseArea {
|
text: root.text
|
||||||
anchors.fill: parent
|
color: root.enabled ? root.textColor : root.textDisabledColor
|
||||||
cursorShape: Qt.PointingHandCursor
|
}
|
||||||
enabled: false
|
|
||||||
}
|
CaptionTextType {
|
||||||
|
id: description
|
||||||
|
|
||||||
Keys.onEnterPressed: {
|
Layout.fillWidth: true
|
||||||
root.checked = !root.checked
|
|
||||||
}
|
text: root.descriptionText
|
||||||
|
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
|
||||||
Keys.onReturnPressed: {
|
|
||||||
root.checked = !root.checked
|
visible: root.descriptionText !== ""
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onEnterPressed: event => handleSwitch(event)
|
||||||
|
Keys.onReturnPressed: event => handleSwitch(event)
|
||||||
|
Keys.onSpacePressed: event => handleSwitch(event)
|
||||||
|
|
||||||
|
function handleSwitch(event) {
|
||||||
|
if (!event.isAutoRepeat) {
|
||||||
|
root.checked = !root.checked
|
||||||
|
root.checkedChanged()
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||