diff --git a/.clang-format b/.clang-format
new file mode 100644
index 00000000..5c459fd2
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,39 @@
+BasedOnStyle: WebKit
+AccessModifierOffset: '-4'
+AlignAfterOpenBracket: Align
+AlignConsecutiveMacros: 'true'
+AlignTrailingComments: 'true'
+AllowAllArgumentsOnNextLine: 'true'
+AllowAllParametersOfDeclarationOnNextLine: 'true'
+AllowShortBlocksOnASingleLine: 'false'
+AllowShortCaseLabelsOnASingleLine: 'true'
+AllowShortEnumsOnASingleLine: 'false'
+AllowShortFunctionsOnASingleLine: None
+AlwaysBreakTemplateDeclarations: 'No'
+BreakBeforeBinaryOperators: NonAssignment
+BreakBeforeBraces: Custom
+BraceWrapping:
+ AfterClass: true
+ AfterControlStatement: false
+ AfterEnum: false
+ AfterFunction: true
+ AfterNamespace: true
+ AfterObjCDeclaration: false
+ AfterStruct: true
+ AfterUnion: false
+ BeforeCatch: false
+ BeforeElse: false
+ IndentBraces: false
+BreakConstructorInitializers: BeforeColon
+ColumnLimit: '120'
+CommentPragmas: '"^!|^:"'
+ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
+ConstructorInitializerIndentWidth: '4'
+ContinuationIndentWidth: '8'
+IndentPPDirectives: BeforeHash
+NamespaceIndentation: All
+PenaltyExcessCharacter: '10'
+PointerAlignment: Right
+SortIncludes: 'true'
+SpaceAfterTemplateKeyword: 'false'
+Standard: Auto
diff --git a/.clang-format-ignore b/.clang-format-ignore
new file mode 100644
index 00000000..4019357f
--- /dev/null
+++ b/.clang-format-ignore
@@ -0,0 +1,20 @@
+/client/3rd
+/client/3rd-prebuild
+/client/android
+/client/cmake
+/client/core/serialization
+/client/daemon
+/client/fonts
+/client/images
+/client/ios
+/client/mozilla
+/client/platforms/dummy
+/client/platforms/linux
+/client/platforms/macos
+/client/platforms/windows
+/client/server_scripts
+/client/translations
+/deploy
+/docs
+/metadata
+/service/src
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 0ca8e075..35e740b0 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -10,12 +10,16 @@ env:
jobs:
Build-Linux-Ubuntu:
- name: 'Build-Linux-Ubuntu'
runs-on: ubuntu-20.04
env:
QT_VERSION: 6.6.2
QIF_VERSION: 4.7
+ 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 }}
steps:
- name: 'Install Qt'
@@ -65,16 +69,27 @@ jobs:
path: deploy/AppDir
retention-days: 7
+ - name: 'Upload translations artifact'
+ uses: actions/upload-artifact@v4
+ with:
+ name: AmneziaVPN_translations
+ path: client/translations
+ retention-days: 7
+
# ------------------------------------------------------
Build-Windows:
- name: Build-Windows
runs-on: windows-latest
env:
QT_VERSION: 6.6.2
QIF_VERSION: 4.7
BUILD_ARCH: 64
+ 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 }}
steps:
- name: 'Get sources'
@@ -130,13 +145,17 @@ jobs:
# ------------------------------------------------------
Build-iOS:
- name: 'Build-iOS'
runs-on: macos-13
env:
QT_VERSION: 6.6.2
CC: cc
CXX: c++
+ 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 }}
steps:
- name: 'Setup xcode'
@@ -171,7 +190,7 @@ jobs:
- name: 'Install go'
uses: actions/setup-go@v5
with:
- go-version: '1.20'
+ go-version: '1.22.1'
cache: false
- name: 'Setup gomobile'
@@ -198,7 +217,11 @@ jobs:
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/ios/bin"
export QT_MACOS_ROOT_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos"
export PATH=$PATH:~/go/bin
- sh deploy/build_ios.sh
+ sh deploy/build_ios.sh | \
+ sed -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/d' | \
+ sed -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/d' | \
+ sed -e '/-DPROD_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DPROD_AGW_PUBLIC_KEY/d' | \
+ sed -e '/-DDEV_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DDEV_AGW_PUBLIC_KEY/d'
env:
IOS_TRUST_CERT_BASE64: ${{ secrets.IOS_TRUST_CERT_BASE64 }}
IOS_SIGNING_CERT_BASE64: ${{ secrets.IOS_SIGNING_CERT_BASE64 }}
@@ -221,19 +244,23 @@ jobs:
# ------------------------------------------------------
Build-MacOS:
- name: 'Build-MacOS'
runs-on: macos-latest
env:
# Keep compat with MacOS 10.15 aka Catalina by Qt 6.4
QT_VERSION: 6.4.3
QIF_VERSION: 4.6
+ 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 }}
steps:
- name: 'Setup xcode'
uses: maxim-lobanov/setup-xcode@v1
with:
- xcode-version: '14.3.1'
+ xcode-version: '15.4.0'
- name: 'Install Qt'
uses: jurplel/install-qt-action@v3
@@ -286,28 +313,33 @@ jobs:
# ------------------------------------------------------
Build-Android:
- name: 'Build-Android'
runs-on: ubuntu-latest
env:
ANDROID_BUILD_PLATFORM: android-34
- QT_VERSION: 6.6.2
+ QT_VERSION: 6.7.3
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
+ 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 }}
steps:
- name: 'Install desktop Qt'
- uses: jurplel/install-qt-action@v3
+ uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'desktop'
- arch: 'gcc_64'
+ arch: 'linux_gcc_64'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
- extra: '--external 7z --base ${{ env.QT_MIRROR }}'
+ py7zrversion: '==0.22.*'
+ extra: '--base ${{ env.QT_MIRROR }}'
- name: 'Install android_x86_64 Qt'
- uses: jurplel/install-qt-action@v3
+ uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
@@ -315,10 +347,11 @@ jobs:
arch: 'android_x86_64'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
- extra: '--external 7z --base ${{ env.QT_MIRROR }}'
+ py7zrversion: '==0.22.*'
+ extra: '--base ${{ env.QT_MIRROR }}'
- name: 'Install android_x86 Qt'
- uses: jurplel/install-qt-action@v3
+ uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
@@ -326,10 +359,11 @@ jobs:
arch: 'android_x86'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
- extra: '--external 7z --base ${{ env.QT_MIRROR }}'
+ py7zrversion: '==0.22.*'
+ extra: '--base ${{ env.QT_MIRROR }}'
- name: 'Install android_armv7 Qt'
- uses: jurplel/install-qt-action@v3
+ uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
@@ -337,10 +371,11 @@ jobs:
arch: 'android_armv7'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
- extra: '--external 7z --base ${{ env.QT_MIRROR }}'
+ py7zrversion: '==0.22.*'
+ extra: '--base ${{ env.QT_MIRROR }}'
- name: 'Install android_arm64_v8a Qt'
- uses: jurplel/install-qt-action@v3
+ uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
@@ -348,7 +383,8 @@ jobs:
arch: 'android_arm64_v8a'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
- extra: '--external 7z --base ${{ env.QT_MIRROR }}'
+ py7zrversion: '==0.22.*'
+ extra: '--base ${{ env.QT_MIRROR }}'
- name: 'Grant execute permission for qt-cmake'
shell: bash
@@ -432,3 +468,21 @@ jobs:
path: deploy/build/AmneziaVPN-release.aab
compression-level: 0
retention-days: 7
+
+ Extra:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Search a corresponding PR
+ uses: octokit/request-action@v2.x
+ id: pull_request
+ with:
+ route: GET /repos/${{ github.repository }}/pulls
+ head: ${{ github.repository_owner }}:${{ github.ref_name }}
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Add PR link to build summary
+ if: ${{ fromJSON(steps.pull_request.outputs.data)[0].number != '' }}
+ run: |
+ echo "Pull request:" >> $GITHUB_STEP_SUMMARY
+ echo "[[#${{ fromJSON(steps.pull_request.outputs.data)[0].number }}] ${{ fromJSON(steps.pull_request.outputs.data)[0].title }}](${{ fromJSON(steps.pull_request.outputs.data)[0].html_url }})" >> $GITHUB_STEP_SUMMARY
diff --git a/.github/workflows/tag-deploy.yml b/.github/workflows/tag-deploy.yml
index b88390f4..2bcbd8c6 100644
--- a/.github/workflows/tag-deploy.yml
+++ b/.github/workflows/tag-deploy.yml
@@ -15,6 +15,11 @@ jobs:
env:
QT_VERSION: 6.4.1
QIF_VERSION: 4.5
+ 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 }}
steps:
- name: 'Install desktop Qt'
diff --git a/.gitmodules b/.gitmodules
index 78d45e25..decab9b7 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
-[submodule "client/3rd/OpenVPNAdapter"]
- path = client/3rd/OpenVPNAdapter
- url = https://github.com/amnezia-vpn/OpenVPNAdapter.git
[submodule "client/3rd/qtkeychain"]
path = client/3rd/qtkeychain
url = https://github.com/frankosterfeld/qtkeychain.git
@@ -13,3 +10,6 @@
[submodule "client/3rd/amneziawg-apple"]
path = client/3rd/amneziawg-apple
url = https://github.com/amnezia-vpn/amneziawg-apple
+[submodule "client/3rd/QSimpleCrypto"]
+ path = client/3rd/QSimpleCrypto
+ url = https://github.com/amnezia-vpn/QSimpleCrypto.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2dbf778d..0ccae139 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
-project(${PROJECT} VERSION 4.6.0.1
+project(${PROJECT} VERSION 4.8.4.4
DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/"
)
@@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
-set(APP_ANDROID_VERSION_CODE 54)
+set(APP_ANDROID_VERSION_CODE 2081)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")
diff --git a/README.md b/README.md
index 590b76bc..992c3ad0 100644
--- a/README.md
+++ b/README.md
@@ -1,25 +1,51 @@
# Amnezia VPN
-## _The best client for self-hosted VPN_
+
+### _The best client for self-hosted VPN_
+
[](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml?query=branch:dev)
[](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
-Amnezia is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
+### [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.
+
+[](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)
+
+> [!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 ).
+
+
+
+
+[All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
+
+
+
+
## Features
-- Very easy to use - enter your IP address, SSH login, and password, and Amnezia will automatically install VPN docker containers to your server and connect to the VPN.
-- OpenVPN, Shadowsocks, WireGuard, and IKEv2 protocols support.
-- Masking VPN with OpenVPN over Cloak plugin
-- Split tunneling support - add any sites to the client to enable VPN only for them (only for desktops)
+- Very easy to use - enter your IP address, SSH login, password and Amnezia will automatically install VPN docker containers to your server and connect to the VPN.
+- Classic VPN-protocols: OpenVPN, WireGuard and IKEv2 protocols.
+- Protocols with traffic Masking (Obfuscation): OpenVPN over [Cloak](https://github.com/cbeuw/Cloak) plugin, Shadowsocks (OpenVPN over Shadowsocks), [AmneziaWG](https://docs.amnezia.org/documentation/amnezia-wg/) and XRay.
+- Split tunneling support - add any sites to the client to enable VPN only for them or add Apps (only for Android and Desktop).
- Windows, MacOS, Linux, Android, iOS releases.
+- Support for AmneziaWG protocol configuration on [Keenetic beta firmware](https://docs.keenetic.com/ua/air/kn-1611/en/6319-latest-development-release.html#UUID-186c4108-5afd-c10b-f38a-cdff6c17fab3_section-idm33192196168192-improved).
## Links
-[https://amnezia.org](https://amnezia.org) - project website
-[https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
-[https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Telegram support channel (English)
-[https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Telegram support channel (Russian)
+- [https://amnezia.org](https://amnezia.org) - Project website | [Alternative link (mirror)](https://storage.googleapis.com/kldscp/amnezia.org)
+- [https://docs.amnezia.org](https://docs.amnezia.org) - Documentation
+- [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
+- [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Telegram support channel (English)
+- [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Telegram support channel (Farsi)
+- [https://t.me/amnezia_vpn_mm](https://t.me/amnezia_vpn_mm) - Telegram support channel (Myanmar)
+- [https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Telegram support channel (Russian)
+- [https://vpnpay.io/en/amnezia-premium/](https://vpnpay.io/en/amnezia-premium/) - Amnezia Premium
## Tech
@@ -44,6 +70,19 @@ git submodule update --init --recursive
Want to contribute? Welcome!
+### Help with translations
+
+Download the most actual translation files.
+
+Go to ["Actions" tab](https://github.com/amnezia-vpn/amnezia-client/actions?query=is%3Asuccess+branch%3Adev), click on the first line.
+Then scroll down to the "Artifacts" section and download "AmneziaVPN_translations".
+
+Unzip this file.
+Each *.ts file contains strings for one corresponding language.
+
+Translate or correct some strings in one or multiple *.ts files and commit them back to this repository into the ``client/translations`` folder.
+You can do it via a web-interface or any other method you're familiar with.
+
### Building sources and deployment
Check deploy folder for build scripts.
@@ -52,7 +91,7 @@ Check deploy folder for build scripts.
1. First, make sure you have [XCode](https://developer.apple.com/xcode/) installed, at least version 14 or higher.
-2. We use QT to generate the XCode project. We need QT version 6.6.1. Install QT for MacOS [here](https://doc.qt.io/qt-6/macos.html) or [QT Online Installer](https://www.qt.io/download-open-source). Required modules:
+2. We use QT to generate the XCode project. We need QT version 6.6.2. Install QT for MacOS [here](https://doc.qt.io/qt-6/macos.html) or [QT Online Installer](https://www.qt.io/download-open-source). Required modules:
- MacOS
- iOS
- Qt 5 Compatibility Module
@@ -119,9 +158,11 @@ The Android app has the following requirements:
* Android platform SDK 33
* CMake 3.25.0
-After you have installed QT, QT Creator, and Android Studio, you need to configure QT Creator correctly. Click in the top menu bar on `QT Creator` -> `Preferences` -> `Devices` and select the tab `Android`.
- * set path to JDK 11
- * set path to Android SDK ($ANDROID_HOME)
+After you have installed QT, QT Creator, and Android Studio, you need to configure QT Creator correctly.
+
+- Click in the top menu bar on `QT Creator` -> `Preferences` -> `Devices` and select the tab `Android`.
+- Set path to JDK 11
+- Set path to Android SDK (`$ANDROID_HOME`)
In case you get errors regarding missing SDK or 'SDK manager not running', you cannot fix them by correcting the paths. If you have some spare GBs on your disk, you can let QT Creator install all requirements by choosing an empty folder for `Android SDK location` and clicking on `Set Up SDK`. Be aware: This will install a second Android SDK and NDK on your machine!
Double-check that the right CMake version is configured: Click on `QT Creator` -> `Preferences` and click on the side menu on `Kits`. Under the center content view's `Kits` tab, you'll find an entry for `CMake Tool`. If the default selected CMake version is lower than 3.25.0, install on your system CMake >= 3.25.0 and choose `System CMake at ` from the drop-down list. If this entry is missing, you either have not installed CMake yet or QT Creator hasn't found the path to it. In that case, click in the preferences window on the side menu item `CMake`, then on the tab `Tools` in the center content view, and finally on the button `Add` to set the path to your installed CMake.
@@ -142,11 +183,13 @@ GPL v3.0
## Donate
-Bitcoin: bc1qn9rhsffuxwnhcuuu4qzrwp4upkrq94xnh8r26u
-XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3
-payeer.com: P2561305
-ko-fi.com: [https://ko-fi.com/amnezia_vpn](https://ko-fi.com/amnezia_vpn)
+Patreon: [https://www.patreon.com/amneziavpn](https://www.patreon.com/amneziavpn)
+Bitcoin: bc1qmhtgcf9637rl3kqyy22r2a8wa8laka4t9rx2mf
+USDT BEP20: 0x6abD576765a826f87D1D95183438f9408C901bE4
+USDT TRC20: TELAitazF1MZGmiNjTcnxDjEiH5oe7LC9d
+XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3
+TON: UQDpU1CyKRmg7L8mNScKk9FRc2SlESuI7N-Hby4nX-CcVmns
## Acknowledgments
This project is tested with BrowserStack.
diff --git a/README_RU.md b/README_RU.md
new file mode 100644
index 00000000..44681875
--- /dev/null
+++ b/README_RU.md
@@ -0,0 +1,181 @@
+# Amnezia VPN
+
+### _Лучший клиент для создания VPN на собственном сервере_
+
+[](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml?query=branch:dev)
+[](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
+
+### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
+[AmneziaVPN](https://amnezia.org) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
+
+[](https://amnezia.org)
+
+### [Сайт](https://amnezia.org) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
+
+> [!TIP]
+> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org).
+
+
+
+
+[Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases)
+
+
+
+
+
+## Особенности
+
+- Простой в использовании — введите IP-адрес, SSH-логин и пароль, и Amnezia автоматически установит VPN-контейнеры Docker на ваш сервер и подключится к VPN.
+- Классические VPN-протоколы: OpenVPN, WireGuard и IKEv2.
+- Протоколы с маскировкой трафика (обфускацией): OpenVPN с плагином [Cloak](https://github.com/cbeuw/Cloak), Shadowsocks (OpenVPN over Shadowsocks), [AmneziaWG](https://docs.amnezia.org/documentation/amnezia-wg/) and XRay.
+- Поддержка Split Tunneling — добавляйте любые сайты или приложения в список, чтобы включить VPN только для них.
+- Поддерживает платформы: Windows, macOS, Linux, Android, iOS.
+- Поддержка конфигурации протокола AmneziaWG на [бета-прошивке Keenetic](https://docs.keenetic.com/ua/air/kn-1611/en/6319-latest-development-release.html#UUID-186c4108-5afd-c10b-f38a-cdff6c17fab3_section-idm33192196168192-improved).
+
+## Ссылки
+
+- [https://amnezia.org](https://amnezia.org) - Веб-сайт проекта | [Альтернативная ссылка (зеркало)](https://storage.googleapis.com/kldscp/amnezia.org)
+- [https://docs.amnezia.org](https://docs.amnezia.org) - Документация
+- [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
+- [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Канал поддержки в Telegram (Английский)
+- [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Канал поддержки в Telegram (Фарси)
+- [https://t.me/amnezia_vpn_mm](https://t.me/amnezia_vpn_mm) - Канал поддержки в Telegram (Мьянма)
+- [https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Канал поддержки в Telegram (Русский)
+- [https://vpnpay.io/en/amnezia-premium/](https://vpnpay.io/en/amnezia-premium/) - Amnezia Premium | [Зеркало](https://storage.googleapis.com/kldscp/vpnpay.io/ru/amnezia-premium\)
+
+## Технологии
+
+AmneziaVPN использует несколько проектов с открытым исходным кодом:
+
+- [OpenSSL](https://www.openssl.org/)
+- [OpenVPN](https://openvpn.net/)
+- [Shadowsocks](https://shadowsocks.org/)
+- [Qt](https://www.qt.io/)
+- [LibSsh](https://libssh.org)
+- и другие...
+
+## Проверка исходного кода
+После клонирования репозитория обязательно загрузите все подмодули.
+
+```bash
+git submodule update --init --recursive
+```
+
+
+## Разработка
+Хотите внести свой вклад? Добро пожаловать!
+
+### Помощь с переводами
+
+Загрузите самые актуальные файлы перевода.
+
+Перейдите на [вкладку "Actions"](https://github.com/amnezia-vpn/amnezia-client/actions?query=is%3Asuccess+branch%3Adev), нажмите на первую строку. Затем прокрутите вниз до раздела "Artifacts" и скачайте "AmneziaVPN_translations".
+
+Распакуйте этот файл. Каждый файл с расширением *.ts содержит строки для соответствующего языка.
+
+Переведите или исправьте строки в одном или нескольких файлах *.ts и загрузите их обратно в этот репозиторий в папку ``client/translations``. Это можно сделать через веб-интерфейс или любым другим знакомым вам способом.
+
+### Сборка исходного кода и деплой
+Проверьте папку deploy для скриптов сборки.
+
+### Как собрать iOS-приложение из исходного кода на MacOS
+1. Убедитесь, что у вас установлен Xcode версии 14 или выше.
+2. Для генерации проекта Xcode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули:
+- MacOS
+- iOS
+- Модуль совместимости с Qt 5
+- Qt Shader Tools
+- Дополнительные библиотеки:
+ - Qt Image Formats
+ - Qt Multimedia
+ - Qt Remote Objects
+
+
+3. Установите CMake, если это необходимо. Рекомендуемая версия — 3.25. Скачать CMake можно здесь.
+4. Установите Go версии >= v1.16. Если Go ещё не установлен, скачайте его с [официального сайта](https://golang.org/dl/) или используйте Homebrew. Установите gomobile:
+
+```bash
+export PATH=$PATH:~/go/bin
+go install golang.org/x/mobile/cmd/gomobile@latest
+gomobile init
+```
+
+5. Соберите проект:
+```bash
+export QT_BIN_DIR="/Qt//ios/bin"
+export QT_MACOS_ROOT_DIR="/Qt//macos"
+export QT_IOS_BIN=$QT_BIN_DIR
+export PATH=$PATH:~/go/bin
+mkdir build-ios
+$QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR
+```
+Замените и на ваши значения.
+
+Если появляется ошибка gomobile: command not found, убедитесь, что PATH настроен на папку bin, где установлен gomobile:
+```bash
+export PATH=$(PATH):/path/to/GOPATH/bin
+```
+
+6. Откройте проект в Xcode. Теперь вы можете тестировать, архивировать или публиковать приложение.
+
+Если сборка завершится с ошибкой:
+```
+make: ***
+[$(PROJECTDIR)/client/build/AmneziaVPN.build/Debug-iphoneos/wireguard-go-bridge/goroot/.prepared]
+Error 1
+```
+Добавьте пользовательскую переменную PATH в настройки сборки для целей AmneziaVPN и WireGuardNetworkExtension с ключом `PATH` и значением `${PATH}/path/to/bin/folder/with/go/executable`, e.g. `${PATH}:/usr/local/go/bin`.
+
+Если ошибка повторяется на Mac с M1, установите версию CMake для архитектуры ARM:
+```
+arch -arm64 brew install cmake
+```
+
+ При первой попытке сборка может завершиться с ошибкой source files not found. Это происходит из-за параллельной компиляции зависимостей в XCode. Просто перезапустите сборку.
+
+
+## Как собрать Android-приложение
+Сборка тестировалась на MacOS. Требования:
+- JDK 11
+- Android SDK 33
+- CMake 3.25.0
+
+Установите QT, QT Creator и Android Studio.
+Настройте QT Creator:
+
+- В меню QT Creator перейдите в `QT Creator` -> `Preferences` -> `Devices` ->`Android`.
+- Укажите путь к JDK 11.
+- Укажите путь к Android SDK (`$ANDROID_HOME`)
+
+Если вы сталкиваетесь с ошибками, связанными с отсутствием SDK или сообщением «SDK manager not running», их нельзя исправить просто корректировкой путей. Если у вас есть несколько свободных гигабайт на диске, вы можете позволить Qt Creator установить все необходимые компоненты, выбрав пустую папку для расположения Android SDK и нажав кнопку **Set Up SDK**. Учтите: это установит второй Android SDK и NDK на вашем компьютере!
+
+Убедитесь, что настроена правильная версия CMake: перейдите в **Qt Creator -> Preferences** и в боковом меню выберите пункт **Kits**. В центральной части окна, на вкладке **Kits**, найдите запись для инструмента **CMake Tool**. Если выбранная по умолчанию версия CMake ниже 3.25.0, установите на свою систему CMake версии 3.25.0 или выше, а затем выберите опцию **System CMake at <путь>** из выпадающего списка. Если этот пункт отсутствует, это может означать, что вы еще не установили CMake, или Qt Creator не смог найти путь к нему. В таком случае в окне **Preferences** перейдите в боковое меню **CMake**, затем во вкладку **Tools** в центральной части окна и нажмите кнопку **Add**, чтобы указать путь к установленному CMake.
+
+Убедитесь, что для вашего проекта выбрана Android Platform SDK 33: в главном окне на боковой панели выберите пункт **Projects**, и слева вы увидите раздел **Build & Run**, показывающий различные целевые Android-платформы. Вы можете выбрать любую из них, так как настройка проекта Amnezia VPN разработана таким образом, чтобы все Android-цели могли быть собраны. Перейдите в подраздел **Build** и прокрутите центральную часть окна до раздела **Build Steps**. Нажмите **Details** в заголовке **Build Android APK** (кнопка **Details** может быть скрыта, если окно Qt Creator не запущено в полноэкранном режиме!). Вот здесь выберите **android-33** в качестве Android Build Platform SDK.
+
+### Разработка Android-компонентов
+
+После сборки QT Creator копирует проект в отдельную папку, например, `build-amnezia-client-Android_Qt__Clang_-`. Для разработки Android-компонентов откройте сгенерированный проект в Android Studio, указав папку `build-amnezia-client-Android_Qt__Clang_-/client/android-build` в качестве корневой.
+Изменения в сгенерированном проекте нужно вручную перенести в репозиторий. После этого можно коммитить изменения.
+Если возникают проблемы со сборкой в QT Creator после работы в Android Studio, выполните команду `./gradlew clean` в корневой папке сгенерированного проекта (`/client/android-build/.`).
+
+
+## Лицензия
+
+GPL v3.0
+
+## Донаты
+
+Patreon: [https://www.patreon.com/amneziavpn](https://www.patreon.com/amneziavpn)
+
+Bitcoin: bc1qmhtgcf9637rl3kqyy22r2a8wa8laka4t9rx2mf
+USDT BEP20: 0x6abD576765a826f87D1D95183438f9408C901bE4
+USDT TRC20: TELAitazF1MZGmiNjTcnxDjEiH5oe7LC9d
+XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3
+TON: UQDpU1CyKRmg7L8mNScKk9FRc2SlESuI7N-Hby4nX-CcVmns
+
+## Благодарности
+
+Этот проект тестируется с помощью BrowserStack.
+Мы выражаем благодарность [BrowserStack](https://www.browserstack.com) за поддержку нашего проекта.
diff --git a/client/3rd-prebuilt b/client/3rd-prebuilt
index ea49bf87..efad1a5b 160000
--- a/client/3rd-prebuilt
+++ b/client/3rd-prebuilt
@@ -1 +1 @@
-Subproject commit ea49bf8796afbc5bd70a0f98f4d99c9ea4792d80
+Subproject commit efad1a5b5cb8e8ab61e49ccdca18c9090a0da8d3
diff --git a/client/3rd/OpenVPNAdapter b/client/3rd/OpenVPNAdapter
deleted file mode 160000
index 7c821a8d..00000000
--- a/client/3rd/OpenVPNAdapter
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 7c821a8d5c1ad5ad94e0763b4f25a875b5a6fe1b
diff --git a/client/3rd/QSimpleCrypto b/client/3rd/QSimpleCrypto
new file mode 160000
index 00000000..c99b33f0
--- /dev/null
+++ b/client/3rd/QSimpleCrypto
@@ -0,0 +1 @@
+Subproject commit c99b33f0e08b7206116ddff85c22d3b97ce1e79d
diff --git a/client/3rd/QSimpleCrypto/QSimpleCrypto.cmake b/client/3rd/QSimpleCrypto/QSimpleCrypto.cmake
deleted file mode 100644
index 7ec5498a..00000000
--- a/client/3rd/QSimpleCrypto/QSimpleCrypto.cmake
+++ /dev/null
@@ -1,20 +0,0 @@
-include_directories(${CMAKE_CURRENT_LIST_DIR})
-
-set(HEADERS ${HEADERS}
- ${CMAKE_CURRENT_LIST_DIR}/include/QAead.h
- ${CMAKE_CURRENT_LIST_DIR}/include/QBlockCipher.h
- ${CMAKE_CURRENT_LIST_DIR}/include/QCryptoError.h
- ${CMAKE_CURRENT_LIST_DIR}/include/QRsa.h
- ${CMAKE_CURRENT_LIST_DIR}/include/QSimpleCrypto_global.h
- ${CMAKE_CURRENT_LIST_DIR}/include/QX509.h
- ${CMAKE_CURRENT_LIST_DIR}/include/QX509Store.h
-)
-
-set(SOURCES ${SOURCES}
- ${CMAKE_CURRENT_LIST_DIR}/sources/QAead.cpp
- ${CMAKE_CURRENT_LIST_DIR}/sources/QBlockCipher.cpp
- ${CMAKE_CURRENT_LIST_DIR}/sources/QCryptoError.cpp
- ${CMAKE_CURRENT_LIST_DIR}/sources/QRsa.cpp
- ${CMAKE_CURRENT_LIST_DIR}/sources/QX509.cpp
- ${CMAKE_CURRENT_LIST_DIR}/sources/QX509Store.cpp
-)
diff --git a/client/3rd/QSimpleCrypto/QSimpleCrypto.pri b/client/3rd/QSimpleCrypto/QSimpleCrypto.pri
deleted file mode 100644
index 99a1c129..00000000
--- a/client/3rd/QSimpleCrypto/QSimpleCrypto.pri
+++ /dev/null
@@ -1,18 +0,0 @@
-INCLUDEPATH += $$PWD
-
-HEADERS += \
- $$PWD/include/QAead.h \
- $$PWD/include/QBlockCipher.h \
- $$PWD/include/QCryptoError.h \
- $$PWD/include/QRsa.h \
- $$PWD/include/QSimpleCrypto_global.h \
- $$PWD/include/QX509.h \
- $$PWD/include/QX509Store.h
-
-SOURCES += \
- $$PWD/sources/QAead.cpp \
- $$PWD/sources/QBlockCipher.cpp \
- $$PWD/sources/QCryptoError.cpp \
- $$PWD/sources/QRsa.cpp \
- $$PWD/sources/QX509.cpp \
- $$PWD/sources/QX509Store.cpp
diff --git a/client/3rd/QSimpleCrypto/include/QAead.h b/client/3rd/QSimpleCrypto/include/QAead.h
deleted file mode 100644
index 11f60b31..00000000
--- a/client/3rd/QSimpleCrypto/include/QAead.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution
-**/
-
-#ifndef QAEAD_H
-#define QAEAD_H
-
-#include "QSimpleCrypto_global.h"
-
-#include
-
-#include
-
-#include
-#include
-#include
-#include
-
-#include "QCryptoError.h"
-
-// clang-format off
-namespace QSimpleCrypto
-{
- class QSIMPLECRYPTO_EXPORT QAead {
- public:
- QAead();
-
- ///
- /// \brief encryptAesGcm - Function encrypts data with Gcm algorithm.
- /// \param data - Data that will be encrypted.
- /// \param key - AES key.
- /// \param iv - Initialization vector.
- /// \param tag - Authorization tag.
- /// \param aad - Additional authenticated data. Must be nullptr, if not used.
- /// \param cipher - Can be used with OpenSSL EVP_CIPHER (gcm) - 128, 192, 256. Example: EVP_aes_256_gcm().
- /// \return Returns encrypted data or "", if error happened.
- ///
- QByteArray encryptAesGcm(QByteArray data, QByteArray key, QByteArray iv, QByteArray* tag, QByteArray aad = "", const EVP_CIPHER* cipher = EVP_aes_256_gcm());
-
- ///
- /// \brief decryptAesGcm - Function decrypts data with Gcm algorithm.
- /// \param data - Data that will be decrypted
- /// \param key - AES key
- /// \param iv - Initialization vector
- /// \param tag - Authorization tag
- /// \param aad - Additional authenticated data. Must be nullptr, if not used
- /// \param cipher - Can be used with OpenSSL EVP_CIPHER (gcm) - 128, 192, 256. Example: EVP_aes_256_gcm()
- /// \return Returns decrypted data or "", if error happened.
- ///
- QByteArray decryptAesGcm(QByteArray data, QByteArray key, QByteArray iv, QByteArray* tag, QByteArray aad = "", const EVP_CIPHER* cipher = EVP_aes_256_gcm());
-
- ///
- /// \brief encryptAesCcm - Function encrypts data with Ccm algorithm.
- /// \param data - Data that will be encrypted.
- /// \param key - AES key.
- /// \param iv - Initialization vector.
- /// \param tag - Authorization tag.
- /// \param aad - Additional authenticated data. Must be nullptr, if not used.
- /// \param cipher - Can be used with OpenSSL EVP_CIPHER (ccm) - 128, 192, 256. Example: EVP_aes_256_ccm().
- /// \return Returns encrypted data or "", if error happened.
- ///
- QByteArray encryptAesCcm(QByteArray data, QByteArray key, QByteArray iv, QByteArray* tag, QByteArray aad = "", const EVP_CIPHER* cipher = EVP_aes_256_ccm());
-
- ///
- /// \brief decryptAesCcm - Function decrypts data with Ccm algorithm.
- /// \param data - Data that will be decrypted.
- /// \param key - AES key.
- /// \param iv - Initialization vector.
- /// \param tag - Authorization tag.
- /// \param aad - Additional authenticated data. Must be nullptr, if not used.
- /// \param cipher - Can be used with OpenSSL EVP_CIPHER (ccm) - 128, 192, 256. Example: EVP_aes_256_ccm().
- /// \return Returns decrypted data or "", if error happened.
- ///
- QByteArray decryptAesCcm(QByteArray data, QByteArray key, QByteArray iv, QByteArray* tag, QByteArray aad = "", const EVP_CIPHER* cipher = EVP_aes_256_ccm());
-
- ///
- /// \brief error - Error handler class.
- ///
- QCryptoError error;
- };
-} // namespace QSimpleCrypto
-
-#endif // QAEAD_H
diff --git a/client/3rd/QSimpleCrypto/include/QBlockCipher.h b/client/3rd/QSimpleCrypto/include/QBlockCipher.h
deleted file mode 100644
index e7b83a88..00000000
--- a/client/3rd/QSimpleCrypto/include/QBlockCipher.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/**
- * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution
-**/
-
-#ifndef QBLOCKCIPHER_H
-#define QBLOCKCIPHER_H
-
-#include "QSimpleCrypto_global.h"
-
-#include
-
-#include
-
-#include
-#include
-#include
-#include
-
-#include "QCryptoError.h"
-
-// clang-format off
-namespace QSimpleCrypto
-{
- class QSIMPLECRYPTO_EXPORT QBlockCipher {
-
- #define Aes128Rounds 10
- #define Aes192Rounds 12
- #define Aes256Rounds 14
-
- public:
- QBlockCipher();
-
- ///
- /// \brief generateRandomBytes - Function generates random bytes by size.
- /// \param size - Size of generated bytes.
- /// \return Returns random bytes.
- ///
- QByteArray generateRandomBytes(const int& size);
- QByteArray generateSecureRandomBytes(const int& size);
-
- ///
- /// \brief encryptAesBlockCipher - Function encrypts data with Aes Block Cipher algorithm.
- /// \param data - Data that will be encrypted.
- /// \param key - AES key.
- /// \param iv - Initialization vector.
- /// \param password - Encryption password.
- /// \param salt - Random delta.
- /// \param rounds - Transformation rounds.
- /// \param chiper - Can be used with OpenSSL EVP_CIPHER (ecb, cbc, cfb, ofb, ctr) - 128, 192, 256. Example: EVP_aes_256_cbc().
- /// \param md - Hash algroitm (OpenSSL EVP_MD). Example: EVP_sha512().
- /// \return Returns decrypted data or "", if error happened.
- ///
- QByteArray encryptAesBlockCipher(QByteArray data, QByteArray key,
- QByteArray iv = "", const int& rounds = Aes256Rounds,
- const EVP_CIPHER* cipher = EVP_aes_256_cbc(), const EVP_MD* md = EVP_sha512());
-
- ///
- /// \brief decryptAesBlockCipher - Function decrypts data with Aes Block Cipher algorithm.
- /// \param data - Data that will be decrypted.
- /// \param key - AES key.
- /// \param iv - Initialization vector.
- /// \param password - Decryption password.
- /// \param salt - Random delta.
- /// \param rounds - Transformation rounds.
- /// \param chiper - Can be used with OpenSSL EVP_CIPHER (ecb, cbc, cfb, ofb, ctr) - 128, 192, 256. Example: EVP_aes_256_cbc().
- /// \param md - Hash algroitm (OpenSSL EVP_MD). Example: EVP_sha512().
- /// \return Returns decrypted data or "", if error happened.
- ///
- QByteArray decryptAesBlockCipher(QByteArray data, QByteArray key,
- QByteArray iv = "", const int& rounds = Aes256Rounds,
- const EVP_CIPHER* cipher = EVP_aes_256_cbc(), const EVP_MD* md = EVP_sha512());
-
- ///
- /// \brief error - Error handler class.
- ///
- QCryptoError error;
- };
-} // namespace QSimpleCrypto
-
-#endif // QBLOCKCIPHER_H
diff --git a/client/3rd/QSimpleCrypto/include/QCryptoError.h b/client/3rd/QSimpleCrypto/include/QCryptoError.h
deleted file mode 100644
index fc059654..00000000
--- a/client/3rd/QSimpleCrypto/include/QCryptoError.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef QCRYPTOERROR_H
-#define QCRYPTOERROR_H
-
-#include
-
-#include "QSimpleCrypto_global.h"
-
-/// TODO: Add Special error code for each error.
-
-// clang-format off
-namespace QSimpleCrypto
-{
- class QSIMPLECRYPTO_EXPORT QCryptoError : public QObject {
- Q_OBJECT
-
- public:
- explicit QCryptoError(QObject* parent = nullptr);
-
- ///
- /// \brief setError - Sets error information
- /// \param errorCode - Error code.
- /// \param errorSummary - Error summary.
- ///
- inline void setError(const quint8 errorCode, const QString& errorSummary)
- {
- m_currentErrorCode = errorCode;
- m_errorSummary = errorSummary;
- }
-
- ///
- /// \brief lastError - Returns last error.
- /// \return Returns eror ID and error Text.
- ///
- inline QPair lastError() const
- {
- return QPair(m_currentErrorCode, m_errorSummary);
- }
-
- private:
- quint8 m_currentErrorCode;
- QString m_errorSummary;
- };
-}
-
-#endif // QCRYPTOERROR_H
diff --git a/client/3rd/QSimpleCrypto/include/QRsa.h b/client/3rd/QSimpleCrypto/include/QRsa.h
deleted file mode 100644
index 45eb3169..00000000
--- a/client/3rd/QSimpleCrypto/include/QRsa.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/**
- * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution
-**/
-
-#ifndef QRSA_H
-#define QRSA_H
-
-#include "QSimpleCrypto_global.h"
-
-#include
-#include
-
-#include
-
-#include
-#include
-#include
-
-#include "QCryptoError.h"
-
-// clang-format off
-namespace QSimpleCrypto
-{
- class QSIMPLECRYPTO_EXPORT QRsa {
-
- #define PublicEncrypt 0
- #define PrivateEncrypt 1
- #define PublicDecrypt 2
- #define PrivateDecrypt 3
-
- public:
- QRsa();
-
- ///
- /// \brief generateRsaKeys - Function generate Rsa Keys and returns them in OpenSSL structure.
- /// \param bits - RSA key size.
- /// \param rsaBigNumber - The exponent is an odd number, typically 3, 17 or 65537.
- /// \return Returns 'OpenSSL RSA structure' or 'nullptr', if error happened. Returned value must be cleaned up with 'RSA_free()' to avoid memory leak.
- ///
- RSA* generateRsaKeys(const int& bits, const int& rsaBigNumber);
-
- ///
- /// \brief savePublicKey - Saves to file RSA public key.
- /// \param rsa - OpenSSL RSA structure.
- /// \param publicKeyFileName - Public key file name.
- ///
- void savePublicKey(RSA *rsa, const QByteArray& publicKeyFileName);
-
- ///
- /// \brief savePrivateKey - Saves to file RSA private key.
- /// \param rsa - OpenSSL RSA structure.
- /// \param privateKeyFileName - Private key file name.
- /// \param password - Private key password.
- /// \param cipher - Can be used with 'OpenSSL EVP_CIPHER' (ecb, cbc, cfb, ofb, ctr) - 128, 192, 256. Example: EVP_aes_256_cbc().
- ///
- void savePrivateKey(RSA* rsa, const QByteArray& privateKeyFileName, QByteArray password = "", const EVP_CIPHER* cipher = nullptr);
-
- ///
- /// \brief getPublicKeyFromFile - Gets RSA public key from a file.
- /// \param filePath - File path to public key file.
- /// \return Returns 'OpenSSL EVP_PKEY structure' or 'nullptr', if error happened. Returned value must be cleaned up with 'EVP_PKEY_free()' to avoid memory leak.
- ///
- EVP_PKEY* getPublicKeyFromFile(const QByteArray& filePath);
-
- ///
- /// \brief getPrivateKeyFromFile - Gets RSA private key from a file.
- /// \param filePath - File path to private key file.
- /// \param password - Private key password.
- /// \return - Returns 'OpenSSL EVP_PKEY structure' or 'nullptr', if error happened. Returned value must be cleaned up with 'EVP_PKEY_free()' to avoid memory leak.
- ///
- EVP_PKEY* getPrivateKeyFromFile(const QByteArray& filePath, const QByteArray& password = "");
-
- ///
- /// \brief encrypt - Encrypt data with RSA algorithm.
- /// \param plaintext - Text that must be encrypted.
- /// \param rsa - OpenSSL RSA structure.
- /// \param encryptType - Public or private encrypt type. (PUBLIC_ENCRYPT, PRIVATE_ENCRYPT).
- /// \param padding - OpenSSL RSA padding can be used with: 'RSA_PKCS1_PADDING', 'RSA_NO_PADDING' and etc.
- /// \return Returns encrypted data or "", if error happened.
- ///
- QByteArray encrypt(QByteArray plainText, RSA* rsa, const int& encryptType = PublicEncrypt, const int& padding = RSA_PKCS1_PADDING);
-
- ///
- /// \brief decrypt - Decrypt data with RSA algorithm.
- /// \param cipherText - Text that must be decrypted.
- /// \param rsa - OpenSSL RSA structure.
- /// \param decryptType - Public or private type. (PUBLIC_DECRYPT, PRIVATE_DECRYPT).
- /// \param padding - RSA padding can be used with: 'RSA_PKCS1_PADDING', 'RSA_NO_PADDING' and etc.
- /// \return - Returns decrypted data or "", if error happened.
- ///
- QByteArray decrypt(QByteArray cipherText, RSA* rsa, const int& decryptType = PrivateDecrypt, const int& padding = RSA_PKCS1_PADDING);
-
- ///
- /// \brief error - Error handler class.
- ///
- QCryptoError error;
- };
-} // namespace QSimpleCrypto
-
-#endif // QRSA_H
diff --git a/client/3rd/QSimpleCrypto/include/QSimpleCrypto_global.h b/client/3rd/QSimpleCrypto/include/QSimpleCrypto_global.h
deleted file mode 100644
index fdd6c020..00000000
--- a/client/3rd/QSimpleCrypto/include/QSimpleCrypto_global.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef QSIMPLECRYPTO_GLOBAL_H
-#define QSIMPLECRYPTO_GLOBAL_H
-
-#include
-#include
-
-#define QSIMPLECRYPTO_EXPORT
-
-#endif // QSIMPLECRYPTO_GLOBAL_H
diff --git a/client/3rd/QSimpleCrypto/include/QX509.h b/client/3rd/QSimpleCrypto/include/QX509.h
deleted file mode 100644
index c31cb9e4..00000000
--- a/client/3rd/QSimpleCrypto/include/QX509.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution
-**/
-
-#ifndef QX509_H
-#define QX509_H
-
-#include "QSimpleCrypto_global.h"
-
-#include
-#include
-
-#include
-
-#include
-#include
-#include
-#include
-#include
-
-#include "QCryptoError.h"
-
-// clang-format off
-namespace QSimpleCrypto
-{
- class QSIMPLECRYPTO_EXPORT QX509 {
-
- #define oneYear 31536000L
- #define x509LastVersion 2
-
- public:
- QX509();
-
- ///
- /// \brief loadCertificateFromFile - Function load X509 from file and returns OpenSSL structure.
- /// \param fileName - File path to certificate.
- /// \return Returns OpenSSL X509 structure or nullptr, if error happened. Returned value must be cleaned up with 'X509_free' to avoid memory leak.
- ///
- X509* loadCertificateFromFile(const QByteArray& fileName);
-
- ///
- /// \brief signCertificate - Function signs X509 certificate and returns signed X509 OpenSSL structure.
- /// \param endCertificate - Certificate that will be signed
- /// \param caCertificate - CA certificate that will sign end certificate
- /// \param caPrivateKey - CA certificate private key
- /// \param fileName - With that name certificate will be saved. Leave "", if don't need to save it
- /// \return Returns OpenSSL X509 structure or nullptr, if error happened.
- ///
- X509* signCertificate(X509* endCertificate, X509* caCertificate, EVP_PKEY* caPrivateKey, const QByteArray& fileName = "");
-
- ///
- /// \brief verifyCertificate - Function verifies X509 certificate and returns verified X509 OpenSSL structure.
- /// \param x509 - OpenSSL X509. That certificate will be verified.
- /// \param store - Trusted certificate must be added to X509_Store with 'addCertificateToStore(X509_STORE* ctx, X509* x509)'.
- /// \return Returns OpenSSL X509 structure or nullptr, if error happened
- ///
- X509* verifyCertificate(X509* x509, X509_STORE* store);
-
- ///
- /// \brief generateSelfSignedCertificate - Function generatesand returns self signed X509.
- /// \param rsa - OpenSSL RSA.
- /// \param additionalData - Certificate information.
- /// \param certificateFileName - With that name certificate will be saved. Leave "", if don't need to save it.
- /// \param md - OpenSSL EVP_MD structure. Example: EVP_sha512().
- /// \param serialNumber - X509 certificate serial number.
- /// \param version - X509 certificate version.
- /// \param notBefore - X509 start date.
- /// \param notAfter - X509 end date.
- /// \return Returns OpenSSL X509 structure or nullptr, if error happened. Returned value must be cleaned up with 'X509_free' to avoid memory leak.
- ///
- X509* generateSelfSignedCertificate(RSA* rsa, const QMap& additionalData,
- const QByteArray& certificateFileName = "", const EVP_MD* md = EVP_sha512(),
- const long& serialNumber = 1, const long& version = x509LastVersion,
- const long& notBefore = 0, const long& notAfter = oneYear);
-
- ///
- /// \brief error - Error handler class.
- ///
- QCryptoError error;
- };
-} // namespace QSimpleCrypto
-
-#endif // QX509_H
diff --git a/client/3rd/QSimpleCrypto/include/QX509Store.h b/client/3rd/QSimpleCrypto/include/QX509Store.h
deleted file mode 100644
index 8cd8ca82..00000000
--- a/client/3rd/QSimpleCrypto/include/QX509Store.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/**
- * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution
-**/
-
-#ifndef QX509STORE_H
-#define QX509STORE_H
-
-#include "QSimpleCrypto_global.h"
-
-#include
-#include
-#include
-
-#include
-
-#include
-#include
-#include
-
-#include "QCryptoError.h"
-
-// clang-format off
-namespace QSimpleCrypto
-{
- class QSIMPLECRYPTO_EXPORT QX509Store {
- public:
- QX509Store();
-
- ///
- /// \brief addCertificateToStore
- /// \param store - OpenSSL X509_STORE.
- /// \param x509 - OpenSSL X509.
- /// \return Returns 'true' on success and 'false', if error happened.
- ///
- bool addCertificateToStore(X509_STORE* store, X509* x509);
-
- ///
- /// \brief addLookup
- /// \param store - OpenSSL X509_STORE.
- /// \param method - OpenSSL X509_LOOKUP_METHOD. Example: X509_LOOKUP_file.
- /// \return Returns 'true' on success and 'false', if error happened.
- ///
- bool addLookup(X509_STORE* store, X509_LOOKUP_METHOD* method);
-
- ///
- /// \brief setCertificateDepth
- /// \param store - OpenSSL X509_STORE.
- /// \param depth - That is the maximum number of untrusted CA certificates that can appear in a chain. Example: 0.
- /// \return Returns 'true' on success and 'false', if error happened.
- ///
- bool setDepth(X509_STORE* store, const int& depth);
-
- ///
- /// \brief setFlag
- /// \param store - OpenSSL X509_STORE.
- /// \param flag - The verification flags consists of zero or more of the following flags ored together. Example: X509_V_FLAG_CRL_CHECK.
- /// \return Returns 'true' on success and 'false', if error happened.
- ///
- bool setFlag(X509_STORE* store, const unsigned long& flag);
-
- ///
- /// \brief setFlag
- /// \param store - OpenSSL X509_STORE.
- /// \param purpose - Verification purpose in param to purpose. Example: X509_PURPOSE_ANY.
- /// \return Returns 'true' on success and 'false', if error happened.
- ///
- bool setPurpose(X509_STORE* store, const int& purpose);
-
- ///
- /// \brief setTrust
- /// \param store - OpenSSL X509_STORE.
- /// \param trust - Trust Level. Example: X509_TRUST_SSL_SERVER.
- /// \return Returns 'true' on success and 'false', if error happened.
- ///
- bool setTrust(X509_STORE* store, const int& trust);
-
- ///
- /// \brief setDefaultPaths
- /// \param store - OpenSSL X509_STORE.
- /// \return Returns 'true' on success and 'false', if error happened.
- ///
- bool setDefaultPaths(X509_STORE* store);
-
- ///
- /// \brief loadLocations
- /// \param store - OpenSSL X509_STORE.
- /// \param fileName - File name. Example: "caCertificate.pem".
- /// \param dirPath - Path to file. Example: "path/To/File".
- /// \return Returns 'true' on success and 'false', if error happened.
- ///
- bool loadLocations(X509_STORE* store, const QByteArray& fileName, const QByteArray& dirPath);
-
- ///
- /// \brief loadLocations
- /// \param store - OpenSSL X509_STORE.
- /// \param file - Qt QFile that will be loaded.
- /// \return Returns 'true' on success and 'false', if error happened.
- ///
- bool loadLocations(X509_STORE* store, const QFile& file);
-
- ///
- /// \brief loadLocations
- /// \param store - OpenSSL X509_STORE.
- /// \param fileInfo - Qt QFileInfo.
- /// \return Returns 'true' on success and 'false', if error happened.
- ///
- bool loadLocations(X509_STORE* store, const QFileInfo& fileInfo);
-
- ///
- /// \brief error - Error handler class.
- ///
- QCryptoError error;
- };
-}
-
-#endif // QX509STORE_H
diff --git a/client/3rd/QSimpleCrypto/sources/QAead.cpp b/client/3rd/QSimpleCrypto/sources/QAead.cpp
deleted file mode 100644
index 968c8841..00000000
--- a/client/3rd/QSimpleCrypto/sources/QAead.cpp
+++ /dev/null
@@ -1,364 +0,0 @@
-/**
- * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution
-**/
-
-#include "include/QAead.h"
-
-QSimpleCrypto::QAead::QAead()
-{
-}
-
-///
-/// \brief QSimpleCrypto::QAEAD::encryptAesGcm - Function encrypts data with Gcm algorithm.
-/// \param data - Data that will be encrypted.
-/// \param key - AES key.
-/// \param iv - Initialization vector.
-/// \param tag - Authorization tag.
-/// \param aad - Additional authenticated data. Must be nullptr, if not used.
-/// \param cipher - Can be used with OpenSSL EVP_CIPHER (gcm) - 128, 192, 256. Example: EVP_aes_256_gcm().
-/// \return Returns encrypted data or "", if error happened.
-///
-QByteArray QSimpleCrypto::QAead::encryptAesGcm(QByteArray data, QByteArray key, QByteArray iv, QByteArray* tag, QByteArray aad, const EVP_CIPHER* cipher)
-{
- try {
- /* Initialize EVP_CIPHER_CTX */
- std::unique_ptr encryptionCipher { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free };
- if (encryptionCipher == nullptr) {
- throw std::runtime_error("Couldn't initialize \'encryptionCipher\'. EVP_CIPHER_CTX_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Set data length */
- int plainTextLength = data.size();
- int cipherTextLength = 0;
-
- /* Initialize cipherText. Here encrypted data will be stored */
- std::unique_ptr cipherText { new unsigned char[plainTextLength]() };
- if (cipherText == nullptr) {
- throw std::runtime_error("Couldn't allocate memory for 'ciphertext'.");
- }
-
- /* Initialize encryption operation. */
- if (!EVP_EncryptInit_ex(encryptionCipher.get(), cipher, nullptr, reinterpret_cast(key.data()), reinterpret_cast(iv.data()))) {
- throw std::runtime_error("Couldn't initialize encryption operation. EVP_EncryptInit_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Set IV length if default 12 bytes (96 bits) is not appropriate */
- if (!EVP_CIPHER_CTX_ctrl(encryptionCipher.get(), EVP_CTRL_GCM_SET_IVLEN, iv.length(), nullptr)) {
- throw std::runtime_error("Couldn't set IV length. EVP_CIPHER_CTX_ctrl(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
-// /* Check if aad need to be used */
-// if (aad.length() > 0) {
-// /* Provide any AAD data. This can be called zero or more times as required */
-// if (!EVP_EncryptUpdate(encryptionCipher.get(), nullptr, &cipherTextLength, reinterpret_cast(aad.data()), aad.length())) {
-// throw std::runtime_error("Couldn't provide aad data. EVP_EncryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
-// }
-// }
-
- /*
- * Provide the message to be encrypted, and obtain the encrypted output.
- * EVP_EncryptUpdate can be called multiple times if necessary
- */
- if (!EVP_EncryptUpdate(encryptionCipher.get(), cipherText.get(), &cipherTextLength, reinterpret_cast(data.data()), plainTextLength)) {
- throw std::runtime_error("Couldn't provide message to be encrypted. EVP_EncryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /*
- * Finalize the encryption. Normally cipher text bytes may be written at
- * this stage, but this does not occur in GCM mode
- */
- if (!EVP_EncryptFinal_ex(encryptionCipher.get(), cipherText.get(), &plainTextLength)) {
- throw std::runtime_error("Couldn't finalize encryption. EVP_EncryptFinal_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
-// /* Get tag */
-// if (!EVP_CIPHER_CTX_ctrl(encryptionCipher.get(), EVP_CTRL_GCM_GET_TAG, tag->length(), reinterpret_cast(tag->data()))) {
-// throw std::runtime_error("Couldn't get tag. EVP_CIPHER_CTX_ctrl(. Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
-// }
-
- /* Finilize data to be readable with qt */
- QByteArray encryptedData = QByteArray(reinterpret_cast(cipherText.get()), cipherTextLength);
-
- return encryptedData;
-
- } catch (std::exception& exception) {
- QSimpleCrypto::QAead::error.setError(1, exception.what());
- return QByteArray();
- } catch (...) {
- QSimpleCrypto::QAead::error.setError(2, "Unknown error!");
- return QByteArray();
- }
-
- return QByteArray();
-}
-
-///
-/// \brief QSimpleCrypto::QAEAD::decryptAesGcm - Function decrypts data with Gcm algorithm.
-/// \param data - Data that will be decrypted
-/// \param key - AES key
-/// \param iv - Initialization vector
-/// \param tag - Authorization tag
-/// \param aad - Additional authenticated data. Must be nullptr, if not used
-/// \param cipher - Can be used with OpenSSL EVP_CIPHER (gcm) - 128, 192, 256. Example: EVP_aes_256_gcm()
-/// \return Returns decrypted data or "", if error happened.
-///
-QByteArray QSimpleCrypto::QAead::decryptAesGcm(QByteArray data, QByteArray key, QByteArray iv, QByteArray* tag, QByteArray aad, const EVP_CIPHER* cipher)
-{
- try {
- /* Initialize EVP_CIPHER_CTX */
- std::unique_ptr decryptionCipher { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free };
- if (decryptionCipher.get() == nullptr) {
- throw std::runtime_error("Couldn't initialize \'decryptionCipher\'. EVP_CIPHER_CTX_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Set data length */
- int cipherTextLength = data.size();
- int plainTextLength = 0;
-
- /* Initialize plainText. Here decrypted data will be stored */
- std::unique_ptr plainText { new unsigned char[cipherTextLength]() };
- if (plainText == nullptr) {
- throw std::runtime_error("Couldn't allocate memory for 'plaintext'.");
- }
-
- /* Initialize decryption operation. */
- if (!EVP_DecryptInit_ex(decryptionCipher.get(), cipher, nullptr, reinterpret_cast(key.data()), reinterpret_cast(iv.data()))) {
- throw std::runtime_error("Couldn't initialize decryption operation. EVP_DecryptInit_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Set IV length. Not necessary if this is 12 bytes (96 bits) */
- if (!EVP_CIPHER_CTX_ctrl(decryptionCipher.get(), EVP_CTRL_GCM_SET_IVLEN, iv.length(), nullptr)) {
- throw std::runtime_error("Couldn't set IV length. EVP_CIPHER_CTX_ctrl(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
-// /* Check if aad need to be used */
-// if (aad.length() > 0) {
-// /* Provide any AAD data. This can be called zero or more times as required */
-// if (!EVP_DecryptUpdate(decryptionCipher.get(), nullptr, &plainTextLength, reinterpret_cast(aad.data()), aad.length())) {
-// throw std::runtime_error("Couldn't provide aad data. EVP_DecryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
-// }
-// }
-
- /*
- * Provide the message to be decrypted, and obtain the plain text output.
- * EVP_DecryptUpdate can be called multiple times if necessary
- */
- if (!EVP_DecryptUpdate(decryptionCipher.get(), plainText.get(), &plainTextLength, reinterpret_cast(data.data()), cipherTextLength)) {
- throw std::runtime_error("Couldn't provide message to be decrypted. EVP_DecryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
-// /* Set expected tag value. Works in OpenSSL 1.0.1d and later */
-// if (!EVP_CIPHER_CTX_ctrl(decryptionCipher.get(), EVP_CTRL_GCM_SET_TAG, tag->length(), reinterpret_cast(tag->data()))) {
-// throw std::runtime_error("Coldn't set tag. EVP_CIPHER_CTX_ctrl(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
-// }
-
- /*
- * Finalize the decryption. A positive return value indicates success,
- * anything else is a failure - the plain text is not trustworthy.
- */
- if (!EVP_DecryptFinal_ex(decryptionCipher.get(), plainText.get(), &cipherTextLength)) {
- throw std::runtime_error("Couldn't finalize decryption. EVP_DecryptFinal_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Finilize data to be readable with qt */
- QByteArray decryptedData = QByteArray(reinterpret_cast(plainText.get()), plainTextLength);
-
- return decryptedData;
-
- } catch (std::exception& exception) {
- QSimpleCrypto::QAead::error.setError(1, exception.what());
- return QByteArray();
- } catch (...) {
- QSimpleCrypto::QAead::error.setError(2, "Unknown error!");
- return QByteArray();
- }
-
- return QByteArray();
-}
-
-///
-/// \brief QSimpleCrypto::QAEAD::encryptAesCcm - Function encrypts data with Ccm algorithm.
-/// \param data - Data that will be encrypted.
-/// \param key - AES key.
-/// \param iv - Initialization vector.
-/// \param tag - Authorization tag.
-/// \param aad - Additional authenticated data. Must be nullptr, if not used.
-/// \param cipher - Can be used with OpenSSL EVP_CIPHER (ccm) - 128, 192, 256. Example: EVP_aes_256_ccm().
-/// \return Returns encrypted data or "", if error happened.
-///
-QByteArray QSimpleCrypto::QAead::encryptAesCcm(QByteArray data, QByteArray key, QByteArray iv, QByteArray* tag, QByteArray aad, const EVP_CIPHER* cipher)
-{
- try {
- /* Initialize EVP_CIPHER_CTX */
- std::unique_ptr encryptionCipher { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free };
- if (encryptionCipher == nullptr) {
- throw std::runtime_error("Couldn't initialize \'encryptionCipher\'. EVP_CIPHER_CTX_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Set data length */
- int plainTextLength = data.size();
- int cipherTextLength = 0;
-
- /* Initialize cipherText. Here encrypted data will be stored */
- std::unique_ptr cipherText { new unsigned char[plainTextLength]() };
- if (cipherText.get() == nullptr) {
- throw std::runtime_error("Couldn't allocate memory for 'ciphertext'.");
- }
-
- /* Initialize encryption operation. */
- if (!EVP_EncryptInit_ex(encryptionCipher.get(), cipher, nullptr, reinterpret_cast(key.data()), reinterpret_cast(iv.data()))) {
- throw std::runtime_error("Couldn't initialize encryption operation. EVP_EncryptInit_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Set IV length if default 12 bytes (96 bits) is not appropriate */
- if (!EVP_CIPHER_CTX_ctrl(encryptionCipher.get(), EVP_CTRL_CCM_SET_IVLEN, iv.length(), nullptr)) {
- throw std::runtime_error("Couldn't set IV length. EVP_CIPHER_CTX_ctrl(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Set tag length */
- if (!EVP_CIPHER_CTX_ctrl(encryptionCipher.get(), EVP_CTRL_CCM_SET_TAG, tag->length(), nullptr)) {
- throw std::runtime_error("Coldn't set tag. EVP_CIPHER_CTX_ctrl(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Check if aad need to be used */
- if (aad.length() > 0) {
- /* Provide the total plain text length */
- if (!EVP_EncryptUpdate(encryptionCipher.get(), nullptr, &cipherTextLength, nullptr, plainTextLength)) {
- throw std::runtime_error("Couldn't provide total plaintext length. EVP_EncryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Provide any AAD data. This can be called zero or more times as required */
- if (!EVP_EncryptUpdate(encryptionCipher.get(), nullptr, &cipherTextLength, reinterpret_cast(aad.data()), aad.length())) {
- throw std::runtime_error("Couldn't provide aad data. EVP_EncryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
- }
-
- /*
- * Provide the message to be encrypted, and obtain the encrypted output.
- * EVP_EncryptUpdate can be called multiple times if necessary
- */
- if (!EVP_EncryptUpdate(encryptionCipher.get(), cipherText.get(), &cipherTextLength, reinterpret_cast(data.data()), plainTextLength)) {
- throw std::runtime_error("Couldn't provide message to be encrypted. EVP_EncryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /*
- * Finalize the encryption. Normally ciphertext bytes may be written at
- * this stage, but this does not occur in GCM mode
- */
- if (!EVP_EncryptFinal_ex(encryptionCipher.get(), cipherText.get(), &plainTextLength)) {
- throw std::runtime_error("Couldn't finalize encryption. EVP_EncryptFinal_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Get tag */
- if (!EVP_CIPHER_CTX_ctrl(encryptionCipher.get(), EVP_CTRL_CCM_GET_TAG, tag->length(), reinterpret_cast(tag->data()))) {
- throw std::runtime_error("Couldn't get tag. EVP_CIPHER_CTX_ctrl(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Finilize data to be readable with qt */
- QByteArray encryptedData = QByteArray(reinterpret_cast(cipherText.get()), cipherTextLength);
-
- return encryptedData;
-
- } catch (std::exception& exception) {
- QSimpleCrypto::QAead::error.setError(1, exception.what());
- return QByteArray();
- } catch (...) {
- QSimpleCrypto::QAead::error.setError(2, "Unknown error!");
- return QByteArray();
- }
-
- return QByteArray();
-}
-
-///
-/// \brief QSimpleCrypto::QAEAD::decryptAesCcm - Function decrypts data with Ccm algorithm.
-/// \param data - Data that will be decrypted.
-/// \param key - AES key.
-/// \param iv - Initialization vector.
-/// \param tag - Authorization tag.
-/// \param aad - Additional authenticated data. Must be nullptr, if not used.
-/// \param cipher - Can be used with OpenSSL EVP_CIPHER (ccm) - 128, 192, 256. Example: EVP_aes_256_ccm().
-/// \return Returns decrypted data or "", if error happened.
-///
-QByteArray QSimpleCrypto::QAead::decryptAesCcm(QByteArray data, QByteArray key, QByteArray iv, QByteArray* tag, QByteArray aad, const EVP_CIPHER* cipher)
-{
- try {
- /* Initialize EVP_CIPHER_CTX */
- std::unique_ptr decryptionCipher { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free };
- if (decryptionCipher.get() == nullptr) {
- throw std::runtime_error("Couldn't initialize \'decryptionCipher\'. EVP_CIPHER_CTX_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Set data length */
- int cipherTextLength = data.size();
- int plainTextLength = 0;
-
- /* Initialize plainText. Here decrypted data will be stored */
- std::unique_ptr plainText { new unsigned char[cipherTextLength]() };
- if (plainText == nullptr) {
- throw std::runtime_error("Couldn't allocate memory for 'plaintext'.");
- }
-
- /* Initialize decryption operation. */
- if (!EVP_DecryptInit_ex(decryptionCipher.get(), cipher, nullptr, reinterpret_cast(key.data()), reinterpret_cast(iv.data()))) {
- throw std::runtime_error("Couldn't initialize decryption operation. EVP_DecryptInit_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Set IV length. Not necessary if this is 12 bytes (96 bits) */
- if (!EVP_CIPHER_CTX_ctrl(decryptionCipher.get(), EVP_CTRL_CCM_SET_IVLEN, iv.length(), nullptr)) {
- throw std::runtime_error("Couldn't set IV length. EVP_CIPHER_CTX_ctrl(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Set expected tag value. Works in OpenSSL 1.0.1d and later */
- if (!EVP_CIPHER_CTX_ctrl(decryptionCipher.get(), EVP_CTRL_CCM_SET_TAG, tag->length(), reinterpret_cast(tag->data()))) {
- throw std::runtime_error("Coldn't set tag. EVP_CIPHER_CTX_ctrl(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Check if aad need to be used */
- if (aad.length() > 0) {
- /* Provide the total ciphertext length */
- if (!EVP_DecryptUpdate(decryptionCipher.get(), nullptr, &plainTextLength, nullptr, cipherTextLength)) {
- throw std::runtime_error("Couldn't provide total plaintext length. EVP_DecryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Provide any AAD data. This can be called zero or more times as required */
- if (!EVP_DecryptUpdate(decryptionCipher.get(), nullptr, &plainTextLength, reinterpret_cast(aad.data()), aad.length())) {
- throw std::runtime_error("Couldn't provide aad data. EVP_DecryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
- }
-
- /*
- * Provide the message to be decrypted, and obtain the plaintext output.
- * EVP_DecryptUpdate can be called multiple times if necessary
- */
- if (!EVP_DecryptUpdate(decryptionCipher.get(), plainText.get(), &plainTextLength, reinterpret_cast(data.data()), cipherTextLength)) {
- throw std::runtime_error("Couldn't provide message to be decrypted. EVP_DecryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /*
- * Finalize the decryption. A positive return value indicates success,
- * anything else is a failure - the plaintext is not trustworthy.
- */
- if (!EVP_DecryptFinal_ex(decryptionCipher.get(), plainText.get(), &cipherTextLength)) {
- throw std::runtime_error("Couldn't finalize decryption. EVP_DecryptFinal_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Finilize data to be readable with qt */
- QByteArray decryptedData = QByteArray(reinterpret_cast(plainText.get()), plainTextLength);
-
- return decryptedData;
-
- } catch (std::exception& exception) {
- QSimpleCrypto::QAead::error.setError(1, exception.what());
- return QByteArray();
- } catch (...) {
- QSimpleCrypto::QAead::error.setError(2, "Unknown error!");
- return QByteArray();
- }
-
- return QByteArray();
-}
diff --git a/client/3rd/QSimpleCrypto/sources/QBlockCipher.cpp b/client/3rd/QSimpleCrypto/sources/QBlockCipher.cpp
deleted file mode 100644
index 8b86ab98..00000000
--- a/client/3rd/QSimpleCrypto/sources/QBlockCipher.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
-/**
- * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution
-**/
-
-#include "include/QBlockCipher.h"
-
-QSimpleCrypto::QBlockCipher::QBlockCipher()
-{
-}
-
-///
-/// \brief QSimpleCrypto::QBlockCipher::generateRandomBytes - Function generates random bytes by size.
-/// \param size - Size of generated bytes.
-/// \return Returns random bytes.
-///
-QByteArray QSimpleCrypto::QBlockCipher::generateRandomBytes(const int& size)
-{
- unsigned char arr[sizeof(size)];
- RAND_bytes(arr, sizeof(size));
-
- QByteArray buffer = QByteArray(reinterpret_cast(arr), size);
- return buffer;
-}
-
-QByteArray QSimpleCrypto::QBlockCipher::generateSecureRandomBytes(const int &size)
-{
- unsigned char arr[sizeof(size)];
- RAND_priv_bytes(arr, sizeof(size));
-
- QByteArray buffer = QByteArray(reinterpret_cast(arr), size);
- return buffer;
-}
-
-///
-/// \brief QSimpleCrypto::QBlockCipher::encryptAesBlockCipher - Function encrypts data with Aes Block Cipher algorithm.
-/// \param data - Data that will be encrypted.
-/// \param key - AES key.
-/// \param iv - Initialization vector.
-/// \param password - Encryption password.
-/// \param salt - Random delta.
-/// \param rounds - Transformation rounds.
-/// \param chiper - Can be used with OpenSSL EVP_CIPHER (ecb, cbc, cfb, ofb, ctr) - 128, 192, 256. Example: EVP_aes_256_cbc().
-/// \param md - Hash algroitm (OpenSSL EVP_MD). Example: EVP_sha512().
-/// \return Returns decrypted data or "", if error happened.
-///
-QByteArray QSimpleCrypto::QBlockCipher::encryptAesBlockCipher(QByteArray data, QByteArray key,
- QByteArray iv,
- const int& rounds, const EVP_CIPHER* cipher, const EVP_MD* md)
-{
- try {
- /* Initialize EVP_CIPHER_CTX */
- std::unique_ptr encryptionCipher { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free };
- if (encryptionCipher == nullptr) {
- throw std::runtime_error("Couldn't initialize \'encryptionCipher\'. EVP_CIPHER_CTX_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Reinterpret values for multi use */
- unsigned char* m_key = reinterpret_cast(key.data());
- unsigned char* m_iv = reinterpret_cast(iv.data());
-
- /* Set data length */
- int cipherTextLength(data.size() + AES_BLOCK_SIZE);
- int finalLength = 0;
-
- /* Initialize cipcherText. Here encrypted data will be stored */
- std::unique_ptr cipherText { new unsigned char[cipherTextLength]() };
- if (cipherText == nullptr) {
- throw std::runtime_error("Couldn't allocate memory for 'cipherText'.");
- }
-
- // Bug here
-// /* Start encryption with password based encryption routine */
-// if (!EVP_BytesToKey(cipher, md, reinterpret_cast(salt.data()), reinterpret_cast(password.data()), password.length(), rounds, m_key, m_iv)) {
-// throw std::runtime_error("Couldn't start encryption routine. EVP_BytesToKey(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
-// }
-
- /* Initialize encryption operation. */
- if (!EVP_EncryptInit_ex(encryptionCipher.get(), cipher, nullptr, m_key, m_iv)) {
- throw std::runtime_error("Couldn't initialize encryption operation. EVP_EncryptInit_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /*
- * Provide the message to be encrypted, and obtain the encrypted output.
- * EVP_EncryptUpdate can be called multiple times if necessary
- */
- if (!EVP_EncryptUpdate(encryptionCipher.get(), cipherText.get(), &cipherTextLength, reinterpret_cast(data.data()), data.size())) {
- throw std::runtime_error("Couldn't provide message to be encrypted. EVP_EncryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Finalize the encryption. Normally ciphertext bytes may be written at this stage */
- if (!EVP_EncryptFinal(encryptionCipher.get(), cipherText.get() + cipherTextLength, &finalLength)) {
- throw std::runtime_error("Couldn't finalize encryption. EVP_EncryptFinal(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Finilize data to be readable with qt */
- QByteArray encryptedData = QByteArray(reinterpret_cast(cipherText.get()), cipherTextLength + finalLength);
-
- return encryptedData;
-
- } catch (std::exception& exception) {
- QSimpleCrypto::QBlockCipher::error.setError(1, exception.what());
- return QByteArray();
- } catch (...) {
- QSimpleCrypto::QBlockCipher::error.setError(2, "Unknown error!");
- return QByteArray();
- }
-
- return QByteArray();
-}
-
-///
-/// \brief QSimpleCrypto::QBlockCipher::encryptAesBlockCipher - Function decrypts data with Aes Block Cipher algorithm.
-/// \param data - Data that will be decrypted.
-/// \param key - AES key.
-/// \param iv - Initialization vector.
-/// \param password - Decryption password.
-/// \param salt - Random delta.
-/// \param rounds - Transformation rounds.
-/// \param chiper - Can be used with OpenSSL EVP_CIPHER (ecb, cbc, cfb, ofb, ctr) - 128, 192, 256. Example: EVP_aes_256_cbc().
-/// \param md - Hash algroitm (OpenSSL EVP_MD). Example: EVP_sha512().
-/// \return Returns decrypted data or "", if error happened.
-///
-QByteArray QSimpleCrypto::QBlockCipher::decryptAesBlockCipher(QByteArray data, QByteArray key,
- QByteArray iv,
- const int& rounds, const EVP_CIPHER* cipher, const EVP_MD* md)
-{
- try {
- /* Initialize EVP_CIPHER_CTX */
- std::unique_ptr decryptionCipher { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free };
- if (decryptionCipher == nullptr) {
- throw std::runtime_error("Couldn't initialize \'decryptionCipher\'. EVP_CIPHER_CTX_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Reinterpret values for multi use */
- unsigned char* m_key = reinterpret_cast(key.data());
- unsigned char* m_iv = reinterpret_cast(iv.data());
-
- /* Set data length */
- int plainTextLength(data.size());
- int finalLength = 0;
-
- /* Initialize plainText. Here decrypted data will be stored */
- std::unique_ptr plainText { new unsigned char[plainTextLength + AES_BLOCK_SIZE]() };
- if (plainText == nullptr) {
- throw std::runtime_error("Couldn't allocate memory for \'plainText\'. EVP_CIPHER_CTX_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- // Bug here
-// /* Start encryption with password based encryption routine */
-// if (!EVP_BytesToKey(cipher, md, reinterpret_cast(salt.data()), reinterpret_cast(password.data()), password.length(), rounds, m_key, m_iv)) {
-// throw std::runtime_error("Couldn't start decryption routine. EVP_BytesToKey(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
-// }
-
- /* Initialize decryption operation. */
- if (!EVP_DecryptInit_ex(decryptionCipher.get(), cipher, nullptr, m_key, m_iv)) {
- throw std::runtime_error("Couldn't initialize decryption operation. EVP_DecryptInit_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /*
- * Provide the message to be decrypted, and obtain the plaintext output.
- * EVP_DecryptUpdate can be called multiple times if necessary
- */
- if (!EVP_DecryptUpdate(decryptionCipher.get(), plainText.get(), &plainTextLength, reinterpret_cast(data.data()), data.size())) {
- throw std::runtime_error("Couldn't provide message to be decrypted. EVP_DecryptUpdate(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /*
- * Finalize the decryption. A positive return value indicates success,
- * anything else is a failure - the plaintext is not trustworthy.
- */
- if (!EVP_DecryptFinal(decryptionCipher.get(), plainText.get() + plainTextLength, &finalLength)) {
- throw std::runtime_error("Couldn't finalize decryption. EVP_DecryptFinal. Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Finilize data to be readable with qt */
- QByteArray decryptedData = QByteArray(reinterpret_cast(plainText.get()), plainTextLength + finalLength);
-
- return decryptedData;
-
- } catch (std::exception& exception) {
- QSimpleCrypto::QBlockCipher::error.setError(1, exception.what());
- return QByteArray(exception.what());
- } catch (...) {
- QSimpleCrypto::QBlockCipher::error.setError(2, "Unknown error!");
- return QByteArray();
- }
-
- return QByteArray();
-}
diff --git a/client/3rd/QSimpleCrypto/sources/QCryptoError.cpp b/client/3rd/QSimpleCrypto/sources/QCryptoError.cpp
deleted file mode 100644
index 234f55d7..00000000
--- a/client/3rd/QSimpleCrypto/sources/QCryptoError.cpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "include/QCryptoError.h"
-
-QSimpleCrypto::QCryptoError::QCryptoError(QObject* parent)
- : QObject(parent)
-{
-}
diff --git a/client/3rd/QSimpleCrypto/sources/QRsa.cpp b/client/3rd/QSimpleCrypto/sources/QRsa.cpp
deleted file mode 100644
index 544d6746..00000000
--- a/client/3rd/QSimpleCrypto/sources/QRsa.cpp
+++ /dev/null
@@ -1,274 +0,0 @@
-/**
- * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution
-**/
-
-#include "include/QRsa.h"
-
-QSimpleCrypto::QRsa::QRsa()
-{
-}
-
-///
-/// \brief QSimpleCrypto::QRSA::generateRsaKeys - Function generate Rsa Keys and returns them in OpenSSL structure.
-/// \param bits - RSA key size.
-/// \param rsaBigNumber - The exponent is an odd number, typically 3, 17 or 65537.
-/// \return Returns 'OpenSSL RSA structure' or 'nullptr', if error happened. Returned value must be cleaned up with 'RSA_free()' to avoid memory leak.
-///
-RSA* QSimpleCrypto::QRsa::generateRsaKeys(const int& bits, const int& rsaBigNumber)
-{
- try {
- /* Initialize big number */
- std::unique_ptr bigNumber { BN_new(), BN_free };
- if (bigNumber == nullptr) {
- throw std::runtime_error("Couldn't initialize \'bigNumber\'. BN_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- return nullptr;
- }
-
- /* Set big number */
- if (!BN_set_word(bigNumber.get(), rsaBigNumber)) {
- throw std::runtime_error("Couldn't set bigNumber. BN_set_word(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Initialize RSA */
- RSA* rsa = nullptr;
- if (!(rsa = RSA_new())) {
- throw std::runtime_error("Couldn't initialize x509. X509_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Generate key pair and store it in RSA */
- if (!RSA_generate_key_ex(rsa, bits, bigNumber.get(), nullptr)) {
- throw std::runtime_error("Couldn't generate RSA. RSA_generate_key_ex(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- return rsa;
- } catch (std::exception& exception) {
- QSimpleCrypto::QRsa::error.setError(1, exception.what());
- return nullptr;
- } catch (...) {
- QSimpleCrypto::QRsa::error.setError(2, "Unknown error!");
- return nullptr;
- }
-}
-
-///
-/// \brief QSimpleCrypto::QRSA::savePublicKey - Saves to file RSA public key.
-/// \param rsa - OpenSSL RSA structure.
-/// \param publicKeyFileName - Public key file name.
-///
-void QSimpleCrypto::QRsa::savePublicKey(RSA* rsa, const QByteArray& publicKeyFileName)
-{
- try {
- /* Initialize BIO */
- std::unique_ptr bioPublicKey { BIO_new_file(publicKeyFileName.data(), "w+"), BIO_free_all };
- if (bioPublicKey == nullptr) {
- throw std::runtime_error("Couldn't initialize \'bioPublicKey\'. BIO_new_file(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Write public key on file */
- if (!PEM_write_bio_RSA_PUBKEY(bioPublicKey.get(), rsa)) {
- throw std::runtime_error("Couldn't save public key. PEM_write_bio_RSAPublicKey(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
- } catch (std::exception& exception) {
- QSimpleCrypto::QRsa::error.setError(1, exception.what());
- return;
- } catch (...) {
- QSimpleCrypto::QRsa::error.setError(2, "Unknown error!");
- return;
- }
-}
-
-///
-/// \brief QSimpleCrypto::QRSA::savePrivateKey - Saves to file RSA private key.
-/// \param rsa - OpenSSL RSA structure.
-/// \param privateKeyFileName - Private key file name.
-/// \param password - Private key password.
-/// \param cipher - Can be used with 'OpenSSL EVP_CIPHER' (ecb, cbc, cfb, ofb, ctr) - 128, 192, 256. Example: EVP_aes_256_cbc().
-///
-void QSimpleCrypto::QRsa::savePrivateKey(RSA* rsa, const QByteArray& privateKeyFileName, QByteArray password, const EVP_CIPHER* cipher)
-{
- try {
- /* Initialize BIO */
- std::unique_ptr bioPrivateKey { BIO_new_file(privateKeyFileName.data(), "w+"), BIO_free_all };
- if (bioPrivateKey == nullptr) {
- throw std::runtime_error("Couldn't initialize bioPrivateKey. BIO_new_file(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Write private key to file */
- if (!PEM_write_bio_RSAPrivateKey(bioPrivateKey.get(), rsa, cipher, reinterpret_cast(password.data()), password.size(), nullptr, nullptr)) {
- throw std::runtime_error("Couldn't save private key. PEM_write_bio_RSAPrivateKey(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
- } catch (std::exception& exception) {
- QSimpleCrypto::QRsa::error.setError(1, exception.what());
- return;
- } catch (...) {
- QSimpleCrypto::QRsa::error.setError(2, "Unknown error!");
- return;
- }
-}
-
-///
-/// \brief QSimpleCrypto::QRSA::getPublicKeyFromFile - Gets RSA public key from a file.
-/// \param filePath - File path to public key file.
-/// \return Returns 'OpenSSL EVP_PKEY structure' or 'nullptr', if error happened. Returned value must be cleaned up with 'EVP_PKEY_free()' to avoid memory leak.
-///
-EVP_PKEY* QSimpleCrypto::QRsa::getPublicKeyFromFile(const QByteArray& filePath)
-{
- try {
- /* Initialize BIO */
- std::unique_ptr bioPublicKey { BIO_new_file(filePath.data(), "r"), BIO_free_all };
- if (bioPublicKey == nullptr) {
- throw std::runtime_error("Couldn't initialize bioPublicKey. BIO_new_file(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Initialize EVP_PKEY */
- EVP_PKEY* keyStore = nullptr;
- if (!(keyStore = EVP_PKEY_new())) {
- throw std::runtime_error("Couldn't initialize keyStore. EVP_PKEY_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Write private key to file */
- if (!PEM_read_bio_PUBKEY(bioPublicKey.get(), &keyStore, nullptr, nullptr)) {
- throw std::runtime_error("Couldn't read private key. PEM_read_bio_PrivateKey(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- return keyStore;
-
- } catch (std::exception& exception) {
- QSimpleCrypto::QRsa::error.setError(1, exception.what());
- return nullptr;
- } catch (...) {
- QSimpleCrypto::QRsa::error.setError(2, "Unknown error!");
- return nullptr;
- }
-}
-
-///
-/// \brief QSimpleCrypto::QRSA::getPrivateKeyFromFile - Gets RSA private key from a file.
-/// \param filePath - File path to private key file.
-/// \param password - Private key password.
-/// \return - Returns 'OpenSSL EVP_PKEY structure' or 'nullptr', if error happened. Returned value must be cleaned up with 'EVP_PKEY_free()' to avoid memory leak.
-///
-EVP_PKEY* QSimpleCrypto::QRsa::getPrivateKeyFromFile(const QByteArray& filePath, const QByteArray& password)
-{
- try {
- /* Initialize BIO */
- std::unique_ptr bioPrivateKey { BIO_new_file(filePath.data(), "r"), BIO_free_all };
- if (bioPrivateKey == nullptr) {
- throw std::runtime_error("Couldn't initialize bioPrivateKey. BIO_new_file(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Initialize EVP_PKEY */
- EVP_PKEY* keyStore = nullptr;
- if (!(keyStore = EVP_PKEY_new())) {
- throw std::runtime_error("Couldn't initialize keyStore. EVP_PKEY_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Write private key to file */
- if (!PEM_read_bio_PrivateKey(bioPrivateKey.get(), &keyStore, nullptr, (void*)password.data())) {
- throw std::runtime_error("Couldn't read private key. PEM_read_bio_PrivateKey(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- return keyStore;
-
- } catch (std::exception& exception) {
- QSimpleCrypto::QRsa::error.setError(1, exception.what());
- return nullptr;
- } catch (...) {
- QSimpleCrypto::QRsa::error.setError(2, "Unknown error!");
- return nullptr;
- }
-}
-
-///
-/// \brief QSimpleCrypto::QRSA::encrypt - Encrypt data with RSA algorithm.
-/// \param plaintext - Text that must be encrypted.
-/// \param rsa - OpenSSL RSA structure.
-/// \param encryptType - Public or private encrypt type. (PUBLIC_ENCRYPT, PRIVATE_ENCRYPT).
-/// \param padding - OpenSSL RSA padding can be used with: 'RSA_PKCS1_PADDING', 'RSA_NO_PADDING' and etc.
-/// \return Returns encrypted data or "", if error happened.
-///
-QByteArray QSimpleCrypto::QRsa::encrypt(QByteArray plainText, RSA* rsa, const int& encryptType, const int& padding)
-{
- try {
- /* Initialize array. Here encrypted data will be saved */
- std::unique_ptr cipherText { new unsigned char[RSA_size(rsa)]() };
- if (cipherText == nullptr) {
- throw std::runtime_error("Couldn't allocate memory for 'cipherText'.");
- }
-
- /* Result of encryption operation */
- short int result = 0;
-
- /* Execute encryption operation */
- if (encryptType == PublicDecrypt) {
- result = RSA_public_encrypt(plainText.size(), reinterpret_cast(plainText.data()), cipherText.get(), rsa, padding);
- } else if (encryptType == PrivateDecrypt) {
- result = RSA_private_encrypt(plainText.size(), reinterpret_cast(plainText.data()), cipherText.get(), rsa, padding);
- }
-
- /* Check for result */
- if (result <= -1) {
- throw std::runtime_error("Couldn't encrypt data. Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Get encrypted data */
- const QByteArray& encryptedData = QByteArray(reinterpret_cast(cipherText.get()), RSA_size(rsa));
-
- return encryptedData;
- } catch (std::exception& exception) {
- QSimpleCrypto::QRsa::error.setError(1, exception.what());
- return "";
- } catch (...) {
- QSimpleCrypto::QRsa::error.setError(2, "Unknown error!");
- return "";
- }
-}
-
-///
-/// \brief QSimpleCrypto::QRSA::decrypt - Decrypt data with RSA algorithm.
-/// \param cipherText - Text that must be decrypted.
-/// \param rsa - OpenSSL RSA structure.
-/// \param decryptType - Public or private type. (PUBLIC_DECRYPT, PRIVATE_DECRYPT).
-/// \param padding - RSA padding can be used with: 'RSA_PKCS1_PADDING', 'RSA_NO_PADDING' and etc.
-/// \return - Returns decrypted data or "", if error happened.
-///
-QByteArray QSimpleCrypto::QRsa::decrypt(QByteArray cipherText, RSA* rsa, const int& decryptType, const int& padding)
-{
- try {
- /* Initialize array. Here decrypted data will be saved */
- std::unique_ptr plainText { new unsigned char[cipherText.size()]() };
- if (plainText == nullptr) {
- throw std::runtime_error("Couldn't allocate memory for 'plainText'.");
- }
-
- /* Result of decryption operation */
- short int result = 0;
-
- /* Execute decryption operation */
- if (decryptType == PublicDecrypt) {
- result = RSA_public_decrypt(RSA_size(rsa), reinterpret_cast(cipherText.data()), plainText.get(), rsa, padding);
- } else if (decryptType == PrivateDecrypt) {
- result = RSA_private_decrypt(RSA_size(rsa), reinterpret_cast(cipherText.data()), plainText.get(), rsa, padding);
- }
-
- /* Check for result */
- if (result <= -1) {
- throw std::runtime_error("Couldn't decrypt data. Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Get decrypted data */
- const QByteArray& decryptedData = QByteArray(reinterpret_cast(plainText.get()));
-
- return decryptedData;
- } catch (std::exception& exception) {
- QSimpleCrypto::QRsa::error.setError(1, exception.what());
- return "";
- } catch (...) {
- QSimpleCrypto::QRsa::error.setError(2, "Unknown error!");
- return "";
- }
-}
diff --git a/client/3rd/QSimpleCrypto/sources/QX509.cpp b/client/3rd/QSimpleCrypto/sources/QX509.cpp
deleted file mode 100644
index ac4fd270..00000000
--- a/client/3rd/QSimpleCrypto/sources/QX509.cpp
+++ /dev/null
@@ -1,234 +0,0 @@
-/**
- * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution
-**/
-
-#include "include/QX509.h"
-
-QSimpleCrypto::QX509::QX509()
-{
-}
-
-///
-/// \brief QSimpleCrypto::QX509::loadCertificateFromFile - Function load X509 from file and returns OpenSSL structure.
-/// \param fileName - File path to certificate.
-/// \return Returns OpenSSL X509 structure or nullptr, if error happened. Returned value must be cleaned up with 'X509_free' to avoid memory leak.
-///
-X509* QSimpleCrypto::QX509::loadCertificateFromFile(const QByteArray& fileName)
-{
- try {
- /* Initialize X509 */
- X509* x509 = nullptr;
- if (!(x509 = X509_new())) {
- throw std::runtime_error("Couldn't initialize X509. X509_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Initialize BIO */
- std::unique_ptr certFile { BIO_new_file(fileName.data(), "r+"), BIO_free_all };
- if (certFile == nullptr) {
- throw std::runtime_error("Couldn't initialize certFile. BIO_new_file(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Read file */
- if (!PEM_read_bio_X509(certFile.get(), &x509, nullptr, nullptr)) {
- throw std::runtime_error("Couldn't read certificate file from disk. PEM_read_bio_X509(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- return x509;
- } catch (std::exception& exception) {
- QSimpleCrypto::QX509::error.setError(1, exception.what());
- return nullptr;
- } catch (...) {
- QSimpleCrypto::QX509::error.setError(2, "Unknown error!");
- return nullptr;
- }
-}
-
-///
-/// \brief QSimpleCrypto::QX509::signCertificate - Function signs X509 certificate and returns signed X509 OpenSSL structure.
-/// \param endCertificate - Certificate that will be signed
-/// \param caCertificate - CA certificate that will sign end certificate
-/// \param caPrivateKey - CA certificate private key
-/// \param fileName - With that name certificate will be saved. Leave "", if don't need to save it
-/// \return Returns OpenSSL X509 structure or nullptr, if error happened.
-///
-X509* QSimpleCrypto::QX509::signCertificate(X509* endCertificate, X509* caCertificate, EVP_PKEY* caPrivateKey, const QByteArray& fileName)
-{
- try {
- /* Set issuer to CA's subject. */
- if (!X509_set_issuer_name(endCertificate, X509_get_subject_name(caCertificate))) {
- throw std::runtime_error("Couldn't set issuer name for X509. X509_set_issuer_name(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Sign the certificate with key. */
- if (!X509_sign(endCertificate, caPrivateKey, EVP_sha256())) {
- throw std::runtime_error("Couldn't sign X509. X509_sign(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Write certificate file on disk. If needed */
- if (!fileName.isEmpty()) {
- /* Initialize BIO */
- std::unique_ptr certFile { BIO_new_file(fileName.data(), "w+"), BIO_free_all };
- if (certFile == nullptr) {
- throw std::runtime_error("Couldn't initialize certFile. BIO_new_file(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Write file on disk */
- if (!PEM_write_bio_X509(certFile.get(), endCertificate)) {
- throw std::runtime_error("Couldn't write certificate file on disk. PEM_write_bio_X509(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
- }
-
- return endCertificate;
- } catch (std::exception& exception) {
- QSimpleCrypto::QX509::error.setError(1, exception.what());
- return nullptr;
- } catch (...) {
- QSimpleCrypto::QX509::error.setError(2, "Unknown error!");
- return nullptr;
- }
-}
-
-///
-/// \brief QSimpleCrypto::QX509::verifyCertificate - Function verifies X509 certificate and returns verified X509 OpenSSL structure.
-/// \param x509 - OpenSSL X509. That certificate will be verified.
-/// \param store - Trusted certificate must be added to X509_Store with 'addCertificateToStore(X509_STORE* ctx, X509* x509)'.
-/// \return Returns OpenSSL X509 structure or nullptr, if error happened
-///
-X509* QSimpleCrypto::QX509::verifyCertificate(X509* x509, X509_STORE* store)
-{
- try {
- /* Initialize X509_STORE_CTX */
- std::unique_ptr ctx { X509_STORE_CTX_new(), X509_STORE_CTX_free };
- if (ctx == nullptr) {
- throw std::runtime_error("Couldn't initialize keyStore. EVP_PKEY_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Set up CTX for a subsequent verification operation */
- if (!X509_STORE_CTX_init(ctx.get(), store, x509, nullptr)) {
- throw std::runtime_error("Couldn't initialize X509_STORE_CTX. X509_STORE_CTX_init(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Verify X509 */
- if (!X509_verify_cert(ctx.get())) {
- throw std::runtime_error("Couldn't verify cert. X509_verify_cert(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- return x509;
- } catch (std::exception& exception) {
- QSimpleCrypto::QX509::error.setError(1, exception.what());
- return nullptr;
- } catch (...) {
- QSimpleCrypto::QX509::error.setError(2, "Unknown error!");
- return nullptr;
- }
-}
-
-///
-/// \brief QSimpleCrypto::QX509::generateSelfSignedCertificate - Function generatesand returns self signed X509.
-/// \param rsa - OpenSSL RSA.
-/// \param additionalData - Certificate information.
-/// \param certificateFileName - With that name certificate will be saved. Leave "", if don't need to save it.
-/// \param md - OpenSSL EVP_MD structure. Example: EVP_sha512().
-/// \param serialNumber - X509 certificate serial number.
-/// \param version - X509 certificate version.
-/// \param notBefore - X509 start date.
-/// \param notAfter - X509 end date.
-/// \return Returns OpenSSL X509 structure or nullptr, if error happened. Returned value must be cleaned up with 'X509_free' to avoid memory leak.
-///
-X509* QSimpleCrypto::QX509::generateSelfSignedCertificate(RSA* rsa, const QMap& additionalData,
- const QByteArray& certificateFileName, const EVP_MD* md,
- const long& serialNumber, const long& version,
- const long& notBefore, const long& notAfter)
-{
- try {
- /* Initialize X509 */
- X509* x509 = nullptr;
- if (!(x509 = X509_new())) {
- throw std::runtime_error("Couldn't initialize X509. X509_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Initialize EVP_PKEY */
- std::unique_ptr keyStore { EVP_PKEY_new(), EVP_PKEY_free };
- if (keyStore == nullptr) {
- throw std::runtime_error("Couldn't initialize keyStore. EVP_PKEY_new(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Sign rsa key */
- if (!EVP_PKEY_assign_RSA(keyStore.get(), rsa)) {
- throw std::runtime_error("Couldn't assign rsa. EVP_PKEY_assign_RSA(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Set certificate serial number. */
- if (!ASN1_INTEGER_set(X509_get_serialNumber(x509), serialNumber)) {
- throw std::runtime_error("Couldn't set serial number. ASN1_INTEGER_set(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Set certificate version */
- if (!X509_set_version(x509, version)) {
- throw std::runtime_error("Couldn't set version. X509_set_version(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Set certificate creation and expiration date */
- X509_gmtime_adj(X509_get_notBefore(x509), notBefore);
- X509_gmtime_adj(X509_get_notAfter(x509), notAfter);
-
- /* Set certificate public key */
- if (!X509_set_pubkey(x509, keyStore.get())) {
- throw std::runtime_error("Couldn't set public key. X509_set_pubkey(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Initialize X509_NAME */
- X509_NAME* x509Name = X509_get_subject_name(x509);
- if (x509Name == nullptr) {
- throw std::runtime_error("Couldn't initialize X509_NAME. X509_NAME(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Add additional data to certificate */
- QMapIterator certificateInformationList(additionalData);
- while (certificateInformationList.hasNext()) {
- /* Read next item in list */
- certificateInformationList.next();
-
- /* Set additional data */
- if (!X509_NAME_add_entry_by_txt(x509Name, certificateInformationList.key().data(), MBSTRING_UTF8, reinterpret_cast(certificateInformationList.value().data()), -1, -1, 0)) {
- throw std::runtime_error("Couldn't set additional information. X509_NAME_add_entry_by_txt(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
- }
-
- /* Set certificate info */
- if (!X509_set_issuer_name(x509, x509Name)) {
- throw std::runtime_error("Couldn't set issuer name. X509_set_issuer_name(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Sign certificate */
- if (!X509_sign(x509, keyStore.get(), md)) {
- throw std::runtime_error("Couldn't sign X509. X509_sign(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Write certificate file on disk. If needed */
- if (!certificateFileName.isEmpty()) {
- /* Initialize BIO */
- std::unique_ptr certFile { BIO_new_file(certificateFileName.data(), "w+"), BIO_free_all };
- if (certFile == nullptr) {
- throw std::runtime_error("Couldn't initialize certFile. BIO_new_file(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
-
- /* Write file on disk */
- if (!PEM_write_bio_X509(certFile.get(), x509)) {
- throw std::runtime_error("Couldn't write certificate file on disk. PEM_write_bio_X509(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- }
- }
-
- return x509;
- } catch (std::exception& exception) {
- QSimpleCrypto::QX509::error.setError(1, exception.what());
- return nullptr;
- } catch (...) {
- QSimpleCrypto::QX509::error.setError(2, "Unknown error!");
- return nullptr;
- }
-}
diff --git a/client/3rd/QSimpleCrypto/sources/QX509Store.cpp b/client/3rd/QSimpleCrypto/sources/QX509Store.cpp
deleted file mode 100644
index bbbec1a8..00000000
--- a/client/3rd/QSimpleCrypto/sources/QX509Store.cpp
+++ /dev/null
@@ -1,176 +0,0 @@
-/**
- * Copyright 2021 BrutalWizard (https://github.com/bru74lw1z4rd). All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution
-**/
-
-#include "include/QX509Store.h"
-
-QSimpleCrypto::QX509Store::QX509Store()
-{
-}
-
-///
-/// \brief QSimpleCrypto::QX509::addCertificateToStore
-/// \param store - OpenSSL X509_STORE.
-/// \param x509 - OpenSSL X509.
-/// \return Returns 'true' on success and 'false', if error happened.
-///
-bool QSimpleCrypto::QX509Store::addCertificateToStore(X509_STORE* store, X509* x509)
-{
- if (!X509_STORE_add_cert(store, x509)) {
- QSimpleCrypto::QX509Store::error.setError(1, "Couldn't add certificate to X509_STORE. X509_STORE_add_cert(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- return false;
- }
-
- return true;
-}
-
-///
-/// \brief QSimpleCrypto::QX509Store::addLookup
-/// \param store - OpenSSL X509_STORE.
-/// \param method - OpenSSL X509_LOOKUP_METHOD. Example: X509_LOOKUP_file.
-/// \return Returns 'true' on success and 'false', if error happened.
-///
-bool QSimpleCrypto::QX509Store::addLookup(X509_STORE* store, X509_LOOKUP_METHOD* method)
-{
- if (!X509_STORE_add_lookup(store, method)) {
- QSimpleCrypto::QX509Store::error.setError(1, "Couldn't add lookup to X509_STORE. X509_STORE_add_lookup(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- return false;
- }
-
- return true;
-}
-
-///
-/// \brief QSimpleCrypto::QX509Store::setCertificateDepth
-/// \param store - OpenSSL X509_STORE.
-/// \param depth - That is the maximum number of untrusted CA certificates that can appear in a chain. Example: 0.
-/// \return Returns 'true' on success and 'false', if error happened.
-///
-bool QSimpleCrypto::QX509Store::setDepth(X509_STORE* store, const int& depth)
-{
- if (!X509_STORE_set_depth(store, depth)) {
- QSimpleCrypto::QX509Store::error.setError(1, "Couldn't set depth for X509_STORE. X509_STORE_set_depth(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- return false;
- }
-
- return true;
-}
-
-///
-/// \brief QSimpleCrypto::QX509Store::setFlag
-/// \param store - OpenSSL X509_STORE.
-/// \param flag - The verification flags consists of zero or more of the following flags ored together. Example: X509_V_FLAG_CRL_CHECK.
-/// \return Returns 'true' on success and 'false', if error happened.
-///
-bool QSimpleCrypto::QX509Store::setFlag(X509_STORE* store, const unsigned long& flag)
-{
- if (!X509_STORE_set_flags(store, flag)) {
- QSimpleCrypto::QX509Store::error.setError(1, "Couldn't set flag for X509_STORE. X509_STORE_set_flags(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- return false;
- }
-
- return true;
-}
-
-///
-/// \brief QSimpleCrypto::QX509Store::setFlag
-/// \param store - OpenSSL X509_STORE.
-/// \param purpose - Verification purpose in param to purpose. Example: X509_PURPOSE_ANY.
-/// \return Returns 'true' on success and 'false', if error happened.
-///
-bool QSimpleCrypto::QX509Store::setPurpose(X509_STORE* store, const int& purpose)
-{
- if (!X509_STORE_set_purpose(store, purpose)) {
- QSimpleCrypto::QX509Store::error.setError(1, "Couldn't set purpose for X509_STORE. X509_STORE_set_purpose(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- return false;
- }
-
- return true;
-}
-
-///
-/// \brief QSimpleCrypto::QX509Store::setTrust
-/// \param store - OpenSSL X509_STORE.
-/// \param trust - Trust Level. Example: X509_TRUST_SSL_SERVER.
-/// \return Returns 'true' on success and 'false', if error happened.
-///
-bool QSimpleCrypto::QX509Store::setTrust(X509_STORE* store, const int& trust)
-{
- if (!X509_STORE_set_trust(store, trust)) {
- QSimpleCrypto::QX509Store::error.setError(1, "Couldn't set trust for X509_STORE. X509_STORE_set_trust(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- return false;
- }
-
- return true;
-}
-
-///
-/// \brief QSimpleCrypto::QX509Store::setDefaultPaths
-/// \param store - OpenSSL X509_STORE.
-/// \return Returns 'true' on success and 'false', if error happened.
-///
-bool QSimpleCrypto::QX509Store::setDefaultPaths(X509_STORE* store)
-{
- if (!X509_STORE_set_default_paths(store)) {
- QSimpleCrypto::QX509Store::error.setError(1, "Couldn't set default paths for X509_STORE. X509_STORE_set_default_paths(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- return false;
- }
-
- return true;
-}
-
-///
-/// \brief QSimpleCrypto::QX509Store::loadLocations
-/// \param store - OpenSSL X509_STORE.
-/// \param fileName - File name. Example: "caCertificate.pem".
-/// \param dirPath - Path to file. Example: "path/To/File".
-/// \return Returns 'true' on success and 'false', if error happened.
-///
-bool QSimpleCrypto::QX509Store::loadLocations(X509_STORE* store, const QByteArray& fileName, const QByteArray& dirPath)
-{
- if (!X509_STORE_load_locations(store, fileName, dirPath)) {
- QSimpleCrypto::QX509Store::error.setError(1, "Couldn't load locations for X509_STORE. X509_STORE_load_locations(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- return false;
- }
-
- return true;
-}
-
-///
-/// \brief QSimpleCrypto::QX509Store::loadLocations
-/// \param store - OpenSSL X509_STORE.
-/// \param file - Qt QFile that will be loaded.
-/// \return Returns 'true' on success and 'false', if error happened.
-///
-bool QSimpleCrypto::QX509Store::loadLocations(X509_STORE* store, const QFile& file)
-{
- /* Initialize QFileInfo to read information about file */
- QFileInfo info(file);
-
- if (!X509_STORE_load_locations(store, info.fileName().toLocal8Bit(), info.absoluteDir().path().toLocal8Bit())) {
- QSimpleCrypto::QX509Store::error.setError(1, "Couldn't load locations for X509_STORE. X509_STORE_load_locations(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- return false;
- }
-
- return true;
-}
-
-///
-/// \brief QSimpleCrypto::QX509Store::loadLocations
-/// \param store - OpenSSL X509_STORE.
-/// \param fileInfo - Qt QFileInfo.
-/// \return Returns 'true' on success and 'false', if error happened.
-///
-bool QSimpleCrypto::QX509Store::loadLocations(X509_STORE* store, const QFileInfo& fileInfo)
-{
- if (!X509_STORE_load_locations(store, fileInfo.fileName().toLocal8Bit(), fileInfo.absoluteDir().path().toLocal8Bit())) {
- QSimpleCrypto::QX509Store::error.setError(1, "Couldn't load locations for X509_STORE. X509_STORE_load_locations(). Error: " + QByteArray(ERR_error_string(ERR_get_error(), nullptr)));
- return false;
- }
-
- return true;
-}
diff --git a/client/3rd/SingleApplication/singleapplication.cmake b/client/3rd/SingleApplication/singleapplication.cmake
deleted file mode 100644
index 78abfa8a..00000000
--- a/client/3rd/SingleApplication/singleapplication.cmake
+++ /dev/null
@@ -1,25 +0,0 @@
-include_directories(${CMAKE_CURRENT_LIST_DIR})
-
-find_package(Qt6 REQUIRED COMPONENTS
- Core Network
-)
-set(LIBS ${LIBS} Qt6::Core Qt6::Network)
-
-
-set(HEADERS ${HEADERS}
- ${CMAKE_CURRENT_LIST_DIR}/singleapplication.h
- ${CMAKE_CURRENT_LIST_DIR}/singleapplication_p.h
-)
-
-set(SOURCES ${SOURCES}
- ${CMAKE_CURRENT_LIST_DIR}/singleapplication.cpp
- ${CMAKE_CURRENT_LIST_DIR}/singleapplication_p.cpp
-)
-
-if(WIN32)
- if(MSVC)
- set(LIBS ${LIBS} Advapi32.lib)
- elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
- set(LIBS ${LIBS} advapi32)
- endif()
-endif()
diff --git a/client/3rd/SingleApplication/singleapplication.cpp b/client/3rd/SingleApplication/singleapplication.cpp
deleted file mode 100644
index 7e153a00..00000000
--- a/client/3rd/SingleApplication/singleapplication.cpp
+++ /dev/null
@@ -1,274 +0,0 @@
-// The MIT License (MIT)
-//
-// Copyright (c) Itay Grudev 2015 - 2020
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-#include
-#include
-#include
-
-#include "singleapplication.h"
-#include "singleapplication_p.h"
-
-/**
- * @brief Constructor. Checks and fires up LocalServer or closes the program
- * if another instance already exists
- * @param argc
- * @param argv
- * @param allowSecondary Whether to enable secondary instance support
- * @param options Optional flags to toggle specific behaviour
- * @param timeout Maximum time blocking functions are allowed during app load
- */
-SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout, const QString &userData )
- : app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
-{
- Q_D( SingleApplication );
-
-#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
- // On Android and iOS since the library is not supported fallback to
- // standard QApplication behaviour by simply returning at this point.
- qWarning() << "SingleApplication is not supported on Android and iOS systems.";
- return;
-#endif
-
- // Store the current mode of the program
- d->options = options;
-
- // Add any unique user data
- if ( ! userData.isEmpty() )
- d->addAppData( userData );
-
- // Generating an application ID used for identifying the shared memory
- // block and QLocalServer
- d->genBlockServerName();
-
- // To mitigate QSharedMemory issues with large amount of processes
- // attempting to attach at the same time
- SingleApplicationPrivate::randomSleep();
-
-#ifdef Q_OS_UNIX
- // By explicitly attaching it and then deleting it we make sure that the
- // memory is deleted even after the process has crashed on Unix.
- d->memory = new QSharedMemory( d->blockServerName );
- d->memory->attach();
- delete d->memory;
-#endif
- // Guarantee thread safe behaviour with a shared memory block.
- d->memory = new QSharedMemory( d->blockServerName );
-
- // Create a shared memory block
- if( d->memory->create( sizeof( InstancesInfo ) )){
- // Initialize the shared memory block
- if( ! d->memory->lock() ){
- qCritical() << "SingleApplication: Unable to lock memory block after create.";
- abortSafely();
- }
- d->initializeMemoryBlock();
- } else {
- if( d->memory->error() == QSharedMemory::AlreadyExists ){
- // Attempt to attach to the memory segment
- if( ! d->memory->attach() ){
- qCritical() << "SingleApplication: Unable to attach to shared memory block.";
- abortSafely();
- }
- if( ! d->memory->lock() ){
- qCritical() << "SingleApplication: Unable to lock memory block after attach.";
- abortSafely();
- }
- } else {
- qCritical() << "SingleApplication: Unable to create block.";
- abortSafely();
- }
- }
-
- auto *inst = static_cast( d->memory->data() );
- QElapsedTimer time;
- time.start();
-
- // Make sure the shared memory block is initialised and in consistent state
- while( true ){
- // If the shared memory block's checksum is valid continue
- if( d->blockChecksum() == inst->checksum ) break;
-
- // If more than 5s have elapsed, assume the primary instance crashed and
- // assume it's position
- if( time.elapsed() > 5000 ){
- qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
- d->initializeMemoryBlock();
- }
-
- // Otherwise wait for a random period and try again. The random sleep here
- // limits the probability of a collision between two racing apps and
- // allows the app to initialise faster
- if( ! d->memory->unlock() ){
- qDebug() << "SingleApplication: Unable to unlock memory for random wait.";
- qDebug() << d->memory->errorString();
- }
- SingleApplicationPrivate::randomSleep();
- if( ! d->memory->lock() ){
- qCritical() << "SingleApplication: Unable to lock memory after random wait.";
- abortSafely();
- }
- }
-
- if( inst->primary == false ){
- d->startPrimary();
- if( ! d->memory->unlock() ){
- qDebug() << "SingleApplication: Unable to unlock memory after primary start.";
- qDebug() << d->memory->errorString();
- }
- return;
- }
-
- // Check if another instance can be started
- if( allowSecondary ){
- d->startSecondary();
- if( d->options & Mode::SecondaryNotification ){
- d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance );
- }
- if( ! d->memory->unlock() ){
- qDebug() << "SingleApplication: Unable to unlock memory after secondary start.";
- qDebug() << d->memory->errorString();
- }
- return;
- }
-
- if( ! d->memory->unlock() ){
- qDebug() << "SingleApplication: Unable to unlock memory at end of execution.";
- qDebug() << d->memory->errorString();
- }
-
- d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance );
-
- delete d;
-
- ::exit( EXIT_SUCCESS );
-}
-
-SingleApplication::~SingleApplication()
-{
- Q_D( SingleApplication );
- delete d;
-}
-
-/**
- * Checks if the current application instance is primary.
- * @return Returns true if the instance is primary, false otherwise.
- */
-bool SingleApplication::isPrimary() const
-{
- Q_D( const SingleApplication );
- return d->server != nullptr;
-}
-
-/**
- * Checks if the current application instance is secondary.
- * @return Returns true if the instance is secondary, false otherwise.
- */
-bool SingleApplication::isSecondary() const
-{
- Q_D( const SingleApplication );
- return d->server == nullptr;
-}
-
-/**
- * Allows you to identify an instance by returning unique consecutive instance
- * ids. It is reset when the first (primary) instance of your app starts and
- * only incremented afterwards.
- * @return Returns a unique instance id.
- */
-quint32 SingleApplication::instanceId() const
-{
- Q_D( const SingleApplication );
- return d->instanceNumber;
-}
-
-/**
- * Returns the OS PID (Process Identifier) of the process running the primary
- * instance. Especially useful when SingleApplication is coupled with OS.
- * specific APIs.
- * @return Returns the primary instance PID.
- */
-qint64 SingleApplication::primaryPid() const
-{
- Q_D( const SingleApplication );
- return d->primaryPid();
-}
-
-/**
- * Returns the username the primary instance is running as.
- * @return Returns the username the primary instance is running as.
- */
-QString SingleApplication::primaryUser() const
-{
- Q_D( const SingleApplication );
- return d->primaryUser();
-}
-
-/**
- * Returns the username the current instance is running as.
- * @return Returns the username the current instance is running as.
- */
-QString SingleApplication::currentUser() const
-{
- return SingleApplicationPrivate::getUsername();
-}
-
-/**
- * Sends message to the Primary Instance.
- * @param message The message to send.
- * @param timeout the maximum timeout in milliseconds for blocking functions.
- * @return true if the message was sent successfuly, false otherwise.
- */
-bool SingleApplication::sendMessage( const QByteArray &message, int timeout )
-{
- Q_D( SingleApplication );
-
- // Nobody to connect to
- if( isPrimary() ) return false;
-
- // Make sure the socket is connected
- if( ! d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect ) )
- return false;
-
- d->socket->write( message );
- bool dataWritten = d->socket->waitForBytesWritten( timeout );
- d->socket->flush();
- return dataWritten;
-}
-
-/**
- * Cleans up the shared memory block and exits with a failure.
- * This function halts program execution.
- */
-void SingleApplication::abortSafely()
-{
- Q_D( SingleApplication );
-
- qCritical() << "SingleApplication: " << d->memory->error() << d->memory->errorString();
- delete d;
- ::exit( EXIT_FAILURE );
-}
-
-QStringList SingleApplication::userData() const
-{
- Q_D( const SingleApplication );
- return d->appData();
-}
diff --git a/client/3rd/SingleApplication/singleapplication.h b/client/3rd/SingleApplication/singleapplication.h
deleted file mode 100644
index 400c88ac..00000000
--- a/client/3rd/SingleApplication/singleapplication.h
+++ /dev/null
@@ -1,154 +0,0 @@
-// The MIT License (MIT)
-//
-// Copyright (c) Itay Grudev 2015 - 2018
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-#ifndef SINGLE_APPLICATION_H
-#define SINGLE_APPLICATION_H
-
-#include
-#include
-
-#ifndef QAPPLICATION_CLASS
- #define QAPPLICATION_CLASS QApplication
-#endif
-
-#include QT_STRINGIFY(QAPPLICATION_CLASS)
-
-class SingleApplicationPrivate;
-
-/**
- * @brief The SingleApplication class handles multiple instances of the same
- * Application
- * @see QCoreApplication
- */
-class SingleApplication : public QAPPLICATION_CLASS
-{
- Q_OBJECT
-
- using app_t = QAPPLICATION_CLASS;
-
-public:
- /**
- * @brief Mode of operation of SingleApplication.
- * Whether the block should be user-wide or system-wide and whether the
- * primary instance should be notified when a secondary instance had been
- * started.
- * @note Operating system can restrict the shared memory blocks to the same
- * user, in which case the User/System modes will have no effect and the
- * block will be user wide.
- * @enum
- */
- enum Mode {
- User = 1 << 0,
- System = 1 << 1,
- SecondaryNotification = 1 << 2,
- ExcludeAppVersion = 1 << 3,
- ExcludeAppPath = 1 << 4
- };
- Q_DECLARE_FLAGS(Options, Mode)
-
- /**
- * @brief Intitializes a SingleApplication instance with argc command line
- * arguments in argv
- * @arg {int &} argc - Number of arguments in argv
- * @arg {const char *[]} argv - Supplied command line arguments
- * @arg {bool} allowSecondary - Whether to start the instance as secondary
- * if there is already a primary instance.
- * @arg {Mode} mode - Whether for the SingleApplication block to be applied
- * User wide or System wide.
- * @arg {int} timeout - Timeout to wait in milliseconds.
- * @note argc and argv may be changed as Qt removes arguments that it
- * recognizes
- * @note Mode::SecondaryNotification only works if set on both the primary
- * instance and the secondary instance.
- * @note The timeout is just a hint for the maximum time of blocking
- * operations. It does not guarantee that the SingleApplication
- * initialisation will be completed in given time, though is a good hint.
- * Usually 4*timeout would be the worst case (fail) scenario.
- * @see See the corresponding QAPPLICATION_CLASS constructor for reference
- */
- explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000, const QString &userData = {} );
- ~SingleApplication() override;
-
- /**
- * @brief Returns if the instance is the primary instance
- * @returns {bool}
- */
- bool isPrimary() const;
-
- /**
- * @brief Returns if the instance is a secondary instance
- * @returns {bool}
- */
- bool isSecondary() const;
-
- /**
- * @brief Returns a unique identifier for the current instance
- * @returns {qint32}
- */
- quint32 instanceId() const;
-
- /**
- * @brief Returns the process ID (PID) of the primary instance
- * @returns {qint64}
- */
- qint64 primaryPid() const;
-
- /**
- * @brief Returns the username of the user running the primary instance
- * @returns {QString}
- */
- QString primaryUser() const;
-
- /**
- * @brief Returns the username of the current user
- * @returns {QString}
- */
- QString currentUser() const;
-
- /**
- * @brief Sends a message to the primary instance. Returns true on success.
- * @param {int} timeout - Timeout for connecting
- * @returns {bool}
- * @note sendMessage() will return false if invoked from the primary
- * instance.
- */
- bool sendMessage( const QByteArray &message, int timeout = 100 );
-
- /**
- * @brief Get the set user data.
- * @returns {QStringList}
- */
- QStringList userData() const;
-
-Q_SIGNALS:
- void instanceStarted();
- void receivedMessage( quint32 instanceId, QByteArray message );
-
-private:
- SingleApplicationPrivate *d_ptr;
- Q_DECLARE_PRIVATE(SingleApplication)
- void abortSafely();
-};
-
-Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
-
-#endif // SINGLE_APPLICATION_H
diff --git a/client/3rd/SingleApplication/singleapplication.pri b/client/3rd/SingleApplication/singleapplication.pri
deleted file mode 100644
index 80283fc4..00000000
--- a/client/3rd/SingleApplication/singleapplication.pri
+++ /dev/null
@@ -1,15 +0,0 @@
-QT += core network
-CONFIG += c++11
-
-HEADERS += \
- $$PWD/singleapplication.h \
- $$PWD/singleapplication_p.h
-SOURCES += $$PWD/singleapplication.cpp \
- $$PWD/singleapplication_p.cpp
-
-INCLUDEPATH += $$PWD
-
-win32 {
- msvc:LIBS += Advapi32.lib
- gcc:LIBS += -ladvapi32
-}
diff --git a/client/3rd/SingleApplication/singleapplication_p.cpp b/client/3rd/SingleApplication/singleapplication_p.cpp
deleted file mode 100644
index e65bd955..00000000
--- a/client/3rd/SingleApplication/singleapplication_p.cpp
+++ /dev/null
@@ -1,486 +0,0 @@
-// The MIT License (MIT)
-//
-// Copyright (c) Itay Grudev 2015 - 2020
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-//
-// W A R N I N G !!!
-// -----------------
-//
-// This file is not part of the SingleApplication API. It is used purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or may even be removed.
-//
-
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
-#include
-#else
-#include
-#endif
-
-#include "singleapplication.h"
-#include "singleapplication_p.h"
-
-#ifdef Q_OS_UNIX
- #include
- #include
- #include
-#endif
-
-#ifdef Q_OS_WIN
- #ifndef NOMINMAX
- #define NOMINMAX 1
- #endif
- #include
- #include
-#endif
-
-SingleApplicationPrivate::SingleApplicationPrivate( SingleApplication *q_ptr )
- : q_ptr( q_ptr )
-{
- server = nullptr;
- socket = nullptr;
- memory = nullptr;
- instanceNumber = 0;
-}
-
-SingleApplicationPrivate::~SingleApplicationPrivate()
-{
- if( socket != nullptr ){
- socket->close();
- delete socket;
- }
-
- if( memory != nullptr ){
- memory->lock();
- auto *inst = static_cast(memory->data());
- if( server != nullptr ){
- server->close();
- delete server;
- inst->primary = false;
- inst->primaryPid = -1;
- inst->primaryUser[0] = '\0';
- inst->checksum = blockChecksum();
- }
- memory->unlock();
-
- delete memory;
- }
-}
-
-QString SingleApplicationPrivate::getUsername()
-{
-#ifdef Q_OS_WIN
- wchar_t username[UNLEN + 1];
- // Specifies size of the buffer on input
- DWORD usernameLength = UNLEN + 1;
- if( GetUserNameW( username, &usernameLength ) )
- return QString::fromWCharArray( username );
-#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
- return QString::fromLocal8Bit( qgetenv( "USERNAME" ) );
-#else
- return qEnvironmentVariable( "USERNAME" );
-#endif
-#endif
-#ifdef Q_OS_UNIX
- QString username;
- uid_t uid = geteuid();
- struct passwd *pw = getpwuid( uid );
- if( pw )
- username = QString::fromLocal8Bit( pw->pw_name );
- if ( username.isEmpty() ){
-#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
- username = QString::fromLocal8Bit( qgetenv( "USER" ) );
-#else
- username = qEnvironmentVariable( "USER" );
-#endif
- }
- return username;
-#endif
-}
-
-void SingleApplicationPrivate::genBlockServerName()
-{
- QCryptographicHash appData( QCryptographicHash::Sha256 );
- appData.addData( "SingleApplication", 17 );
- appData.addData( SingleApplication::app_t::applicationName().toUtf8() );
- appData.addData( SingleApplication::app_t::organizationName().toUtf8() );
- appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() );
-
- if ( ! appDataList.isEmpty() )
- appData.addData( appDataList.join( "" ).toUtf8() );
-
- if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ){
- appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() );
- }
-
- if( ! (options & SingleApplication::Mode::ExcludeAppPath) ){
-#ifdef Q_OS_WIN
- appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() );
-#else
- appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
-#endif
- }
-
- // User level block requires a user specific data in the hash
- if( options & SingleApplication::Mode::User ){
- appData.addData( getUsername().toUtf8() );
- }
-
- // Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
- // server naming requirements.
- blockServerName = appData.result().toBase64().replace("/", "_");
-}
-
-void SingleApplicationPrivate::initializeMemoryBlock() const
-{
- auto *inst = static_cast( memory->data() );
- inst->primary = false;
- inst->secondary = 0;
- inst->primaryPid = -1;
- inst->primaryUser[0] = '\0';
- inst->checksum = blockChecksum();
-}
-
-void SingleApplicationPrivate::startPrimary()
-{
- // Reset the number of connections
- auto *inst = static_cast ( memory->data() );
-
- inst->primary = true;
- inst->primaryPid = QCoreApplication::applicationPid();
- qstrncpy( inst->primaryUser, getUsername().toUtf8().data(), sizeof(inst->primaryUser) );
- inst->checksum = blockChecksum();
- instanceNumber = 0;
- // Successful creation means that no main process exists
- // So we start a QLocalServer to listen for connections
- QLocalServer::removeServer( blockServerName );
- server = new QLocalServer();
-
- // Restrict access to the socket according to the
- // SingleApplication::Mode::User flag on User level or no restrictions
- if( options & SingleApplication::Mode::User ){
- server->setSocketOptions( QLocalServer::UserAccessOption );
- } else {
- server->setSocketOptions( QLocalServer::WorldAccessOption );
- }
-
- server->listen( blockServerName );
- QObject::connect(
- server,
- &QLocalServer::newConnection,
- this,
- &SingleApplicationPrivate::slotConnectionEstablished
- );
-}
-
-void SingleApplicationPrivate::startSecondary()
-{
- auto *inst = static_cast ( memory->data() );
-
- inst->secondary += 1;
- inst->checksum = blockChecksum();
- instanceNumber = inst->secondary;
-}
-
-bool SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType )
-{
- QElapsedTimer time;
- time.start();
-
- // Connect to the Local Server of the Primary Instance if not already
- // connected.
- if( socket == nullptr ){
- socket = new QLocalSocket();
- }
-
- if( socket->state() == QLocalSocket::ConnectedState ) return true;
-
- if( socket->state() != QLocalSocket::ConnectedState ){
-
- while( true ){
- randomSleep();
-
- if( socket->state() != QLocalSocket::ConnectingState )
- socket->connectToServer( blockServerName );
-
- if( socket->state() == QLocalSocket::ConnectingState ){
- socket->waitForConnected( static_cast(msecs - time.elapsed()) );
- }
-
- // If connected break out of the loop
- if( socket->state() == QLocalSocket::ConnectedState ) break;
-
- // If elapsed time since start is longer than the method timeout return
- if( time.elapsed() >= msecs ) return false;
- }
- }
-
- // Initialisation message according to the SingleApplication protocol
- QByteArray initMsg;
- QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
-
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
- writeStream.setVersion(QDataStream::Qt_5_6);
-#endif
-
- writeStream << blockServerName.toLatin1();
- writeStream << static_cast(connectionType);
- writeStream << instanceNumber;
-#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
- quint16 checksum = qChecksum(QByteArray(initMsg, static_cast(initMsg.length())));
-#else
- quint16 checksum = qChecksum(initMsg.constData(), static_cast(initMsg.length()));
-#endif
- writeStream << checksum;
-
- // The header indicates the message length that follows
- QByteArray header;
- QDataStream headerStream(&header, QIODevice::WriteOnly);
-
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
- headerStream.setVersion(QDataStream::Qt_5_6);
-#endif
- headerStream << static_cast ( initMsg.length() );
-
- socket->write( header );
- socket->write( initMsg );
- bool result = socket->waitForBytesWritten( static_cast(msecs - time.elapsed()) );
- socket->flush();
- return result;
-}
-
-quint16 SingleApplicationPrivate::blockChecksum() const
-{
-#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
- quint16 checksum = qChecksum(QByteArray(static_cast(memory->constData()), offsetof(InstancesInfo, checksum)));
-#else
- quint16 checksum = qChecksum(static_cast(memory->constData()), offsetof(InstancesInfo, checksum));
-#endif
- return checksum;
-}
-
-qint64 SingleApplicationPrivate::primaryPid() const
-{
- qint64 pid;
-
- memory->lock();
- auto *inst = static_cast( memory->data() );
- pid = inst->primaryPid;
- memory->unlock();
-
- return pid;
-}
-
-QString SingleApplicationPrivate::primaryUser() const
-{
- QByteArray username;
-
- memory->lock();
- auto *inst = static_cast( memory->data() );
- username = inst->primaryUser;
- memory->unlock();
-
- return QString::fromUtf8( username );
-}
-
-/**
- * @brief Executed when a connection has been made to the LocalServer
- */
-void SingleApplicationPrivate::slotConnectionEstablished()
-{
- QLocalSocket *nextConnSocket = server->nextPendingConnection();
- connectionMap.insert(nextConnSocket, ConnectionInfo());
-
- QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
- [nextConnSocket, this](){
- auto &info = connectionMap[nextConnSocket];
- Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId );
- }
- );
-
- QObject::connect(nextConnSocket, &QLocalSocket::disconnected, nextConnSocket, &QLocalSocket::deleteLater);
-
- QObject::connect(nextConnSocket, &QLocalSocket::destroyed,
- [nextConnSocket, this](){
- connectionMap.remove(nextConnSocket);
- }
- );
-
- QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
- [nextConnSocket, this](){
- auto &info = connectionMap[nextConnSocket];
- switch(info.stage){
- case StageHeader:
- readInitMessageHeader(nextConnSocket);
- break;
- case StageBody:
- readInitMessageBody(nextConnSocket);
- break;
- case StageConnected:
- Q_EMIT this->slotDataAvailable( nextConnSocket, info.instanceId );
- break;
- default:
- break;
- };
- }
- );
-}
-
-void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
-{
- if (!connectionMap.contains( sock )){
- return;
- }
-
- if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ){
- return;
- }
-
- QDataStream headerStream( sock );
-
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
- headerStream.setVersion( QDataStream::Qt_5_6 );
-#endif
-
- // Read the header to know the message length
- quint64 msgLen = 0;
- headerStream >> msgLen;
- ConnectionInfo &info = connectionMap[sock];
- info.stage = StageBody;
- info.msgLen = msgLen;
-
- if ( sock->bytesAvailable() >= (qint64) msgLen ){
- readInitMessageBody( sock );
- }
-}
-
-void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
-{
- Q_Q(SingleApplication);
-
- if (!connectionMap.contains( sock )){
- return;
- }
-
- ConnectionInfo &info = connectionMap[sock];
- if( sock->bytesAvailable() < ( qint64 )info.msgLen ){
- return;
- }
-
- // Read the message body
- QByteArray msgBytes = sock->read(info.msgLen);
- QDataStream readStream(msgBytes);
-
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
- readStream.setVersion( QDataStream::Qt_5_6 );
-#endif
-
- // server name
- QByteArray latin1Name;
- readStream >> latin1Name;
-
- // connection type
- ConnectionType connectionType = InvalidConnection;
- quint8 connTypeVal = InvalidConnection;
- readStream >> connTypeVal;
- connectionType = static_cast ( connTypeVal );
-
- // instance id
- quint32 instanceId = 0;
- readStream >> instanceId;
-
- // checksum
- quint16 msgChecksum = 0;
- readStream >> msgChecksum;
-
-#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
- const quint16 actualChecksum = qChecksum(QByteArray(msgBytes, static_cast(msgBytes.length() - sizeof(quint16))));
-#else
- const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast(msgBytes.length() - sizeof(quint16)));
-#endif
-
- bool isValid = readStream.status() == QDataStream::Ok &&
- QLatin1String(latin1Name) == blockServerName &&
- msgChecksum == actualChecksum;
-
- if( !isValid ){
- sock->close();
- return;
- }
-
- info.instanceId = instanceId;
- info.stage = StageConnected;
-
- if( connectionType == NewInstance ||
- ( connectionType == SecondaryInstance &&
- options & SingleApplication::Mode::SecondaryNotification ) )
- {
- Q_EMIT q->instanceStarted();
- }
-
- if (sock->bytesAvailable() > 0){
- Q_EMIT this->slotDataAvailable( sock, instanceId );
- }
-}
-
-void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
-{
- Q_Q(SingleApplication);
- Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() );
-}
-
-void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
-{
- if( closedSocket->bytesAvailable() > 0 )
- Q_EMIT slotDataAvailable( closedSocket, instanceId );
-}
-
-void SingleApplicationPrivate::randomSleep()
-{
-#if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 )
- QThread::msleep( QRandomGenerator::global()->bounded( 8u, 18u ));
-#else
- qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits::max() );
- QThread::msleep( 8 + static_cast ( static_cast ( qrand() ) / RAND_MAX * 10 ));
-#endif
-}
-
-void SingleApplicationPrivate::addAppData(const QString &data)
-{
- appDataList.push_back(data);
-}
-
-QStringList SingleApplicationPrivate::appData() const
-{
- return appDataList;
-}
diff --git a/client/3rd/SingleApplication/singleapplication_p.h b/client/3rd/SingleApplication/singleapplication_p.h
deleted file mode 100644
index c49a46dd..00000000
--- a/client/3rd/SingleApplication/singleapplication_p.h
+++ /dev/null
@@ -1,104 +0,0 @@
-// The MIT License (MIT)
-//
-// Copyright (c) Itay Grudev 2015 - 2020
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-//
-// W A R N I N G !!!
-// -----------------
-//
-// This file is not part of the SingleApplication API. It is used purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or may even be removed.
-//
-
-#ifndef SINGLEAPPLICATION_P_H
-#define SINGLEAPPLICATION_P_H
-
-#include
-#include
-#include
-#include "singleapplication.h"
-
-struct InstancesInfo {
- bool primary;
- quint32 secondary;
- qint64 primaryPid;
- char primaryUser[128];
- quint16 checksum; // Must be the last field
-};
-
-struct ConnectionInfo {
- qint64 msgLen = 0;
- quint32 instanceId = 0;
- quint8 stage = 0;
-};
-
-class SingleApplicationPrivate : public QObject {
-Q_OBJECT
-public:
- enum ConnectionType : quint8 {
- InvalidConnection = 0,
- NewInstance = 1,
- SecondaryInstance = 2,
- Reconnect = 3
- };
- enum ConnectionStage : quint8 {
- StageHeader = 0,
- StageBody = 1,
- StageConnected = 2,
- };
- Q_DECLARE_PUBLIC(SingleApplication)
-
- SingleApplicationPrivate( SingleApplication *q_ptr );
- ~SingleApplicationPrivate() override;
-
- static QString getUsername();
- void genBlockServerName();
- void initializeMemoryBlock() const;
- void startPrimary();
- void startSecondary();
- bool connectToPrimary( int msecs, ConnectionType connectionType );
- quint16 blockChecksum() const;
- qint64 primaryPid() const;
- QString primaryUser() const;
- void readInitMessageHeader(QLocalSocket *socket);
- void readInitMessageBody(QLocalSocket *socket);
- static void randomSleep();
- void addAppData(const QString &data);
- QStringList appData() const;
-
- SingleApplication *q_ptr;
- QSharedMemory *memory;
- QLocalSocket *socket;
- QLocalServer *server;
- quint32 instanceNumber;
- QString blockServerName;
- SingleApplication::Options options;
- QMap connectionMap;
- QStringList appDataList;
-
-public Q_SLOTS:
- void slotConnectionEstablished();
- void slotDataAvailable( QLocalSocket*, quint32 );
- void slotClientConnectionClosed( QLocalSocket*, quint32 );
-};
-
-#endif // SINGLEAPPLICATION_P_H
diff --git a/client/3rd/amneziawg-apple b/client/3rd/amneziawg-apple
index 0829e99e..76e7db55 160000
--- a/client/3rd/amneziawg-apple
+++ b/client/3rd/amneziawg-apple
@@ -1 +1 @@
-Subproject commit 0829e99ea9f4508fd1d4742546b62145d17587bb
+Subproject commit 76e7db556a6d7e2582f9481df91db188a46c009c
diff --git a/client/3rd/qtkeychain b/client/3rd/qtkeychain
index 74776e2a..7460df6a 160000
--- a/client/3rd/qtkeychain
+++ b/client/3rd/qtkeychain
@@ -1 +1 @@
-Subproject commit 74776e2a3e2d98d19943e0968901c5b5e04cc1bd
+Subproject commit 7460df6a978669290de5b56c2d98b199b61c3f88
diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt
index 1fc28b82..72adaf25 100644
--- a/client/CMakeLists.txt
+++ b/client/CMakeLists.txt
@@ -24,6 +24,13 @@ execute_process(
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
+add_definitions(-DPROD_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}")
+add_definitions(-DPROD_S3_ENDPOINT="$ENV{PROD_S3_ENDPOINT}")
+
+add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
+add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
+add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}")
+
if(IOS)
set(PACKAGES ${PACKAGES} Multimedia)
endif()
@@ -34,7 +41,7 @@ endif()
find_package(Qt6 REQUIRED COMPONENTS ${PACKAGES})
-set(LIBS ${LIBS}
+set(LIBS ${LIBS}
Qt6::Core Qt6::Gui
Qt6::Network Qt6::Xml Qt6::RemoteObjects
Qt6::Quick Qt6::Svg Qt6::QuickControls2
@@ -55,6 +62,7 @@ qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
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_process_interface.rep)
+ qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_tun2socks.rep)
endif()
qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc)
@@ -88,11 +96,6 @@ configure_file(${CMAKE_CURRENT_LIST_DIR}/translations/translations.qrc.in ${CMAK
qt6_add_resources(QRC ${I18NQRC} ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)
# -- i18n end
-if(IOS)
- execute_process(COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/ios/scripts/openvpn.sh args
- WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
-endif()
-
set(IS_CI ${CI})
if(IS_CI)
message("Detected CI env")
@@ -102,169 +105,29 @@ if(IS_CI)
endif()
endif()
-
include(${CMAKE_CURRENT_LIST_DIR}/cmake/3rdparty.cmake)
+include(${CMAKE_CURRENT_LIST_DIR}/cmake/sources.cmake)
include_directories(
${CMAKE_CURRENT_LIST_DIR}/../ipc
+ ${CMAKE_CURRENT_LIST_DIR}/../common/logger
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
-configure_file(${CMAKE_CURRENT_LIST_DIR}/../version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
-
-set(HEADERS ${HEADERS}
- ${CMAKE_CURRENT_LIST_DIR}/migrations.h
- ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc.h
- ${CMAKE_CURRENT_LIST_DIR}/amnezia_application.h
- ${CMAKE_CURRENT_LIST_DIR}/containers/containers_defs.h
- ${CMAKE_CURRENT_LIST_DIR}/core/defs.h
- ${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.h
- ${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.h
- ${CMAKE_CURRENT_LIST_DIR}/core/server_defs.h
- ${CMAKE_CURRENT_LIST_DIR}/core/controllers/apiController.h
- ${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.h
- ${CMAKE_CURRENT_LIST_DIR}/core/controllers/vpnConfigurationController.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/qml_register_protocols.h
- ${CMAKE_CURRENT_LIST_DIR}/ui/pages.h
- ${CMAKE_CURRENT_LIST_DIR}/ui/property_helper.h
- ${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.h
- ${CMAKE_CURRENT_BINARY_DIR}/version.h
- ${CMAKE_CURRENT_LIST_DIR}/core/sshclient.h
- ${CMAKE_CURRENT_LIST_DIR}/core/networkUtilities.h
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/serialization.h
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/transfer.h
-)
-
-# Mozilla headres
-set(HEADERS ${HEADERS}
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/models/server.h
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/ipaddress.h
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/leakdetector.h
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/controllerimpl.h
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/localsocketcontroller.h
-)
-
include_directories(mozilla)
include_directories(mozilla/shared)
include_directories(mozilla/models)
-if(NOT IOS)
- set(HEADERS ${HEADERS}
- ${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.h
- )
-endif()
-
-if(NOT ANDROID)
- set(HEADERS ${HEADERS}
- ${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h
- )
-endif()
-
-set(SOURCES ${SOURCES}
- ${CMAKE_CURRENT_LIST_DIR}/migrations.cpp
- ${CMAKE_CURRENT_LIST_DIR}/amnezia_application.cpp
- ${CMAKE_CURRENT_LIST_DIR}/containers/containers_defs.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/server_defs.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/controllers/apiController.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/controllers/vpnConfigurationController.cpp
- ${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.cpp
- ${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.cpp
- ${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/sshclient.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/networkUtilities.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/outbound.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/inbound.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/ss.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/ssd.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/vless.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/trojan.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess_new.cpp
-)
-
-# Mozilla sources
-set(SOURCES ${SOURCES}
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/models/server.cpp
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/ipaddress.cpp
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/leakdetector.cpp
- ${CMAKE_CURRENT_LIST_DIR}/mozilla/localsocketcontroller.cpp
-)
+configure_file(${CMAKE_CURRENT_LIST_DIR}/../version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_definitions(${PROJECT} PRIVATE "MZ_DEBUG")
endif()
-if(NOT IOS)
- set(SOURCES ${SOURCES}
- ${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.cpp
- )
-endif()
-
-if(NOT ANDROID)
- set(SOURCES ${SOURCES}
- ${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.cpp
- )
-endif()
-
-file(GLOB COMMON_FILES_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/*.h)
-file(GLOB COMMON_FILES_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/*.cpp)
-
-file(GLOB_RECURSE PAGE_LOGIC_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/pages_logic/*.h)
-file(GLOB_RECURSE PAGE_LOGIC_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/pages_logic/*.cpp)
-
-file(GLOB CONFIGURATORS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/configurators/*.h)
-file(GLOB CONFIGURATORS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/configurators/*.cpp)
-
-file(GLOB UI_MODELS_H CONFIGURE_DEPENDS
- ${CMAKE_CURRENT_LIST_DIR}/ui/models/*.h
- ${CMAKE_CURRENT_LIST_DIR}/ui/models/protocols/*.h
- ${CMAKE_CURRENT_LIST_DIR}/ui/models/services/*.h
-)
-file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS
- ${CMAKE_CURRENT_LIST_DIR}/ui/models/*.cpp
- ${CMAKE_CURRENT_LIST_DIR}/ui/models/protocols/*.cpp
- ${CMAKE_CURRENT_LIST_DIR}/ui/models/services/*.cpp
-)
-
-file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.h)
-file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.cpp)
-
-set(HEADERS ${HEADERS}
- ${COMMON_FILES_H}
- ${PAGE_LOGIC_H}
- ${CONFIGURATORS_H}
- ${UI_MODELS_H}
- ${UI_CONTROLLERS_H}
-)
-set(SOURCES ${SOURCES}
- ${COMMON_FILES_CPP}
- ${PAGE_LOGIC_CPP}
- ${CONFIGURATORS_CPP}
- ${UI_MODELS_CPP}
- ${UI_CONTROLLERS_CPP}
-)
-
if(WIN32)
configure_file(
- ${CMAKE_CURRENT_LIST_DIR}/platforms/windows/amneziavpn.rc.in
- ${CMAKE_CURRENT_BINARY_DIR}/amneziavpn.rc
- )
-
- set(HEADERS ${HEADERS}
- ${CMAKE_CURRENT_LIST_DIR}/protocols/ikev2_vpn_protocol_windows.h
- )
-
- set(SOURCES ${SOURCES}
- ${CMAKE_CURRENT_LIST_DIR}/protocols/ikev2_vpn_protocol_windows.cpp
- )
-
- set(RESOURCES ${RESOURCES}
+ ${CMAKE_CURRENT_LIST_DIR}/platforms/windows/amneziavpn.rc.in
${CMAKE_CURRENT_BINARY_DIR}/amneziavpn.rc
)
@@ -311,30 +174,6 @@ endif()
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
message("Client desktop build")
add_compile_definitions(AMNEZIA_DESKTOP)
-
- set(HEADERS ${HEADERS}
- ${CMAKE_CURRENT_LIST_DIR}/core/ipcclient.h
- ${CMAKE_CURRENT_LIST_DIR}/core/privileged_process.h
- ${CMAKE_CURRENT_LIST_DIR}/ui/systemtray_notificationhandler.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnprotocol.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/xrayprotocol.h
- ${CMAKE_CURRENT_LIST_DIR}/protocols/awgprotocol.h
- )
-
- set(SOURCES ${SOURCES}
- ${CMAKE_CURRENT_LIST_DIR}/core/ipcclient.cpp
- ${CMAKE_CURRENT_LIST_DIR}/core/privileged_process.cpp
- ${CMAKE_CURRENT_LIST_DIR}/ui/systemtray_notificationhandler.cpp
- ${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnprotocol.cpp
- ${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.cpp
- ${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.cpp
- ${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.cpp
- ${CMAKE_CURRENT_LIST_DIR}/protocols/xrayprotocol.cpp
- ${CMAKE_CURRENT_LIST_DIR}/protocols/awgprotocol.cpp
- )
endif()
if(ANDROID)
diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp
index eb1eab45..f32d525a 100644
--- a/client/amnezia_application.cpp
+++ b/client/amnezia_application.cpp
@@ -2,39 +2,27 @@
#include
#include
+#include
+#include
#include
+#include
#include
#include
#include
#include
#include
#include
-#include
#include "logger.h"
+#include "ui/controllers/pageController.h"
#include "ui/models/installedAppsModel.h"
#include "version.h"
#include "platforms/ios/QRCodeReaderBase.h"
-#if defined(Q_OS_ANDROID)
- #include "core/installedAppsImageProvider.h"
- #include "platforms/android/android_controller.h"
-#endif
#include "protocols/qml_register_protocols.h"
-#if defined(Q_OS_IOS)
- #include "platforms/ios/ios_controller.h"
- #include
-#endif
-
-#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
-#else
-AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecondary, SingleApplication::Options options, int timeout,
- const QString &userData)
- : SingleApplication(argc, argv, allowSecondary, options, timeout, userData)
-#endif
{
setQuitOnLastWindowClosed(false);
@@ -88,103 +76,30 @@ void AmneziaApplication::init()
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
m_vpnConnectionThread.start();
- initModels();
- loadTranslator();
- initControllers();
-
-#ifdef Q_OS_ANDROID
- if (!AndroidController::initLogging()) {
- qFatal("Android logging initialization failed");
- }
- AndroidController::instance()->setSaveLogs(m_settings->isSaveLogs());
- connect(m_settings.get(), &Settings::saveLogsChanged, AndroidController::instance(), &AndroidController::setSaveLogs);
-
- AndroidController::instance()->setScreenshotsEnabled(m_settings->isScreenshotsEnabled());
- connect(m_settings.get(), &Settings::screenshotsEnabledChanged, AndroidController::instance(), &AndroidController::setScreenshotsEnabled);
-
- connect(m_settings.get(), &Settings::serverRemoved, AndroidController::instance(), &AndroidController::resetLastServer);
-
- connect(m_settings.get(), &Settings::settingsCleared, []() { AndroidController::instance()->resetLastServer(-1); });
-
- connect(AndroidController::instance(), &AndroidController::initConnectionState, this, [this](Vpn::ConnectionState state) {
- m_connectionController->onConnectionStateChanged(state);
- if (m_vpnConnection)
- m_vpnConnection->restoreConnection();
- });
- if (!AndroidController::instance()->initialize()) {
- qFatal("Android controller initialization failed");
- }
-
- connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, [this](QString data) {
- m_pageController->replaceStartPage();
- m_importController->extractConfigFromData(data);
- m_pageController->goToPageViewConfig();
- });
-
- m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider);
-#endif
-
-#ifdef Q_OS_IOS
- IosController::Instance()->initialize();
- connect(IosController::Instance(), &IosController::importConfigFromOutside, [this](QString data) {
- m_pageController->replaceStartPage();
- m_importController->extractConfigFromData(data);
- m_pageController->goToPageViewConfig();
- });
-
- connect(IosController::Instance(), &IosController::importBackupFromOutside, [this](QString filePath) {
- m_pageController->replaceStartPage();
- m_pageController->goToPageSettingsBackup();
- m_settingsController->importBackupFromOutside(filePath);
- });
-
- QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); });
-
- connect(m_settings.get(), &Settings::screenshotsEnabledChanged, [](bool enabled) { AmneziaVPN::toggleScreenshots(enabled); });
-#endif
-
-#ifndef Q_OS_ANDROID
- m_notificationHandler.reset(NotificationHandler::create(nullptr));
-
- connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
- &NotificationHandler::setConnectionState);
-
- connect(m_notificationHandler.get(), &NotificationHandler::raiseRequested, m_pageController.get(), &PageController::raiseMainWindow);
- connect(m_notificationHandler.get(), &NotificationHandler::connectRequested, m_connectionController.get(),
- static_cast(&ConnectionController::openConnection));
- connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
- &ConnectionController::closeConnection);
- connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
-#endif
+ m_coreController.reset(new CoreController(m_vpnConnection, m_settings, m_engine));
+ m_engine->addImportPath("qrc:/ui/qml/Modules/");
m_engine->load(url);
- m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
+ m_coreController->setQmlRoot();
+
+ bool enabled = m_settings->isSaveLogs();
#ifndef Q_OS_ANDROID
- if (m_settings->isSaveLogs()) {
- if (!Logger::init()) {
+ if (enabled) {
+ if (!Logger::init(false)) {
qWarning() << "Initialization of debug subsystem failed";
}
}
#endif
+ Logger::setServiceLogsEnabled(enabled);
-#ifdef Q_OS_WIN
+#ifdef Q_OS_WIN //TODO
if (m_parser.isSet("a"))
- m_pageController->showOnStartup();
+ m_coreController->pageController()->showOnStartup();
else
- emit m_pageController->raiseMainWindow();
+ emit m_coreController->pageController()->raiseMainWindow();
#else
- m_pageController->showOnStartup();
-#endif
-
- // TODO - fix
-#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
- if (isPrimary()) {
- QObject::connect(this, &SingleApplication::instanceStarted, m_pageController.get(), [this]() {
- qDebug() << "Secondary instance started, showing this window instead";
- emit m_pageController->raiseMainWindow();
- });
- }
+ m_coreController->pageController()->showOnStartup();
#endif
// Android TextArea clipboard workaround
@@ -241,33 +156,6 @@ void AmneziaApplication::loadFonts()
QFontDatabase::addApplicationFont(":/fonts/pt-root-ui_vf.ttf");
}
-void AmneziaApplication::loadTranslator()
-{
- auto locale = m_settings->getAppLanguage();
- m_translator.reset(new QTranslator());
- updateTranslator(locale);
-}
-
-void AmneziaApplication::updateTranslator(const QLocale &locale)
-{
- if (!m_translator->isEmpty()) {
- QCoreApplication::removeTranslator(m_translator.get());
- }
-
- QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm";
- if (m_translator->load(strFileName)) {
- if (QCoreApplication::installTranslator(m_translator.get())) {
- m_settings->setAppLanguage(locale);
- }
- } else {
- m_settings->setAppLanguage(QLocale::English);
- }
-
- m_engine->retranslate();
-
- emit translationsUpdated();
-}
-
bool AmneziaApplication::parseCommands()
{
m_parser.setApplicationDescription(APPLICATION_NAME);
@@ -291,128 +179,36 @@ bool AmneziaApplication::parseCommands()
return true;
}
+#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
+void AmneziaApplication::startLocalServer()
+{
+ const QString serverName("AmneziaVPNInstance");
+ QLocalServer::removeServer(serverName);
+
+ QLocalServer *server = new QLocalServer(this);
+ server->listen(serverName);
+
+ QObject::connect(server, &QLocalServer::newConnection, this, [server, this]() {
+ if (server) {
+ QLocalSocket *clientConnection = server->nextPendingConnection();
+ clientConnection->deleteLater();
+ }
+ emit m_coreController->pageController()->raiseMainWindow(); //TODO
+ });
+}
+#endif
+
QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
{
return m_engine;
}
-void AmneziaApplication::initModels()
+QNetworkAccessManager *AmneziaApplication::networkManager()
{
- m_containersModel.reset(new ContainersModel(this));
- m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
-
- m_defaultServerContainersModel.reset(new ContainersModel(this));
- m_engine->rootContext()->setContextProperty("DefaultServerContainersModel", m_defaultServerContainersModel.get());
-
- m_serversModel.reset(new ServersModel(m_settings, this));
- m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
- connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(), &ContainersModel::updateModel);
- connect(m_serversModel.get(), &ServersModel::defaultServerContainersUpdated, m_defaultServerContainersModel.get(),
- &ContainersModel::updateModel);
- m_serversModel->resetModel();
-
- m_languageModel.reset(new LanguageModel(m_settings, this));
- m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
- connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &AmneziaApplication::updateTranslator);
- connect(this, &AmneziaApplication::translationsUpdated, m_languageModel.get(), &LanguageModel::translationsUpdated);
-
- m_sitesModel.reset(new SitesModel(m_settings, this));
- m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
-
- m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
- m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
-
- m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
- m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
-
- m_openVpnConfigModel.reset(new OpenVpnConfigModel(this));
- m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get());
-
- m_shadowSocksConfigModel.reset(new ShadowSocksConfigModel(this));
- m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get());
-
- m_cloakConfigModel.reset(new CloakConfigModel(this));
- m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get());
-
- m_wireGuardConfigModel.reset(new WireGuardConfigModel(this));
- m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get());
-
- m_awgConfigModel.reset(new AwgConfigModel(this));
- m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get());
-
- m_xrayConfigModel.reset(new XrayConfigModel(this));
- m_engine->rootContext()->setContextProperty("XrayConfigModel", m_xrayConfigModel.get());
-
-#ifdef Q_OS_WINDOWS
- m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this));
- m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get());
-#endif
-
- m_sftpConfigModel.reset(new SftpConfigModel(this));
- m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
-
- m_socks5ConfigModel.reset(new Socks5ProxyConfigModel(this));
- m_engine->rootContext()->setContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel.get());
-
- m_clientManagementModel.reset(new ClientManagementModel(m_settings, this));
- m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get());
- connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked, m_serversModel.get(),
- &ServersModel::clearCachedProfile);
+ return m_nam;
}
-void AmneziaApplication::initControllers()
+QClipboard *AmneziaApplication::getClipboard()
{
- m_connectionController.reset(
- new ConnectionController(m_serversModel, m_containersModel, m_clientManagementModel, m_vpnConnection, m_settings));
- m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
-
- connect(m_connectionController.get(), qOverload(&ConnectionController::connectionErrorOccurred), this, [this](const QString &errorMessage) {
- emit m_pageController->showErrorMessage(errorMessage);
- emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
- });
-
- connect(m_connectionController.get(), qOverload(&ConnectionController::connectionErrorOccurred), this, [this](ErrorCode errorCode) {
- emit m_pageController->showErrorMessage(errorCode);
- emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
- });
-
- connect(m_connectionController.get(), &ConnectionController::connectButtonClicked, m_connectionController.get(),
- &ConnectionController::toggleConnection, Qt::QueuedConnection);
-
- connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(), &ConnectionController::onTranslationsUpdated);
-
- m_pageController.reset(new PageController(m_serversModel, m_settings));
- m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
-
- m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel, m_settings));
- m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
- connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(),
- &PageController::showPassphraseRequestDrawer);
- connect(m_pageController.get(), &PageController::passphraseRequestDrawerClosed, m_installController.get(),
- &InstallController::setEncryptedPassphrase);
- connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
- &ConnectionController::onCurrentContainerUpdated);
-
- m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
- m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
-
- m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel, m_settings));
- m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
-
- m_settingsController.reset(
- new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_appSplitTunnelingModel, m_settings));
- m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
- if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
- QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
- }
- connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled, m_serversModel.get(), &ServersModel::toggleAmneziaDns);
-
- m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
- m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
-
- m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
- m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
-
- m_systemController.reset(new SystemController(m_settings));
- m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
+ return this->clipboard();
}
diff --git a/client/amnezia_application.h b/client/amnezia_application.h
index b15d55d7..ea5f6f52 100644
--- a/client/amnezia_application.h
+++ b/client/amnezia_application.h
@@ -11,127 +11,53 @@
#else
#include
#endif
+#include
+#include "core/controllers/coreController.h"
#include "settings.h"
#include "vpnconnection.h"
-#include "ui/controllers/connectionController.h"
-#include "ui/controllers/exportController.h"
-#include "ui/controllers/importController.h"
-#include "ui/controllers/installController.h"
-#include "ui/controllers/pageController.h"
-#include "ui/controllers/settingsController.h"
-#include "ui/controllers/sitesController.h"
-#include "ui/controllers/systemController.h"
-#include "ui/controllers/appSplitTunnelingController.h"
-#include "ui/models/containers_model.h"
-#include "ui/models/languageModel.h"
-#include "ui/models/protocols/cloakConfigModel.h"
-#ifndef Q_OS_ANDROID
- #include "ui/notificationhandler.h"
-#endif
-#ifdef Q_OS_WINDOWS
- #include "ui/models/protocols/ikev2ConfigModel.h"
-#endif
-#include "ui/models/protocols/awgConfigModel.h"
-#include "ui/models/protocols/openvpnConfigModel.h"
-#include "ui/models/protocols/shadowsocksConfigModel.h"
-#include "ui/models/protocols/wireguardConfigModel.h"
-#include "ui/models/protocols/xrayConfigModel.h"
-#include "ui/models/protocols_model.h"
-#include "ui/models/servers_model.h"
-#include "ui/models/services/sftpConfigModel.h"
-#include "ui/models/services/socks5ProxyConfigModel.h"
-#include "ui/models/sites_model.h"
-#include "ui/models/clientManagementModel.h"
-#include "ui/models/appSplitTunnelingModel.h"
-
#define amnApp (static_cast(QCoreApplication::instance()))
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
#define AMNEZIA_BASE_CLASS QGuiApplication
#else
- #define AMNEZIA_BASE_CLASS SingleApplication
- #define QAPPLICATION_CLASS QApplication
- #include "singleapplication.h"
+ #define AMNEZIA_BASE_CLASS QApplication
#endif
class AmneziaApplication : public AMNEZIA_BASE_CLASS
{
Q_OBJECT
public:
-#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
AmneziaApplication(int &argc, char *argv[]);
-#else
- AmneziaApplication(int &argc, char *argv[], bool allowSecondary = false,
- SingleApplication::Options options = SingleApplication::User, int timeout = 1000,
- const QString &userData = {});
-#endif
virtual ~AmneziaApplication();
void init();
void registerTypes();
void loadFonts();
- void loadTranslator();
- void updateTranslator(const QLocale &locale);
bool parseCommands();
- QQmlApplicationEngine *qmlEngine() const;
- QNetworkAccessManager *manager() { return m_nam; }
+#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
+ void startLocalServer();
+#endif
-signals:
- void translationsUpdated();
+ QQmlApplicationEngine *qmlEngine() const;
+ QNetworkAccessManager *networkManager();
+ QClipboard *getClipboard();
private:
- void initModels();
- void initControllers();
-
QQmlApplicationEngine *m_engine {};
std::shared_ptr m_settings;
+ QScopedPointer m_coreController;
+
QSharedPointer m_containerProps;
QSharedPointer m_protocolProps;
- QSharedPointer m_translator;
QCommandLineParser m_parser;
- QSharedPointer m_containersModel;
- QSharedPointer m_defaultServerContainersModel;
- QSharedPointer m_serversModel;
- QSharedPointer m_languageModel;
- QSharedPointer m_protocolsModel;
- QSharedPointer m_sitesModel;
- QSharedPointer m_appSplitTunnelingModel;
- QSharedPointer m_clientManagementModel;
-
- QScopedPointer m_openVpnConfigModel;
- QScopedPointer m_shadowSocksConfigModel;
- QScopedPointer m_cloakConfigModel;
- QScopedPointer m_xrayConfigModel;
- QScopedPointer m_wireGuardConfigModel;
- QScopedPointer m_awgConfigModel;
-#ifdef Q_OS_WINDOWS
- QScopedPointer m_ikev2ConfigModel;
-#endif
-
- QScopedPointer m_sftpConfigModel;
- QScopedPointer m_socks5ConfigModel;
-
QSharedPointer m_vpnConnection;
QThread m_vpnConnectionThread;
-#ifndef Q_OS_ANDROID
- QScopedPointer m_notificationHandler;
-#endif
-
- QScopedPointer m_connectionController;
- QScopedPointer m_pageController;
- QScopedPointer m_installController;
- QScopedPointer m_importController;
- QScopedPointer m_exportController;
- QScopedPointer m_settingsController;
- QScopedPointer m_sitesController;
- QScopedPointer m_systemController;
- QScopedPointer m_appSplitTunnelingController;
QNetworkAccessManager *m_nam;
};
diff --git a/client/android/AndroidManifest.xml b/client/android/AndroidManifest.xml
index f1d2682b..b28f754b 100644
--- a/client/android/AndroidManifest.xml
+++ b/client/android/AndroidManifest.xml
@@ -3,7 +3,6 @@
@@ -11,6 +10,9 @@
+
+
+
@@ -18,7 +20,7 @@
-
+
@@ -31,9 +33,11 @@
android:label="-- %%INSERT_APP_NAME%% --"
android:icon="@mipmap/icon"
android:roundIcon="@mipmap/icon_round"
+ android:banner="@mipmap/ic_banner"
android:theme="@style/NoActionBar"
android:fullBackupContent="@xml/backup_content"
android:dataExtractionRules="@xml/data_extraction_rules"
+ android:hasFragileUserData="false"
tools:targetApi="s">
+
@@ -62,9 +67,6 @@
android:name="android.app.lib_name"
android:value="-- %%INSERT_APP_LIB_NAME%% --" />
-
+
+
+
+
Unit): AwgConfig = Builder().apply(block).build()
- }
-}
diff --git a/client/android/build.gradle b/client/android/build.gradle
index d768000e..5044727b 100644
--- a/client/android/build.gradle
+++ b/client/android/build.gradle
@@ -3,3 +3,6 @@
// android.bundle.enableUncompressedNativeLibs is deprecated
// disable adding gradle property android.bundle.enableUncompressedNativeLibs by androiddeployqt
useLegacyPackaging
+
+// package name for androiddeployqt
+namespace = "org.amnezia.vpn"
diff --git a/client/android/build.gradle.kts b/client/android/build.gradle.kts
index 7c4cec5a..3c742621 100644
--- a/client/android/build.gradle.kts
+++ b/client/android/build.gradle.kts
@@ -115,9 +115,11 @@ dependencies {
implementation(project(":xray"))
implementation(libs.androidx.core)
implementation(libs.androidx.activity)
+ implementation(libs.androidx.fragment)
implementation(libs.kotlinx.coroutines)
implementation(libs.kotlinx.serialization.protobuf)
implementation(libs.bundles.androidx.camera)
implementation(libs.google.mlkit)
implementation(libs.androidx.datastore)
+ implementation(libs.androidx.biometric)
}
diff --git a/client/android/cloak/src/main/kotlin/Cloak.kt b/client/android/cloak/src/main/kotlin/Cloak.kt
index 5a549130..d408fb19 100644
--- a/client/android/cloak/src/main/kotlin/Cloak.kt
+++ b/client/android/cloak/src/main/kotlin/Cloak.kt
@@ -3,40 +3,16 @@ package org.amnezia.vpn.protocol.cloak
import android.util.Base64
import net.openvpn.ovpn3.ClientAPI_Config
import org.amnezia.vpn.protocol.openvpn.OpenVpn
+import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary
import org.json.JSONObject
-/**
- * Config Example:
- * {
- * "protocol": "cloak",
- * "description": "Server 1",
- * "dns1": "1.1.1.1",
- * "dns2": "1.0.0.1",
- * "hostName": "100.100.100.0",
- * "splitTunnelSites": [
- * ],
- * "splitTunnelType": 0,
- * "openvpn_config_data": {
- * "config": "openVpnConfig"
- * }
- * "cloak_config_data": {
- * "BrowserSig": "chrome",
- * "EncryptionMethod": "aes-gcm",
- * "NumConn": 1,
- * "ProxyMethod": "openvpn",
- * "PublicKey": "PublicKey=",
- * "RemoteHost": "100.100.100.0",
- * "RemotePort": "443",
- * "ServerName": "servername",
- * "StreamTimeout": 300,
- * "Transport": "direct",
- * "UID": "UID="
- * }
- * }
- */
-
class Cloak : OpenVpn() {
+ override fun internalInit() {
+ super.internalInit()
+ if (!isInitialized) loadSharedLibrary(context, "ck-ovpn-plugin")
+ }
+
override fun parseConfig(config: JSONObject): ClientAPI_Config {
val openVpnConfig = ClientAPI_Config()
diff --git a/client/android/gradle.properties b/client/android/gradle.properties
index 5a27838c..ce651e1c 100644
--- a/client/android/gradle.properties
+++ b/client/android/gradle.properties
@@ -33,7 +33,7 @@ android.library.defaults.buildfeatures.androidresources=false
# For development copy and set local values for these parameters in local.properties
#androidCompileSdkVersion=android-34
#androidBuildToolsVersion=34.0.0
-#qtMinSdkVersion=24
+#qtMinSdkVersion=26
#qtTargetSdkVersion=34
#androidNdkVersion=26.1.10909125
#qtTargetAbiList=x86_64
diff --git a/client/android/gradle/libs.versions.toml b/client/android/gradle/libs.versions.toml
index a5466422..c6fa1907 100644
--- a/client/android/gradle/libs.versions.toml
+++ b/client/android/gradle/libs.versions.toml
@@ -1,24 +1,28 @@
[versions]
-agp = "8.2.0"
-kotlin = "1.9.20"
-androidx-core = "1.12.0"
-androidx-activity = "1.8.1"
-androidx-annotation = "1.7.0"
-androidx-camera = "1.3.0"
+agp = "8.5.2"
+kotlin = "1.9.24"
+androidx-core = "1.13.1"
+androidx-activity = "1.9.1"
+androidx-annotation = "1.8.2"
+androidx-biometric = "1.2.0-alpha05"
+androidx-camera = "1.3.4"
+androidx-fragment = "1.8.2"
androidx-security-crypto = "1.1.0-alpha06"
-androidx-datastore = "1.1.0-beta01"
-kotlinx-coroutines = "1.7.3"
+androidx-datastore = "1.1.1"
+kotlinx-coroutines = "1.8.1"
kotlinx-serialization = "1.6.3"
-google-mlkit = "17.2.0"
+google-mlkit = "17.3.0"
[libraries]
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-activity = { module = "androidx.activity:activity-ktx", version.ref = "androidx-activity" }
androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" }
+androidx-biometric = { module = "androidx.biometric:biometric-ktx", version.ref = "androidx-biometric" }
androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "androidx-camera" }
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "androidx-camera" }
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "androidx-camera" }
androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "androidx-camera" }
+androidx-fragment = { module = "androidx.fragment:fragment-ktx", version.ref = "androidx-fragment" }
androidx-security-crypto = { module = "androidx.security:security-crypto-ktx", version.ref = "androidx-security-crypto" }
androidx-datastore = { module = "androidx.datastore:datastore-preferences", version.ref = "androidx-datastore" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
diff --git a/client/android/gradle/wrapper/gradle-wrapper.jar b/client/android/gradle/wrapper/gradle-wrapper.jar
index d64cd491..a4b76b95 100644
Binary files a/client/android/gradle/wrapper/gradle-wrapper.jar and b/client/android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/client/android/gradle/wrapper/gradle-wrapper.properties b/client/android/gradle/wrapper/gradle-wrapper.properties
index 1af9e093..e1adfb49 100644
--- a/client/android/gradle/wrapper/gradle-wrapper.properties
+++ b/client/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,7 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
-networkTimeout=10000
-validateDistributionUrl=true
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/client/android/gradlew b/client/android/gradlew
index 1aa94a42..f5feea6d 100755
--- a/client/android/gradlew
+++ b/client/android/gradlew
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+# SPDX-License-Identifier: Apache-2.0
+#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -84,7 +86,8 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
-APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
+' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
diff --git a/client/android/gradlew.bat b/client/android/gradlew.bat
index 93e3f59f..9d21a218 100644
--- a/client/android/gradlew.bat
+++ b/client/android/gradlew.bat
@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
@@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
diff --git a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt
index abe46245..22fe35cd 100644
--- a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt
+++ b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt
@@ -11,28 +11,12 @@ import org.amnezia.vpn.protocol.Protocol
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
import org.amnezia.vpn.protocol.Statistics
import org.amnezia.vpn.protocol.VpnStartException
+import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary
import org.amnezia.vpn.util.net.InetNetwork
import org.amnezia.vpn.util.net.getLocalNetworks
import org.amnezia.vpn.util.net.parseInetAddress
import org.json.JSONObject
-/**
- * Config Example:
- * {
- * "protocol": "openvpn",
- * "description": "Server 1",
- * "dns1": "1.1.1.1",
- * "dns2": "1.0.0.1",
- * "hostName": "100.100.100.0",
- * "splitTunnelSites": [
- * ],
- * "splitTunnelType": 0,
- * "openvpn_config_data": {
- * "config": "openVpnConfig"
- * }
- * }
- */
-
open class OpenVpn : Protocol() {
private var openVpnClient: OpenVpnClient? = null
@@ -51,14 +35,17 @@ open class OpenVpn : Protocol() {
}
override fun internalInit() {
- if (!isInitialized) loadSharedLibrary(context, "ovpn3")
+ if (!isInitialized) {
+ loadSharedLibrary(context, "ovpn3")
+ loadSharedLibrary(context, "ovpnutil")
+ }
if (this::scope.isInitialized) {
scope.cancel()
}
scope = CoroutineScope(Dispatchers.IO)
}
- override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
+ override suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
val configBuilder = OpenVpnConfig.Builder()
openVpnClient = OpenVpnClient(
diff --git a/client/android/protocolApi/src/main/kotlin/Exceptions.kt b/client/android/protocolApi/src/main/kotlin/Exceptions.kt
index 739a327c..b80648b0 100644
--- a/client/android/protocolApi/src/main/kotlin/Exceptions.kt
+++ b/client/android/protocolApi/src/main/kotlin/Exceptions.kt
@@ -2,7 +2,6 @@ package org.amnezia.vpn.protocol
sealed class ProtocolException(message: String? = null, cause: Throwable? = null) : Exception(message, cause)
-class LoadLibraryException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
class BadConfigException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
class VpnStartException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
diff --git a/client/android/protocolApi/src/main/kotlin/Protocol.kt b/client/android/protocolApi/src/main/kotlin/Protocol.kt
index a475a2fc..6e682aa4 100644
--- a/client/android/protocolApi/src/main/kotlin/Protocol.kt
+++ b/client/android/protocolApi/src/main/kotlin/Protocol.kt
@@ -1,6 +1,5 @@
package org.amnezia.vpn.protocol
-import android.annotation.SuppressLint
import android.content.Context
import android.net.IpPrefix
import android.net.VpnService
@@ -8,9 +7,6 @@ import android.net.VpnService.Builder
import android.os.Build
import android.system.OsConstants
import androidx.annotation.RequiresApi
-import java.io.File
-import java.io.FileOutputStream
-import java.util.zip.ZipFile
import kotlinx.coroutines.flow.MutableStateFlow
import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.net.InetNetwork
@@ -42,7 +38,7 @@ abstract class Protocol {
protected abstract fun internalInit()
- abstract fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean)
+ abstract suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean)
abstract fun stopVpn()
@@ -158,60 +154,6 @@ abstract class Protocol {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
vpnBuilder.setMetered(false)
}
-
- companion object {
- private fun extractLibrary(context: Context, libraryName: String, destination: File): Boolean {
- Log.d(TAG, "Extracting library: $libraryName")
- val apks = hashSetOf()
- context.applicationInfo.run {
- sourceDir?.let { apks += it }
- splitSourceDirs?.let { apks += it }
- }
- for (abi in Build.SUPPORTED_ABIS) {
- for (apk in apks) {
- ZipFile(File(apk), ZipFile.OPEN_READ).use { zipFile ->
- val mappedName = System.mapLibraryName(libraryName)
- val libraryZipPath = listOf("lib", abi, mappedName).joinToString(File.separator)
- val zipEntry = zipFile.getEntry(libraryZipPath)
- zipEntry?.let {
- Log.d(TAG, "Extracting apk:/$libraryZipPath to ${destination.absolutePath}")
- FileOutputStream(destination).use { outStream ->
- zipFile.getInputStream(zipEntry).use { inStream ->
- inStream.copyTo(outStream, 32 * 1024)
- outStream.fd.sync()
- }
- }
- }
- return true
- }
- }
- }
- return false
- }
-
- @SuppressLint("UnsafeDynamicallyLoadedCode")
- fun loadSharedLibrary(context: Context, libraryName: String) {
- Log.d(TAG, "Loading library: $libraryName")
- try {
- System.loadLibrary(libraryName)
- return
- } catch (_: UnsatisfiedLinkError) {
- Log.d(TAG, "Failed to load library, try to extract it from apk")
- }
- var tempFile: File? = null
- try {
- tempFile = File.createTempFile("lib", ".so", context.codeCacheDir)
- if (extractLibrary(context, libraryName, tempFile)) {
- System.load(tempFile.absolutePath)
- return
- }
- } catch (e: Exception) {
- throw LoadLibraryException("Failed to load library apk: $libraryName", e)
- } finally {
- tempFile?.delete()
- }
- }
- }
}
private fun VpnService.Builder.addAddress(addr: InetNetwork) = addAddress(addr.address, addr.mask)
diff --git a/client/android/qt/build.gradle.kts b/client/android/qt/build.gradle.kts
index 139adf4f..6b1d3fd1 100644
--- a/client/android/qt/build.gradle.kts
+++ b/client/android/qt/build.gradle.kts
@@ -21,5 +21,5 @@ android {
}
dependencies {
- implementation(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar"))))
+ api(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar"))))
}
diff --git a/client/android/res/mipmap-hdpi/ic_banner.png b/client/android/res/mipmap-hdpi/ic_banner.png
new file mode 100644
index 00000000..a444777f
Binary files /dev/null and b/client/android/res/mipmap-hdpi/ic_banner.png differ
diff --git a/client/android/res/mipmap-mdpi/ic_banner.png b/client/android/res/mipmap-mdpi/ic_banner.png
new file mode 100644
index 00000000..b9ad1db7
Binary files /dev/null and b/client/android/res/mipmap-mdpi/ic_banner.png differ
diff --git a/client/android/res/mipmap-xhdpi/ic_banner.png b/client/android/res/mipmap-xhdpi/ic_banner.png
new file mode 100644
index 00000000..f5029b23
Binary files /dev/null and b/client/android/res/mipmap-xhdpi/ic_banner.png differ
diff --git a/client/android/res/values-ru/strings.xml b/client/android/res/values-ru/strings.xml
index 8bdabfc0..5e35bba5 100644
--- a/client/android/res/values-ru/strings.xml
+++ b/client/android/res/values-ru/strings.xml
@@ -23,4 +23,6 @@
Настройки уведомлений
Для показа уведомлений необходимо включить уведомления в системных настройках
Открыть настройки уведомлений
+
+ Пожалуйста, установите приложение для просмотра файлов
\ No newline at end of file
diff --git a/client/android/res/values/libs.xml b/client/android/res/values/libs.xml
index fe63866f..3ccf1d80 100644
--- a/client/android/res/values/libs.xml
+++ b/client/android/res/values/libs.xml
@@ -3,7 +3,6 @@
-
diff --git a/client/android/res/values/strings.xml b/client/android/res/values/strings.xml
index 5251403b..bf8d76d1 100644
--- a/client/android/res/values/strings.xml
+++ b/client/android/res/values/strings.xml
@@ -23,4 +23,6 @@
Notification settings
To show notifications, you must enable notifications in the system settings
Open notification settings
+
+ Please install a file management utility to browse files
\ No newline at end of file
diff --git a/client/android/res/values/styles.xml b/client/android/res/values/styles.xml
index 9f4201f8..bc67beb9 100644
--- a/client/android/res/values/styles.xml
+++ b/client/android/res/values/styles.xml
@@ -1,6 +1,9 @@
+ #FF0E0E11
diff --git a/client/android/settings.gradle.kts b/client/android/settings.gradle.kts
index 5cfc8314..68426ec8 100644
--- a/client/android/settings.gradle.kts
+++ b/client/android/settings.gradle.kts
@@ -22,7 +22,7 @@ dependencyResolutionManagement {
includeBuild("./gradle/plugins")
plugins {
- id("com.android.settings") version "8.2.0"
+ id("com.android.settings") version "8.5.2"
id("settings-property-delegate")
}
diff --git a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt
index 202fe2e6..c6db5e29 100644
--- a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt
+++ b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt
@@ -1,8 +1,10 @@
package org.amnezia.vpn
import android.Manifest
+import android.annotation.SuppressLint
import android.app.AlertDialog
import android.app.NotificationManager
+import android.content.ActivityNotFoundException
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Intent
@@ -11,6 +13,7 @@ import android.content.Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.graphics.Bitmap
+import android.net.Uri
import android.net.VpnService
import android.os.Build
import android.os.Bundle
@@ -19,7 +22,13 @@ import android.os.IBinder
import android.os.Looper
import android.os.Message
import android.os.Messenger
+import android.os.ParcelFileDescriptor
+import android.os.SystemClock
+import android.provider.OpenableColumns
import android.provider.Settings
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewGroup
import android.view.WindowManager.LayoutParams
import android.webkit.MimeTypeMap
import android.widget.Toast
@@ -28,6 +37,7 @@ import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import java.io.IOException
import kotlin.LazyThreadSafetyMode.NONE
+import kotlin.coroutines.CoroutineContext
import kotlin.text.RegexOption.IGNORE_CASE
import AppListProvider
import kotlinx.coroutines.CompletableDeferred
@@ -42,6 +52,7 @@ import kotlinx.coroutines.withContext
import org.amnezia.vpn.protocol.getStatistics
import org.amnezia.vpn.protocol.getStatus
import org.amnezia.vpn.qt.QtAndroidController
+import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary
import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.Prefs
import org.json.JSONException
@@ -68,6 +79,7 @@ class AmneziaActivity : QtActivity() {
private var isInBoundState = false
private var notificationStateReceiver: BroadcastReceiver? = null
private lateinit var vpnServiceMessenger: IpcMessenger
+ private var pfd: ParcelFileDescriptor? = null
private val actionResultHandlers = mutableMapOf()
private val permissionRequestHandlers = mutableMapOf()
@@ -156,7 +168,12 @@ class AmneziaActivity : QtActivity() {
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- Log.d(TAG, "Create Amnezia activity: $intent")
+ Log.d(TAG, "Create Amnezia activity")
+ loadLibs()
+ window.apply {
+ addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
+ statusBarColor = getColor(R.color.black)
+ }
mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
val proto = mainScope.async(Dispatchers.IO) {
VpnStateStore.getVpnState().vpnProto
@@ -174,6 +191,17 @@ class AmneziaActivity : QtActivity() {
runBlocking { vpnProto = proto.await() }
}
+ private fun loadLibs() {
+ listOf(
+ "rsapss",
+ "crypto_3",
+ "ssl_3",
+ "ssh"
+ ).forEach {
+ loadSharedLibrary(this.applicationContext, it)
+ }
+ }
+
private fun registerBroadcastReceivers() {
notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
registerBroadcastReceiver(
@@ -182,7 +210,7 @@ class AmneziaActivity : QtActivity() {
NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED
)
) {
- Log.d(
+ Log.v(
TAG, "Notification state changed: ${it?.action}, blocked = " +
"${it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)}"
)
@@ -196,7 +224,7 @@ class AmneziaActivity : QtActivity() {
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
- Log.d(TAG, "onNewIntent: $intent")
+ Log.v(TAG, "onNewIntent: $intent")
intent?.let(::processIntent)
}
@@ -230,7 +258,10 @@ class AmneziaActivity : QtActivity() {
override fun onStop() {
Log.d(TAG, "Stop Amnezia activity")
doUnbindService()
- QtAndroidController.onServiceDisconnected()
+ mainScope.launch {
+ qtInitialized.await()
+ QtAndroidController.onServiceDisconnected()
+ }
super.onStop()
}
@@ -382,9 +413,10 @@ class AmneziaActivity : QtActivity() {
@MainThread
private fun startVpn(vpnConfig: String) {
getVpnProto(vpnConfig)?.let { proto ->
- Log.d(TAG, "Proto from config: $proto, current proto: $vpnProto")
+ Log.v(TAG, "Proto from config: $proto, current proto: $vpnProto")
if (isServiceConnected) {
- if (proto == vpnProto) {
+ if (proto.serviceClass == vpnProto?.serviceClass) {
+ vpnProto = proto
connectToVpn(vpnConfig)
return
}
@@ -491,21 +523,25 @@ class AmneziaActivity : QtActivity() {
type = "text/*"
putExtra(Intent.EXTRA_TITLE, fileName)
}.also {
- startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler(
- onSuccess = {
- it?.data?.let { uri ->
- Log.d(TAG, "Save file to $uri")
- try {
- contentResolver.openOutputStream(uri)?.use { os ->
- os.bufferedWriter().use { it.write(data) }
+ try {
+ startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler(
+ onSuccess = {
+ it?.data?.let { uri ->
+ Log.v(TAG, "Save file to $uri")
+ try {
+ contentResolver.openOutputStream(uri)?.use { os ->
+ os.bufferedWriter().use { it.write(data) }
+ }
+ } catch (e: IOException) {
+ Log.e(TAG, "Failed to save file $uri: $e")
+ // todo: send error to Qt
}
- } catch (e: IOException) {
- Log.e(TAG, "Failed to save file $uri: $e")
- // todo: send error to Qt
}
}
- }
- ))
+ ))
+ } catch (_: ActivityNotFoundException) {
+ Toast.makeText(this@AmneziaActivity, "Unsupported", Toast.LENGTH_LONG).show()
+ }
}
}
}
@@ -514,49 +550,122 @@ class AmneziaActivity : QtActivity() {
fun openFile(filter: String?) {
Log.v(TAG, "Open file with filter: $filter")
mainScope.launch {
- val mimeTypes = if (!filter.isNullOrEmpty()) {
- val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
- val mime = MimeTypeMap.getSingleton()
- extensionRegex.findAll(filter).map {
- it.groups[1]?.value?.let { mime.getMimeTypeFromExtension(it) } ?: "*/*"
- }.toSet()
- } else emptySet()
+ val intent = if (!isOnTv()) {
+ val mimeTypes = if (!filter.isNullOrEmpty()) {
+ val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
+ val mime = MimeTypeMap.getSingleton()
+ extensionRegex.findAll(filter).map {
+ it.groups[1]?.value?.let { mime.getMimeTypeFromExtension(it) } ?: "*/*"
+ }.toSet()
+ } else emptySet()
- Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
- addCategory(Intent.CATEGORY_OPENABLE)
- Log.v(TAG, "File mimyType filter: $mimeTypes")
- if ("*/*" in mimeTypes) {
- type = "*/*"
- } else {
- when (mimeTypes.size) {
- 1 -> type = mimeTypes.first()
+ Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ Log.v(TAG, "File mimyType filter: $mimeTypes")
+ if ("*/*" in mimeTypes) {
+ type = "*/*"
+ } else {
+ when (mimeTypes.size) {
+ 1 -> type = mimeTypes.first()
- in 2..Int.MAX_VALUE -> {
- type = "*/*"
- putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
+ in 2..Int.MAX_VALUE -> {
+ type = "*/*"
+ putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
+ }
+
+ else -> type = "*/*"
}
-
- else -> type = "*/*"
}
}
- }.also {
- startActivityForResult(it, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
- onSuccess = {
- val uri = it?.data?.toString() ?: ""
- Log.d(TAG, "Open file: $uri")
+ } else {
+ Intent(this@AmneziaActivity, TvFilePicker::class.java)
+ }
+
+ try {
+ startActivityForResult(intent, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
+ onAny = {
+ if (isOnTv() && it?.hasExtra("activityNotFound") == true) {
+ showNoFileBrowserAlertDialog()
+ }
+ val uri = it?.data?.apply {
+ grantUriPermission(packageName, this, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ }?.toString() ?: ""
+ Log.v(TAG, "Open file: $uri")
mainScope.launch {
qtInitialized.await()
QtAndroidController.onFileOpened(uri)
}
}
))
+ } catch (_: ActivityNotFoundException) {
+ showNoFileBrowserAlertDialog()
+ mainScope.launch {
+ qtInitialized.await()
+ QtAndroidController.onFileOpened("")
+ }
+ }
+ }
+ }
+
+ private fun showNoFileBrowserAlertDialog() {
+ AlertDialog.Builder(this)
+ .setMessage(R.string.tvNoFileBrowser)
+ .setCancelable(false)
+ .setPositiveButton(android.R.string.ok) { _, _ ->
+ try {
+ startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://webstoreredirect")))
+ } catch (_: Throwable) {}
+ }
+ .show()
+ }
+
+ @Suppress("unused")
+ fun getFd(fileName: String): Int {
+ Log.v(TAG, "Get fd for $fileName")
+ return blockingCall {
+ try {
+ pfd = contentResolver.openFileDescriptor(Uri.parse(fileName), "r")
+ pfd?.fd ?: -1
+ } catch (e: Exception) {
+ Log.e(TAG, "Failed to get fd: $e")
+ -1
}
}
}
@Suppress("unused")
+ fun closeFd() {
+ Log.v(TAG, "Close fd")
+ mainScope.launch {
+ pfd?.close()
+ pfd = null
+ }
+ }
+
+ @Suppress("unused")
+ fun getFileName(uri: String): String {
+ Log.v(TAG, "Get file name for uri: $uri")
+ return blockingCall {
+ try {
+ contentResolver.query(Uri.parse(uri), arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null)?.use { cursor ->
+ if (cursor.moveToFirst() && !cursor.isNull(0)) {
+ return@blockingCall cursor.getString(0) ?: ""
+ }
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Failed to get file name: $e")
+ }
+ ""
+ }
+ }
+
+ @Suppress("unused")
+ @SuppressLint("UnsupportedChromeOsCameraSystemFeature")
fun isCameraPresent(): Boolean = applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
+ @Suppress("unused")
+ fun isOnTv(): Boolean = applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+
@Suppress("unused")
fun startQrCodeReader() {
Log.v(TAG, "Start camera")
@@ -601,6 +710,14 @@ class AmneziaActivity : QtActivity() {
}
}
+ @Suppress("unused")
+ fun setNavigationBarColor(color: Int) {
+ Log.v(TAG, "Change navigation bar color: ${"#%08X".format(color)}")
+ mainScope.launch {
+ window.navigationBarColor = color
+ }
+ }
+
@Suppress("unused")
fun minimizeApp() {
Log.v(TAG, "Minimize application")
@@ -675,9 +792,132 @@ class AmneziaActivity : QtActivity() {
.show()
}
+ @Suppress("unused")
+ fun requestAuthentication() {
+ Log.v(TAG, "Request authentication")
+ mainScope.launch {
+ qtInitialized.await()
+ Intent(this@AmneziaActivity, AuthActivity::class.java).also {
+ startActivity(it)
+ }
+ }
+ }
+
+ // method to workaround Qt's problem with calling the keyboard on TVs
+ @Suppress("unused")
+ fun sendTouch(x: Float, y: Float) {
+ Log.v(TAG, "Send touch: $x, $y")
+ blockingCall {
+ findQtWindow(window.decorView)?.let {
+ Log.v(TAG, "Send touch to $it")
+ it.dispatchTouchEvent(createEvent(x, y, SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN))
+ it.dispatchTouchEvent(createEvent(x, y, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP))
+ }
+ }
+ }
+
+ private fun findQtWindow(view: View): View? {
+ Log.v(TAG, "findQtWindow: process $view")
+ if (view::class.simpleName == "QtWindow") return view
+ else if (view is ViewGroup) {
+ for (i in 0 until view.childCount) {
+ val result = findQtWindow(view.getChildAt(i))
+ if (result != null) return result
+ }
+ return null
+ } else return null
+ }
+
+ private fun createEvent(x: Float, y: Float, eventTime: Long, action: Int): MotionEvent =
+ MotionEvent.obtain(
+ eventTime,
+ eventTime,
+ action,
+ 1,
+ arrayOf(MotionEvent.PointerProperties().apply {
+ id = 0
+ toolType = MotionEvent.TOOL_TYPE_FINGER
+ }),
+ arrayOf(MotionEvent.PointerCoords().apply {
+ this.x = x
+ this.y = y
+ pressure = 1f
+ size = 1f
+ }),
+ 0, 0, 1.0f, 1.0f, 0, 0, 0,0
+ )
+
+ // workaround for a bug in Qt that causes the mouse click event not to be handled
+ // also disable right-click, as it causes the application to crash
+ private var lastButtonState = 0
+ private fun MotionEvent.fixCopy(): MotionEvent = MotionEvent.obtain(
+ downTime,
+ eventTime,
+ action,
+ pointerCount,
+ (0 until pointerCount).map { i ->
+ MotionEvent.PointerProperties().apply {
+ getPointerProperties(i, this)
+ }
+ }.toTypedArray(),
+ (0 until pointerCount).map { i ->
+ MotionEvent.PointerCoords().apply {
+ getPointerCoords(i, this)
+ }
+ }.toTypedArray(),
+ metaState,
+ MotionEvent.BUTTON_PRIMARY,
+ xPrecision,
+ yPrecision,
+ deviceId,
+ edgeFlags,
+ source,
+ flags
+ )
+
+ private fun handleMouseEvent(ev: MotionEvent, superDispatch: (MotionEvent?) -> Boolean): Boolean {
+ when (ev.action) {
+ MotionEvent.ACTION_DOWN -> {
+ lastButtonState = ev.buttonState
+ if (ev.buttonState == MotionEvent.BUTTON_SECONDARY) return true
+ }
+
+ MotionEvent.ACTION_UP -> {
+ when (lastButtonState) {
+ MotionEvent.BUTTON_SECONDARY -> return true
+ MotionEvent.BUTTON_PRIMARY -> {
+ val modEvent = ev.fixCopy()
+ return superDispatch(modEvent).apply { modEvent.recycle() }
+ }
+ }
+ }
+ }
+ return superDispatch(ev)
+ }
+
+ override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
+ Log.v(TAG, "dispatchTouch: $ev")
+ if (ev != null && ev.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
+ return handleMouseEvent(ev) { super.dispatchTouchEvent(it) }
+ }
+ return super.dispatchTouchEvent(ev)
+ }
+
+ override fun dispatchTrackballEvent(ev: MotionEvent?): Boolean {
+ ev?.let { return handleMouseEvent(ev) { super.dispatchTrackballEvent(it) }}
+ return super.dispatchTrackballEvent(ev)
+ }
+
/**
* Utils methods
*/
+ private fun blockingCall(
+ context: CoroutineContext = Dispatchers.Main.immediate,
+ block: suspend () -> T
+ ) = runBlocking {
+ mainScope.async(context) { block() }.await()
+ }
+
companion object {
private fun actionCodeToString(actionCode: Int): String =
when (actionCode) {
diff --git a/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt b/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt
index b30f1503..8d108bc3 100644
--- a/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt
+++ b/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt
@@ -22,6 +22,7 @@ import androidx.annotation.MainThread
import androidx.core.app.ServiceCompat
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
+import java.net.UnknownHostException
import java.util.concurrent.ConcurrentHashMap
import kotlin.LazyThreadSafetyMode.NONE
import kotlinx.coroutines.CoroutineExceptionHandler
@@ -31,6 +32,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.cancel
+import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.drop
@@ -39,7 +41,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import org.amnezia.vpn.protocol.BadConfigException
-import org.amnezia.vpn.protocol.LoadLibraryException
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
import org.amnezia.vpn.protocol.ProtocolState.CONNECTING
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
@@ -49,6 +50,7 @@ import org.amnezia.vpn.protocol.ProtocolState.UNKNOWN
import org.amnezia.vpn.protocol.VpnException
import org.amnezia.vpn.protocol.VpnStartException
import org.amnezia.vpn.protocol.putStatus
+import org.amnezia.vpn.util.LoadLibraryException
import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.Prefs
import org.amnezia.vpn.util.net.NetworkState
@@ -111,6 +113,10 @@ open class AmneziaVpnService : VpnService() {
get() = clientMessengers.any { it.value.name == ACTIVITY_MESSENGER_NAME }
private val connectionExceptionHandler = CoroutineExceptionHandler { _, e ->
+ connectionJob?.cancel()
+ connectionJob = null
+ disconnectionJob?.cancel()
+ disconnectionJob = null
protocolState.value = DISCONNECTED
when (e) {
is IllegalArgumentException,
@@ -122,6 +128,8 @@ open class AmneziaVpnService : VpnService() {
is LoadLibraryException -> onError("${e.message}. Caused: ${e.cause?.message}")
+ is UnknownHostException -> onError("Unknown host")
+
else -> throw e
}
}
@@ -292,7 +300,7 @@ open class AmneziaVpnService : VpnService() {
arrayOf(ACTION_CONNECT, ACTION_DISCONNECT), ContextCompat.RECEIVER_NOT_EXPORTED
) {
it?.action?.let { action ->
- Log.d(TAG, "Broadcast request received: $action")
+ Log.v(TAG, "Broadcast request received: $action")
when (action) {
ACTION_CONNECT -> connect()
ACTION_DISCONNECT -> disconnect()
@@ -309,7 +317,7 @@ open class AmneziaVpnService : VpnService() {
)
) {
val state = it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)
- Log.d(TAG, "Notification state changed: ${it?.action}, blocked = $state")
+ Log.v(TAG, "Notification state changed: ${it?.action}, blocked = $state")
if (state == false) {
enableNotification()
} else {
@@ -442,7 +450,7 @@ open class AmneziaVpnService : VpnService() {
serviceNotification.isNotificationEnabled() &&
getSystemService()?.isInteractive != false
) {
- Log.d(TAG, "Launch traffic stats update")
+ Log.v(TAG, "Launch traffic stats update")
trafficStats.reset()
startTrafficStatsUpdateJob()
}
@@ -531,7 +539,7 @@ open class AmneziaVpnService : VpnService() {
protocolState.value = DISCONNECTING
disconnectionJob = connectionScope.launch {
- connectionJob?.join()
+ connectionJob?.cancelAndJoin()
connectionJob = null
vpnProto?.protocol?.stopVpn()
diff --git a/client/android/src/org/amnezia/vpn/AuthActivity.kt b/client/android/src/org/amnezia/vpn/AuthActivity.kt
new file mode 100644
index 00000000..46401548
--- /dev/null
+++ b/client/android/src/org/amnezia/vpn/AuthActivity.kt
@@ -0,0 +1,97 @@
+package org.amnezia.vpn
+
+import android.os.Build
+import android.os.Bundle
+import androidx.biometric.BiometricManager
+import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
+import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
+import androidx.biometric.BiometricPrompt
+import androidx.biometric.BiometricPrompt.AuthenticationResult
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.FragmentActivity
+import org.amnezia.vpn.qt.QtAndroidController
+import org.amnezia.vpn.util.Log
+
+private const val TAG = "AuthActivity"
+
+private const val AUTHENTICATORS = BIOMETRIC_STRONG or DEVICE_CREDENTIAL
+
+class AuthActivity : FragmentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val biometricManager = BiometricManager.from(applicationContext)
+ when (biometricManager.canAuthenticate(AUTHENTICATORS)) {
+ BiometricManager.BIOMETRIC_SUCCESS -> {
+ showBiometricPrompt(biometricManager)
+ return
+ }
+
+ BiometricManager.BIOMETRIC_STATUS_UNKNOWN -> {
+ Log.w(TAG, "Unknown biometric status")
+ showBiometricPrompt(biometricManager)
+ return
+ }
+
+ BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED -> {
+ Log.e(TAG, "The specified options are incompatible with the current Android " +
+ "version ${Build.VERSION.SDK_INT}")
+ }
+
+ BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> {
+ Log.w(TAG, "The hardware is unavailable")
+ }
+
+ BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
+ Log.w(TAG, "No biometric or device credential is enrolled")
+ }
+
+ BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
+ Log.w(TAG, "There is no suitable hardware")
+ }
+
+ BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> {
+ Log.w(TAG, "A security vulnerability has been discovered with one or " +
+ "more hardware sensors")
+ }
+ }
+ QtAndroidController.onAuthResult(true)
+ finish()
+ }
+
+ private fun showBiometricPrompt(biometricManager: BiometricManager) {
+ val executor = ContextCompat.getMainExecutor(applicationContext)
+ val biometricPrompt = BiometricPrompt(this, executor,
+ object : BiometricPrompt.AuthenticationCallback() {
+ override fun onAuthenticationSucceeded(result: AuthenticationResult) {
+ super.onAuthenticationSucceeded(result)
+ Log.v(TAG, "Authentication succeeded")
+ QtAndroidController.onAuthResult(true)
+ finish()
+ }
+
+ override fun onAuthenticationFailed() {
+ super.onAuthenticationFailed()
+ Log.w(TAG, "Authentication failed")
+ }
+
+ override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
+ super.onAuthenticationError(errorCode, errString)
+ Log.e(TAG, "Authentication error $errorCode: $errString")
+ QtAndroidController.onAuthResult(false)
+ finish()
+ }
+ })
+
+
+
+ val promptInfo = BiometricPrompt.PromptInfo.Builder()
+ .setAllowedAuthenticators(AUTHENTICATORS)
+ .setTitle("AmneziaVPN")
+ .setSubtitle(biometricManager.getStrings(AUTHENTICATORS)?.promptMessage)
+ .build()
+
+ biometricPrompt.authenticate(promptInfo)
+ }
+}
diff --git a/client/android/src/org/amnezia/vpn/AuthHelper.java b/client/android/src/org/amnezia/vpn/AuthHelper.java
deleted file mode 100644
index 940d03c2..00000000
--- a/client/android/src/org/amnezia/vpn/AuthHelper.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.amnezia.vpn;
-
-import android.content.Context;
-import android.app.KeyguardManager;
-import android.content.Intent;
-import org.qtproject.qt.android.bindings.QtActivity;
-
-
-import static android.content.Context.KEYGUARD_SERVICE;
-
-public class AuthHelper extends QtActivity {
-
- static final String TAG = "AuthHelper";
-
- public static Intent getAuthIntent(Context context) {
- KeyguardManager mKeyguardManager = (KeyguardManager)context.getSystemService(KEYGUARD_SERVICE);
- if (mKeyguardManager.isDeviceSecure()) {
- return mKeyguardManager.createConfirmDeviceCredentialIntent(null, null);
- } else {
- return null;
- }
- }
-
-}
diff --git a/client/android/src/org/amnezia/vpn/ImportConfigActivity.kt b/client/android/src/org/amnezia/vpn/ImportConfigActivity.kt
index cae7ab75..49823a36 100644
--- a/client/android/src/org/amnezia/vpn/ImportConfigActivity.kt
+++ b/client/android/src/org/amnezia/vpn/ImportConfigActivity.kt
@@ -29,20 +29,20 @@ class ImportConfigActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- Log.d(TAG, "Create Import Config Activity: $intent")
+ Log.v(TAG, "Create Import Config Activity: $intent")
intent?.let(::readConfig)
}
- override fun onNewIntent(intent: Intent?) {
+ override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
- Log.d(TAG, "onNewIntent: $intent")
- intent?.let(::readConfig)
+ Log.v(TAG, "onNewIntent: $intent")
+ intent.let(::readConfig)
}
private fun readConfig(intent: Intent) {
when (intent.action) {
ACTION_SEND -> {
- Log.d(TAG, "Process SEND action, type: ${intent.type}")
+ Log.v(TAG, "Process SEND action, type: ${intent.type}")
when (intent.type) {
"application/octet-stream" -> {
intent.getUriCompat()?.let { uri ->
@@ -60,7 +60,7 @@ class ImportConfigActivity : ComponentActivity() {
}
ACTION_VIEW -> {
- Log.d(TAG, "Process VIEW action, scheme: ${intent.scheme}")
+ Log.v(TAG, "Process VIEW action, scheme: ${intent.scheme}")
when (intent.scheme) {
"file", "content" -> {
intent.data?.let { uri ->
diff --git a/client/android/src/org/amnezia/vpn/ServiceNotification.kt b/client/android/src/org/amnezia/vpn/ServiceNotification.kt
index f4707731..47e8f263 100644
--- a/client/android/src/org/amnezia/vpn/ServiceNotification.kt
+++ b/client/android/src/org/amnezia/vpn/ServiceNotification.kt
@@ -62,7 +62,7 @@ class ServiceNotification(private val context: Context) {
fun buildNotification(serverName: String?, protocol: String?, state: ProtocolState): Notification {
val speedString = if (state == CONNECTED) zeroSpeed else null
- Log.d(TAG, "Build notification: $serverName, $state")
+ Log.v(TAG, "Build notification: $serverName, $state")
return notificationBuilder
.setSmallIcon(R.drawable.ic_amnezia_round)
@@ -88,17 +88,15 @@ class ServiceNotification(private val context: Context) {
fun isNotificationEnabled(): Boolean {
if (!context.isNotificationPermissionGranted()) return false
if (!notificationManager.areNotificationsEnabled()) return false
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- return notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID)
- ?.let { it.importance != NotificationManager.IMPORTANCE_NONE } ?: true
- }
- return true
+ return notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID)?.let {
+ it.importance != NotificationManager.IMPORTANCE_NONE
+ } ?: true
}
@SuppressLint("MissingPermission")
fun updateNotification(serverName: String?, protocol: String?, state: ProtocolState) {
if (context.isNotificationPermissionGranted()) {
- Log.d(TAG, "Update notification: $serverName, $state")
+ Log.v(TAG, "Update notification: $serverName, $state")
notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, protocol, state))
}
}
diff --git a/client/android/src/org/amnezia/vpn/TvFilePicker.kt b/client/android/src/org/amnezia/vpn/TvFilePicker.kt
new file mode 100644
index 00000000..1ac275eb
--- /dev/null
+++ b/client/android/src/org/amnezia/vpn/TvFilePicker.kt
@@ -0,0 +1,45 @@
+package org.amnezia.vpn
+
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.result.contract.ActivityResultContracts
+import org.amnezia.vpn.util.Log
+
+private const val TAG = "TvFilePicker"
+
+class TvFilePicker : ComponentActivity() {
+
+ private val fileChooseResultLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) {
+ setResult(RESULT_OK, Intent().apply { data = it })
+ finish()
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ Log.v(TAG, "onCreate")
+ getFile()
+ }
+
+ override fun onNewIntent(intent: Intent) {
+ super.onNewIntent(intent)
+ Log.v(TAG, "onNewIntent")
+ getFile()
+ }
+
+ private fun getFile() {
+ try {
+ Log.v(TAG, "getFile")
+ fileChooseResultLauncher.launch("*/*")
+ } catch (_: ActivityNotFoundException) {
+ Log.w(TAG, "Activity not found")
+ setResult(RESULT_CANCELED, Intent().apply { putExtra("activityNotFound", true) })
+ finish()
+ } catch (e: Exception) {
+ Log.e(TAG, "Failed to get file: $e")
+ setResult(RESULT_CANCELED)
+ finish()
+ }
+ }
+}
diff --git a/client/android/src/org/amnezia/vpn/VpnProto.kt b/client/android/src/org/amnezia/vpn/VpnProto.kt
index 508ce226..658e06ac 100644
--- a/client/android/src/org/amnezia/vpn/VpnProto.kt
+++ b/client/android/src/org/amnezia/vpn/VpnProto.kt
@@ -49,7 +49,15 @@ enum class VpnProto(
"org.amnezia.vpn:amneziaXrayService",
XrayService::class.java
) {
- override fun createProtocol(): Protocol = Xray()
+ override fun createProtocol(): Protocol = Xray.instance
+ },
+
+ SSXRAY(
+ "SSXRay",
+ "org.amnezia.vpn:amneziaXrayService",
+ XrayService::class.java
+ ) {
+ override fun createProtocol(): Protocol = Xray.instance
};
private var _protocol: Protocol? = null
diff --git a/client/android/src/org/amnezia/vpn/qt/QtAndroidController.kt b/client/android/src/org/amnezia/vpn/qt/QtAndroidController.kt
index e382b080..4af138a2 100644
--- a/client/android/src/org/amnezia/vpn/qt/QtAndroidController.kt
+++ b/client/android/src/org/amnezia/vpn/qt/QtAndroidController.kt
@@ -25,5 +25,7 @@ object QtAndroidController {
external fun onConfigImported(data: String)
+ external fun onAuthResult(result: Boolean)
+
external fun decodeQrCode(data: String): Boolean
}
\ No newline at end of file
diff --git a/client/android/utils/src/main/kotlin/JsonExt.kt b/client/android/utils/src/main/kotlin/JsonExt.kt
new file mode 100644
index 00000000..45c5bacd
--- /dev/null
+++ b/client/android/utils/src/main/kotlin/JsonExt.kt
@@ -0,0 +1,9 @@
+package org.amnezia.vpn.util
+
+import org.json.JSONArray
+import org.json.JSONObject
+
+inline fun JSONArray.asSequence(): Sequence =
+ (0..()
+ context.applicationInfo.run {
+ sourceDir?.let { apks += it }
+ splitSourceDirs?.let { apks += it }
+ }
+ for (abi in Build.SUPPORTED_ABIS) {
+ for (apk in apks) {
+ ZipFile(File(apk), ZipFile.OPEN_READ).use { zipFile ->
+ val mappedName = System.mapLibraryName(libraryName)
+ val libraryZipPath = listOf("lib", abi, mappedName).joinToString(File.separator)
+ val zipEntry = zipFile.getEntry(libraryZipPath)
+ zipEntry?.let {
+ Log.d(TAG, "Extracting apk:/$libraryZipPath to ${destination.absolutePath}")
+ FileOutputStream(destination).use { outStream ->
+ zipFile.getInputStream(zipEntry).use { inStream ->
+ inStream.copyTo(outStream, 32 * 1024)
+ outStream.fd.sync()
+ }
+ }
+ }
+ return true
+ }
+ }
+ }
+ return false
+ }
+
+ @SuppressLint("UnsafeDynamicallyLoadedCode")
+ fun loadSharedLibrary(context: Context, libraryName: String) {
+ Log.d(TAG, "Loading library: $libraryName")
+ try {
+ System.loadLibrary(libraryName)
+ return
+ } catch (_: UnsatisfiedLinkError) {
+ Log.w(TAG, "Failed to load library, try to extract it from apk")
+ }
+ var tempFile: File? = null
+ try {
+ tempFile = File.createTempFile("lib", ".so", context.codeCacheDir)
+ if (extractLibrary(context, libraryName, tempFile)) {
+ System.load(tempFile.absolutePath)
+ return
+ }
+ } catch (e: Exception) {
+ throw LoadLibraryException("Failed to load library apk: $libraryName", e)
+ } finally {
+ tempFile?.delete()
+ }
+ }
+}
+
+class LoadLibraryException(message: String? = null, cause: Throwable? = null) : Exception(message, cause)
diff --git a/client/android/utils/src/main/kotlin/Log.kt b/client/android/utils/src/main/kotlin/Log.kt
index a656b9ea..da11c200 100644
--- a/client/android/utils/src/main/kotlin/Log.kt
+++ b/client/android/utils/src/main/kotlin/Log.kt
@@ -1,8 +1,6 @@
package org.amnezia.vpn.util
import android.content.Context
-import android.icu.text.DateFormat
-import android.icu.text.SimpleDateFormat
import android.os.Build
import android.os.Process
import java.io.File
@@ -12,8 +10,6 @@ import java.nio.channels.FileChannel
import java.nio.channels.FileLock
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
-import java.util.Date
-import java.util.Locale
import java.util.concurrent.locks.ReentrantLock
import org.amnezia.vpn.util.Log.Priority.D
import org.amnezia.vpn.util.Log.Priority.E
@@ -41,11 +37,7 @@ private const val LOG_MAX_FILE_SIZE = 1024 * 1024
* | | | create a report and/or terminate the process |
*/
object Log {
- private val dateTimeFormat: Any =
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)
- else object : ThreadLocal() {
- override fun initialValue(): DateFormat = SimpleDateFormat(DATE_TIME_PATTERN, Locale.US)
- }
+ private val dateTimeFormat: DateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)
private lateinit var logDir: File
private val logFile: File by lazy { File(logDir, LOG_FILE_NAME) }
@@ -143,12 +135,7 @@ object Log {
}
private fun formatLogMsg(tag: String, msg: String, priority: Priority): String {
- val date = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- LocalDateTime.now().format(dateTimeFormat as DateTimeFormatter)
- } else {
- @Suppress("UNCHECKED_CAST")
- (dateTimeFormat as ThreadLocal).get()?.format(Date())
- }
+ val date = LocalDateTime.now().format(dateTimeFormat)
return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
"$tag: $msg\n"
}
diff --git a/client/android/utils/src/main/kotlin/net/NetworkState.kt b/client/android/utils/src/main/kotlin/net/NetworkState.kt
index 3cff8c04..1cab5535 100644
--- a/client/android/utils/src/main/kotlin/net/NetworkState.kt
+++ b/client/android/utils/src/main/kotlin/net/NetworkState.kt
@@ -42,18 +42,12 @@ class NetworkState(
private val networkCallback: NetworkCallback by lazy(NONE) {
object : NetworkCallback() {
override fun onAvailable(network: Network) {
- Log.d(TAG, "onAvailable: $network")
+ Log.v(TAG, "onAvailable: $network")
}
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
- Log.d(TAG, "onCapabilitiesChanged: $network, $networkCapabilities")
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- checkNetworkState(network, networkCapabilities)
- } else {
- handler.post {
- checkNetworkState(network, networkCapabilities)
- }
- }
+ Log.v(TAG, "onCapabilitiesChanged: $network, $networkCapabilities")
+ checkNetworkState(network, networkCapabilities)
}
private fun checkNetworkState(network: Network, networkCapabilities: NetworkCapabilities) {
@@ -73,11 +67,11 @@ class NetworkState(
}
override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
- Log.d(TAG, "onBlockedStatusChanged: $network, $blocked")
+ Log.v(TAG, "onBlockedStatusChanged: $network, $blocked")
}
override fun onLost(network: Network) {
- Log.d(TAG, "onLost: $network")
+ Log.v(TAG, "onLost: $network")
}
}
}
@@ -87,21 +81,27 @@ class NetworkState(
Log.d(TAG, "Bind network listener")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
connectivityManager.registerBestMatchingNetworkCallback(networkRequest, networkCallback, handler)
- } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- try {
- connectivityManager.requestNetwork(networkRequest, networkCallback, handler)
- } catch (e: SecurityException) {
- Log.e(TAG, "Failed to bind network listener: $e")
- // Android 11 bug: https://issuetracker.google.com/issues/175055271
- if (e.message?.startsWith("Package android does not belong to") == true) {
- delay(1000)
+ } else {
+ val numberAttempts = 300
+ var attemptCount = 0
+ while(true) {
+ try {
connectivityManager.requestNetwork(networkRequest, networkCallback, handler)
- } else {
- throw e
+ break
+ } catch (e: SecurityException) {
+ Log.e(TAG, "Failed to bind network listener: $e")
+ // Android 11 bug: https://issuetracker.google.com/issues/175055271
+ if (e.message?.startsWith("Package android does not belong to") == true) {
+ if (++attemptCount > numberAttempts) {
+ throw e
+ }
+ delay(1000)
+ continue
+ } else {
+ throw e
+ }
}
}
- } else {
- connectivityManager.requestNetwork(networkRequest, networkCallback)
}
isListenerBound = true
}
diff --git a/client/android/utils/src/main/kotlin/net/NetworkUtils.kt b/client/android/utils/src/main/kotlin/net/NetworkUtils.kt
index b75748be..784aa352 100644
--- a/client/android/utils/src/main/kotlin/net/NetworkUtils.kt
+++ b/client/android/utils/src/main/kotlin/net/NetworkUtils.kt
@@ -35,7 +35,7 @@ fun getLocalNetworks(context: Context, ipv6: Boolean): List {
return emptyList()
}
-fun parseInetAddress(address: String): InetAddress = parseNumericAddressCompat(address)
+fun parseInetAddress(address: String): InetAddress = InetAddress.getByName(address)
private val parseNumericAddressCompat: (String) -> InetAddress =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
@@ -60,7 +60,7 @@ private val parseNumericAddressCompat: (String) -> InetAddress =
internal fun convertIpv6ToCanonicalForm(ipv6: String): String = ipv6
.replace("((?:(?:^|:)0+\\b){2,}):?(?!\\S*\\b\\1:0+\\b)(\\S*)".toRegex(), "::$2")
-internal val InetAddress.ip: String
+val InetAddress.ip: String
get() = if (this is Inet4Address) {
hostAddress!!
} else {
diff --git a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt
index 690510eb..80cab96d 100644
--- a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt
+++ b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt
@@ -1,60 +1,35 @@
package org.amnezia.vpn.protocol.wireguard
import android.net.VpnService.Builder
-import java.util.TreeMap
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
import org.amnezia.awg.GoBackend
import org.amnezia.vpn.protocol.Protocol
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
import org.amnezia.vpn.protocol.Statistics
import org.amnezia.vpn.protocol.VpnStartException
+import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary
import org.amnezia.vpn.util.Log
+import org.amnezia.vpn.util.asSequence
import org.amnezia.vpn.util.net.InetEndpoint
import org.amnezia.vpn.util.net.InetNetwork
import org.amnezia.vpn.util.net.parseInetAddress
+import org.amnezia.vpn.util.optStringOrNull
import org.json.JSONObject
-/**
- * Config example:
- * {
- * "protocol": "wireguard",
- * "description": "Server 1",
- * "dns1": "1.1.1.1",
- * "dns2": "1.0.0.1",
- * "hostName": "100.100.100.0",
- * "splitTunnelSites": [
- * ],
- * "splitTunnelType": 0,
- * "wireguard_config_data": {
- * "client_ip": "10.8.1.1",
- * "hostName": "100.100.100.0",
- * "port": 12345,
- * "client_pub_key": "clientPublicKeyBase64",
- * "client_priv_key": "privateKeyBase64",
- * "psk_key": "presharedKeyBase64",
- * "server_pub_key": "publicKeyBase64",
- * "config": "[Interface]
- * Address = 10.8.1.1/32
- * DNS = 1.1.1.1, 1.0.0.1
- * PrivateKey = privateKeyBase64
- *
- * [Peer]
- * PublicKey = publicKeyBase64
- * PresharedKey = presharedKeyBase64
- * AllowedIPs = 0.0.0.0/0, ::/0
- * Endpoint = 100.100.100.0:12345
- * PersistentKeepalive = 25
- * "
- * }
- * }
- */
-
private const val TAG = "Wireguard"
open class Wireguard : Protocol() {
private var tunnelHandle: Int = -1
protected open val ifName: String = "amn0"
+ private lateinit var scope: CoroutineScope
+ private var statusJob: Job? = null
override val statistics: Statistics
get() {
@@ -77,69 +52,78 @@ open class Wireguard : Protocol() {
override fun internalInit() {
if (!isInitialized) loadSharedLibrary(context, "wg-go")
+ if (this::scope.isInitialized) {
+ scope.cancel()
+ }
+ scope = CoroutineScope(Dispatchers.IO)
}
- override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
+ override suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
val wireguardConfig = parseConfig(config)
start(wireguardConfig, vpnBuilder, protect)
- state.value = CONNECTED
}
protected open fun parseConfig(config: JSONObject): WireguardConfig {
- val configDataJson = config.getJSONObject("wireguard_config_data")
- val configData = parseConfigData(configDataJson.getString("config"))
+ val configData = config.getJSONObject("wireguard_config_data")
return WireguardConfig.build {
- configWireguard(configData, configDataJson)
+ configWireguard(config, configData)
configSplitTunneling(config)
configAppSplitTunneling(config)
}
}
- protected fun WireguardConfig.Builder.configWireguard(configData: Map, configDataJson: JSONObject) {
- configData["Address"]?.split(",")?.map { address ->
+ protected fun WireguardConfig.Builder.configWireguard(config: JSONObject, configData: JSONObject) {
+ configData.getString("client_ip").split(",").map { address ->
InetNetwork.parse(address.trim())
- }?.forEach(::addAddress)
+ }.forEach(::addAddress)
- configData["DNS"]?.split(",")?.map { dns ->
- parseInetAddress(dns.trim())
- }?.forEach(::addDnsServer)
+ config.optStringOrNull("dns1")?.let { dns ->
+ addDnsServer(parseInetAddress(dns.trim()))
+ }
+
+ config.optStringOrNull("dns2")?.let { dns ->
+ addDnsServer(parseInetAddress(dns.trim()))
+ }
val defRoutes = hashSetOf(
InetNetwork("0.0.0.0", 0),
InetNetwork("::", 0)
)
val routes = hashSetOf()
- configData["AllowedIPs"]?.split(",")?.map { route ->
+ configData.getJSONArray("allowed_ips").asSequence().map { route ->
InetNetwork.parse(route.trim())
- }?.forEach(routes::add)
+ }.forEach(routes::add)
// if the allowed IPs list contains at least one non-default route, disable global split tunneling
if (routes.any { it !in defRoutes }) disableSplitTunneling()
addRoutes(routes)
- configDataJson.optString("mtu").let { mtu ->
- if (mtu.isNotEmpty()) {
- setMtu(mtu.toInt())
- } else {
- configData["MTU"]?.let { setMtu(it.toInt()) }
- }
+ configData.optStringOrNull("mtu")?.let { setMtu(it.toInt()) }
+
+ val host = configData.getString("hostName").let { parseInetAddress(it.trim()) }
+ val port = configData.getInt("port")
+ setEndpoint(InetEndpoint(host, port))
+
+ if (configData.optBoolean("isObfuscationEnabled")) {
+ setUseProtocolExtension(true)
+ configExtensionParameters(configData)
}
- configData["Endpoint"]?.let { setEndpoint(InetEndpoint.parse(it)) }
- configData["PersistentKeepalive"]?.let { setPersistentKeepalive(it.toInt()) }
- configData["PrivateKey"]?.let { setPrivateKeyHex(it.base64ToHex()) }
- configData["PublicKey"]?.let { setPublicKeyHex(it.base64ToHex()) }
- configData["PresharedKey"]?.let { setPreSharedKeyHex(it.base64ToHex()) }
+ configData.optStringOrNull("persistent_keep_alive")?.let { setPersistentKeepalive(it.toInt()) }
+ configData.getString("client_priv_key").let { setPrivateKeyHex(it.base64ToHex()) }
+ configData.getString("server_pub_key").let { setPublicKeyHex(it.base64ToHex()) }
+ configData.optStringOrNull("psk_key")?.let { setPreSharedKeyHex(it.base64ToHex()) }
}
- protected fun parseConfigData(data: String): Map {
- val parsedData = TreeMap(String.CASE_INSENSITIVE_ORDER)
- data.lineSequence()
- .filter { it.isNotEmpty() && !it.startsWith('[') }
- .forEach { line ->
- val attr = line.split("=", limit = 2)
- parsedData[attr.first().trim()] = attr.last().trim()
- }
- return parsedData
+ protected fun WireguardConfig.Builder.configExtensionParameters(configData: JSONObject) {
+ configData.optStringOrNull("Jc")?.let { setJc(it.toInt()) }
+ configData.optStringOrNull("Jmin")?.let { setJmin(it.toInt()) }
+ configData.optStringOrNull("Jmax")?.let { setJmax(it.toInt()) }
+ configData.optStringOrNull("S1")?.let { setS1(it.toInt()) }
+ configData.optStringOrNull("S2")?.let { setS2(it.toInt()) }
+ configData.optStringOrNull("H1")?.let { setH1(it.toLong()) }
+ configData.optStringOrNull("H2")?.let { setH2(it.toLong()) }
+ configData.optStringOrNull("H3")?.let { setH3(it.toLong()) }
+ configData.optStringOrNull("H4")?.let { setH4(it.toLong()) }
}
private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) {
@@ -168,6 +152,43 @@ open class Wireguard : Protocol() {
tunnelHandle = -1
throw VpnStartException("Protect VPN interface: permission not granted or revoked")
}
+ launchStatusJob()
+ }
+
+ private fun launchStatusJob() {
+ Log.d(TAG, "Launch status job")
+ statusJob = scope.launch {
+ while (true) {
+ val lastHandshake = getLastHandshake()
+ Log.v(TAG, "lastHandshake=$lastHandshake")
+ if (lastHandshake == 0L) {
+ delay(1000)
+ continue
+ }
+ if (lastHandshake == -2L || lastHandshake > 0L) state.value = CONNECTED
+ else if (lastHandshake == -1L) state.value = DISCONNECTED
+ statusJob = null
+ break
+ }
+ }
+ }
+
+ private fun getLastHandshake(): Long {
+ if (tunnelHandle == -1) {
+ Log.e(TAG, "Trying to get config of a non-existent tunnel")
+ return -1
+ }
+ val config = GoBackend.awgGetConfig(tunnelHandle)
+ if (config == null) {
+ Log.e(TAG, "Failed to get tunnel config")
+ return -2
+ }
+ val lastHandshake = config.lines().find { it.startsWith("last_handshake_time_sec=") }?.substring(24)?.toLong()
+ if (lastHandshake == null) {
+ Log.e(TAG, "Failed to get last_handshake_time_sec")
+ return -2
+ }
+ return lastHandshake
}
override fun stopVpn() {
@@ -175,6 +196,8 @@ open class Wireguard : Protocol() {
Log.w(TAG, "Tunnel already down")
return
}
+ statusJob?.cancel()
+ statusJob = null
val handleToClose = tunnelHandle
tunnelHandle = -1
GoBackend.awgTurnOff(handleToClose)
diff --git a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt
index 09269f54..7ae3d43b 100644
--- a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt
+++ b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt
@@ -1,6 +1,7 @@
package org.amnezia.vpn.protocol.wireguard
import android.util.Base64
+import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.ProtocolConfig
import org.amnezia.vpn.util.net.InetEndpoint
@@ -12,7 +13,17 @@ open class WireguardConfig protected constructor(
val persistentKeepalive: Int,
val publicKeyHex: String,
val preSharedKeyHex: String?,
- val privateKeyHex: String
+ val privateKeyHex: String,
+ val useProtocolExtension: Boolean,
+ val jc: Int?,
+ val jmin: Int?,
+ val jmax: Int?,
+ val s1: Int?,
+ val s2: Int?,
+ val h1: Long?,
+ val h2: Long?,
+ val h3: Long?,
+ val h4: Long?
) : ProtocolConfig(protocolConfigBuilder) {
protected constructor(builder: Builder) : this(
@@ -21,7 +32,17 @@ open class WireguardConfig protected constructor(
builder.persistentKeepalive,
builder.publicKeyHex,
builder.preSharedKeyHex,
- builder.privateKeyHex
+ builder.privateKeyHex,
+ builder.useProtocolExtension,
+ builder.jc,
+ builder.jmin,
+ builder.jmax,
+ builder.s1,
+ builder.s2,
+ builder.h1,
+ builder.h2,
+ builder.h3,
+ builder.h4
)
fun toWgUserspaceString(): String = with(StringBuilder()) {
@@ -33,6 +54,30 @@ open class WireguardConfig protected constructor(
open fun appendDeviceLine(sb: StringBuilder) = with(sb) {
appendLine("private_key=$privateKeyHex")
+ if (useProtocolExtension) {
+ validateProtocolExtensionParameters()
+ appendLine("jc=$jc")
+ appendLine("jmin=$jmin")
+ appendLine("jmax=$jmax")
+ appendLine("s1=$s1")
+ appendLine("s2=$s2")
+ appendLine("h1=$h1")
+ appendLine("h2=$h2")
+ appendLine("h3=$h3")
+ appendLine("h4=$h4")
+ }
+ }
+
+ private fun validateProtocolExtensionParameters() {
+ if (jc == null) throw BadConfigException("Parameter jc is undefined")
+ if (jmin == null) throw BadConfigException("Parameter jmin is undefined")
+ if (jmax == null) throw BadConfigException("Parameter jmax is undefined")
+ if (s1 == null) throw BadConfigException("Parameter s1 is undefined")
+ if (s2 == null) throw BadConfigException("Parameter s2 is undefined")
+ if (h1 == null) throw BadConfigException("Parameter h1 is undefined")
+ if (h2 == null) throw BadConfigException("Parameter h2 is undefined")
+ if (h3 == null) throw BadConfigException("Parameter h3 is undefined")
+ if (h4 == null) throw BadConfigException("Parameter h4 is undefined")
}
open fun appendPeerLine(sb: StringBuilder) = with(sb) {
@@ -65,6 +110,18 @@ open class WireguardConfig protected constructor(
override var mtu: Int = WIREGUARD_DEFAULT_MTU
+ internal var useProtocolExtension: Boolean = false
+
+ internal var jc: Int? = null
+ internal var jmin: Int? = null
+ internal var jmax: Int? = null
+ internal var s1: Int? = null
+ internal var s2: Int? = null
+ internal var h1: Long? = null
+ internal var h2: Long? = null
+ internal var h3: Long? = null
+ internal var h4: Long? = null
+
fun setEndpoint(endpoint: InetEndpoint) = apply { this.endpoint = endpoint }
fun setPersistentKeepalive(persistentKeepalive: Int) = apply { this.persistentKeepalive = persistentKeepalive }
@@ -75,6 +132,18 @@ open class WireguardConfig protected constructor(
fun setPrivateKeyHex(privateKeyHex: String) = apply { this.privateKeyHex = privateKeyHex }
+ fun setUseProtocolExtension(useProtocolExtension: Boolean) = apply { this.useProtocolExtension = useProtocolExtension }
+
+ fun setJc(jc: Int) = apply { this.jc = jc }
+ fun setJmin(jmin: Int) = apply { this.jmin = jmin }
+ fun setJmax(jmax: Int) = apply { this.jmax = jmax }
+ fun setS1(s1: Int) = apply { this.s1 = s1 }
+ fun setS2(s2: Int) = apply { this.s2 = s2 }
+ fun setH1(h1: Long) = apply { this.h1 = h1 }
+ fun setH2(h2: Long) = apply { this.h2 = h2 }
+ fun setH3(h3: Long) = apply { this.h3 = h3 }
+ fun setH4(h4: Long) = apply { this.h4 = h4 }
+
override fun build(): WireguardConfig = configBuild().run { WireguardConfig(this@Builder) }
}
diff --git a/client/android/xray/src/main/kotlin/Xray.kt b/client/android/xray/src/main/kotlin/Xray.kt
index b4d0b51f..08242525 100644
--- a/client/android/xray/src/main/kotlin/Xray.kt
+++ b/client/android/xray/src/main/kotlin/Xray.kt
@@ -5,6 +5,7 @@ import android.net.VpnService.Builder
import java.io.File
import java.io.IOException
import go.Seq
+import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.Protocol
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
@@ -16,72 +17,10 @@ import org.amnezia.vpn.protocol.xray.libXray.Logger
import org.amnezia.vpn.protocol.xray.libXray.Tun2SocksConfig
import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.net.InetNetwork
+import org.amnezia.vpn.util.net.ip
import org.amnezia.vpn.util.net.parseInetAddress
import org.json.JSONObject
-/**
- * Config example:
- * {
- * "appSplitTunnelType": 0,
- * "config_version": 0,
- * "description": "Server 1",
- * "dns1": "1.1.1.1",
- * "dns2": "1.0.0.1",
- * "hostName": "100.100.100.0",
- * "protocol": "xray",
- * "splitTunnelApps": [],
- * "splitTunnelSites": [],
- * "splitTunnelType": 0,
- * "xray_config_data": {
- * "inbounds": [
- * {
- * "listen": "127.0.0.1",
- * "port": 8080,
- * "protocol": "socks",
- * "settings": {
- * "udp": true
- * }
- * }
- * ],
- * "log": {
- * "loglevel": "error"
- * },
- * "outbounds": [
- * {
- * "protocol": "vless",
- * "settings": {
- * "vnext": [
- * {
- * "address": "100.100.100.0",
- * "port": 443,
- * "users": [
- * {
- * "encryption": "none",
- * "flow": "xtls-rprx-vision",
- * "id": "id"
- * }
- * ]
- * }
- * ]
- * },
- * "streamSettings": {
- * "network": "tcp",
- * "realitySettings": {
- * "fingerprint": "chrome",
- * "publicKey": "publicKey",
- * "serverName": "google.com",
- * "shortId": "id",
- * "spiderX": ""
- * },
- * "security": "reality"
- * }
- * }
- * ]
- * }
- * }
- *
- */
-
private const val TAG = "Xray"
private const val LIBXRAY_TAG = "libXray"
@@ -108,25 +47,30 @@ class Xray : Protocol() {
}
}
- override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
+ override suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
if (isRunning) {
Log.w(TAG, "XRay already running")
return
}
- val xrayJsonConfig = config.getJSONObject("xray_config_data")
+ val xrayJsonConfig = config.optJSONObject("xray_config_data")
+ ?: config.optJSONObject("ssxray_config_data")
+ ?: throw BadConfigException("config_data not found")
val xrayConfig = parseConfig(config, xrayJsonConfig)
- // for debug
- // xrayJsonConfig.getJSONObject("log").put("loglevel", "debug")
- xrayJsonConfig.getJSONObject("log").put("loglevel", "warning")
- // disable access log
- xrayJsonConfig.getJSONObject("log").put("access", "none")
+ (xrayJsonConfig.optJSONObject("log") ?: JSONObject().also { xrayJsonConfig.put("log", it) })
+ .put("loglevel", "warning")
+ .put("access", "none") // disable access log
- // replace socks address
- // (xrayJsonConfig.getJSONArray("inbounds")[0] as JSONObject).put("listen", "::1")
+ var xrayJsonConfigString = xrayJsonConfig.toString()
+ config.getString("hostName").let { hostName ->
+ val ipAddress = parseInetAddress(hostName).ip
+ if (hostName != ipAddress) {
+ xrayJsonConfigString = xrayJsonConfigString.replace(hostName, ipAddress)
+ }
+ }
- start(xrayConfig, xrayJsonConfig.toString(), vpnBuilder, protect)
+ start(xrayConfig, xrayJsonConfigString, vpnBuilder, protect)
state.value = CONNECTED
isRunning = true
}
@@ -186,8 +130,8 @@ class Xray : Protocol() {
LibXray.initXray(assetsPath)
val geoDir = File(assetsPath, "geo").absolutePath
val configPath = File(context.cacheDir, "config.json")
- Log.d(TAG, "xray.location.asset: $geoDir")
- Log.d(TAG, "config: $configPath")
+ Log.v(TAG, "xray.location.asset: $geoDir")
+ Log.v(TAG, "config: $configPath")
try {
configPath.writeText(configJson)
} catch (e: IOException) {
@@ -228,6 +172,10 @@ class Xray : Protocol() {
throw VpnStartException("Failed to start tun2socks: $err")
}
}
+
+ companion object {
+ val instance: Xray by lazy { Xray() }
+ }
}
private fun String?.isNotNullOrBlank(block: (String) -> Unit) {
diff --git a/client/cmake/3rdparty.cmake b/client/cmake/3rdparty.cmake
index ec544764..2b5036c5 100644
--- a/client/cmake/3rdparty.cmake
+++ b/client/cmake/3rdparty.cmake
@@ -2,15 +2,11 @@ set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/Modules;${CMAKE_MODULE_PATH}")
-if(NOT IOS AND NOT ANDROID)
- include(${CLIENT_ROOT_DIR}/3rd/SingleApplication/singleapplication.cmake)
-endif()
-
add_subdirectory(${CLIENT_ROOT_DIR}/3rd/SortFilterProxyModel)
set(LIBS ${LIBS} SortFilterProxyModel)
+include(${CLIENT_ROOT_DIR}/cmake/QSimpleCrypto.cmake)
include(${CLIENT_ROOT_DIR}/3rd/qrcodegen/qrcodegen.cmake)
-include(${CLIENT_ROOT_DIR}/3rd/QSimpleCrypto/QSimpleCrypto.cmake)
set(LIBSSH_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/libssh/")
set(OPENSSL_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/openssl/")
@@ -83,13 +79,12 @@ set(BUILD_WITH_QT6 ON)
add_subdirectory(${CLIENT_ROOT_DIR}/3rd/qtkeychain)
set(LIBS ${LIBS} qt6keychain)
-
include_directories(
${OPENSSL_INCLUDE_DIR}
${LIBSSH_INCLUDE_DIR}/include
${LIBSSH_ROOT_DIR}/include
${CLIENT_ROOT_DIR}/3rd/libssh/include
- ${CLIENT_ROOT_DIR}/3rd/QSimpleCrypto/include
+ ${CLIENT_ROOT_DIR}/3rd/QSimpleCrypto/src/include
${CLIENT_ROOT_DIR}/3rd/qtkeychain/qtkeychain
${CMAKE_CURRENT_BINARY_DIR}/3rd/qtkeychain
${CMAKE_CURRENT_BINARY_DIR}/3rd/libssh/include
diff --git a/client/cmake/QSimpleCrypto.cmake b/client/cmake/QSimpleCrypto.cmake
new file mode 100644
index 00000000..ec43cb83
--- /dev/null
+++ b/client/cmake/QSimpleCrypto.cmake
@@ -0,0 +1,21 @@
+set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
+set(QSIMPLECRYPTO_DIR ${CLIENT_ROOT_DIR}/3rd/QSimpleCrypto/src)
+
+include_directories(${QSIMPLECRYPTO_DIR})
+
+set(HEADERS ${HEADERS}
+ ${QSIMPLECRYPTO_DIR}/include/QAead.h
+ ${QSIMPLECRYPTO_DIR}/include/QBlockCipher.h
+ ${QSIMPLECRYPTO_DIR}/include/QRsa.h
+ ${QSIMPLECRYPTO_DIR}/include/QSimpleCrypto_global.h
+ ${QSIMPLECRYPTO_DIR}/include/QX509.h
+ ${QSIMPLECRYPTO_DIR}/include/QX509Store.h
+)
+
+set(SOURCES ${SOURCES}
+ ${QSIMPLECRYPTO_DIR}/sources/QAead.cpp
+ ${QSIMPLECRYPTO_DIR}/sources/QBlockCipher.cpp
+ ${QSIMPLECRYPTO_DIR}/sources/QRsa.cpp
+ ${QSIMPLECRYPTO_DIR}/sources/QX509.cpp
+ ${QSIMPLECRYPTO_DIR}/sources/QX509Store.cpp
+)
diff --git a/client/cmake/android.cmake b/client/cmake/android.cmake
index 13c357bd..34ca5bff 100644
--- a/client/cmake/android.cmake
+++ b/client/cmake/android.cmake
@@ -1,6 +1,6 @@
message("Client android ${CMAKE_ANDROID_ARCH_ABI} build")
-set(APP_ANDROID_MIN_SDK 24)
+set(APP_ANDROID_MIN_SDK 26)
set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING
"The minimum API level supported by the application or library" FORCE)
@@ -27,7 +27,6 @@ link_directories(${CMAKE_CURRENT_SOURCE_DIR}/platforms/android)
set(HEADERS ${HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.h
- ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.h
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.h
${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.h
)
@@ -35,7 +34,6 @@ set(HEADERS ${HEADERS}
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.cpp
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.cpp
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.cpp
${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.cpp
)
diff --git a/client/cmake/ios.cmake b/client/cmake/ios.cmake
index 5fda3506..58192237 100644
--- a/client/cmake/ios.cmake
+++ b/client/cmake/ios.cmake
@@ -76,11 +76,7 @@ set_target_properties(${PROJECT} PROPERTIES
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
XCODE_EMBED_APP_EXTENSIONS networkextension
- 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 "match AppStore org.amnezia.AmneziaVPN"
- XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "match Development org.amnezia.AmneziaVPN"
+ XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
)
set_target_properties(${PROJECT} PROPERTIES
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
@@ -126,9 +122,9 @@ add_subdirectory(ios/networkextension)
add_dependencies(${PROJECT} networkextension)
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
- "${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework"
+ "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework"
)
-set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos)
-target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework")
+set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/)
+target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework")
diff --git a/client/cmake/sources.cmake b/client/cmake/sources.cmake
new file mode 100644
index 00000000..c3af531a
--- /dev/null
+++ b/client/cmake/sources.cmake
@@ -0,0 +1,191 @@
+set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
+
+set(HEADERS ${HEADERS}
+ ${CLIENT_ROOT_DIR}/migrations.h
+ ${CLIENT_ROOT_DIR}/../ipc/ipc.h
+ ${CLIENT_ROOT_DIR}/amnezia_application.h
+ ${CLIENT_ROOT_DIR}/containers/containers_defs.h
+ ${CLIENT_ROOT_DIR}/core/defs.h
+ ${CLIENT_ROOT_DIR}/core/errorstrings.h
+ ${CLIENT_ROOT_DIR}/core/scripts_registry.h
+ ${CLIENT_ROOT_DIR}/core/server_defs.h
+ ${CLIENT_ROOT_DIR}/core/api/apiDefs.h
+ ${CLIENT_ROOT_DIR}/core/qrCodeUtils.h
+ ${CLIENT_ROOT_DIR}/core/controllers/coreController.h
+ ${CLIENT_ROOT_DIR}/core/controllers/gatewayController.h
+ ${CLIENT_ROOT_DIR}/core/controllers/serverController.h
+ ${CLIENT_ROOT_DIR}/core/controllers/vpnConfigurationController.h
+ ${CLIENT_ROOT_DIR}/protocols/protocols_defs.h
+ ${CLIENT_ROOT_DIR}/protocols/qml_register_protocols.h
+ ${CLIENT_ROOT_DIR}/ui/pages.h
+ ${CLIENT_ROOT_DIR}/ui/qautostart.h
+ ${CLIENT_ROOT_DIR}/protocols/vpnprotocol.h
+ ${CMAKE_CURRENT_BINARY_DIR}/version.h
+ ${CLIENT_ROOT_DIR}/core/sshclient.h
+ ${CLIENT_ROOT_DIR}/core/networkUtilities.h
+ ${CLIENT_ROOT_DIR}/core/serialization/serialization.h
+ ${CLIENT_ROOT_DIR}/core/serialization/transfer.h
+ ${CLIENT_ROOT_DIR}/../common/logger/logger.h
+ ${CLIENT_ROOT_DIR}/utils/qmlUtils.h
+ ${CLIENT_ROOT_DIR}/core/api/apiUtils.h
+)
+
+# Mozilla headres
+set(HEADERS ${HEADERS}
+ ${CLIENT_ROOT_DIR}/mozilla/models/server.h
+ ${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.h
+ ${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.h
+ ${CLIENT_ROOT_DIR}/mozilla/controllerimpl.h
+ ${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h
+)
+
+if(NOT IOS)
+ set(HEADERS ${HEADERS}
+ ${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.h
+ )
+endif()
+
+if(NOT ANDROID)
+ set(HEADERS ${HEADERS}
+ ${CLIENT_ROOT_DIR}/ui/notificationhandler.h
+ )
+endif()
+
+set(SOURCES ${SOURCES}
+ ${CLIENT_ROOT_DIR}/migrations.cpp
+ ${CLIENT_ROOT_DIR}/amnezia_application.cpp
+ ${CLIENT_ROOT_DIR}/containers/containers_defs.cpp
+ ${CLIENT_ROOT_DIR}/core/errorstrings.cpp
+ ${CLIENT_ROOT_DIR}/core/scripts_registry.cpp
+ ${CLIENT_ROOT_DIR}/core/server_defs.cpp
+ ${CLIENT_ROOT_DIR}/core/qrCodeUtils.cpp
+ ${CLIENT_ROOT_DIR}/core/controllers/coreController.cpp
+ ${CLIENT_ROOT_DIR}/core/controllers/gatewayController.cpp
+ ${CLIENT_ROOT_DIR}/core/controllers/serverController.cpp
+ ${CLIENT_ROOT_DIR}/core/controllers/vpnConfigurationController.cpp
+ ${CLIENT_ROOT_DIR}/protocols/protocols_defs.cpp
+ ${CLIENT_ROOT_DIR}/ui/qautostart.cpp
+ ${CLIENT_ROOT_DIR}/protocols/vpnprotocol.cpp
+ ${CLIENT_ROOT_DIR}/core/sshclient.cpp
+ ${CLIENT_ROOT_DIR}/core/networkUtilities.cpp
+ ${CLIENT_ROOT_DIR}/core/serialization/outbound.cpp
+ ${CLIENT_ROOT_DIR}/core/serialization/inbound.cpp
+ ${CLIENT_ROOT_DIR}/core/serialization/ss.cpp
+ ${CLIENT_ROOT_DIR}/core/serialization/ssd.cpp
+ ${CLIENT_ROOT_DIR}/core/serialization/vless.cpp
+ ${CLIENT_ROOT_DIR}/core/serialization/trojan.cpp
+ ${CLIENT_ROOT_DIR}/core/serialization/vmess.cpp
+ ${CLIENT_ROOT_DIR}/core/serialization/vmess_new.cpp
+ ${CLIENT_ROOT_DIR}/../common/logger/logger.cpp
+ ${CLIENT_ROOT_DIR}/utils/qmlUtils.cpp
+ ${CLIENT_ROOT_DIR}/core/api/apiUtils.cpp
+)
+
+# Mozilla sources
+set(SOURCES ${SOURCES}
+ ${CLIENT_ROOT_DIR}/mozilla/models/server.cpp
+ ${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.cpp
+ ${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.cpp
+ ${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
+)
+
+if(NOT IOS)
+ set(SOURCES ${SOURCES}
+ ${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.cpp
+ )
+endif()
+
+if(NOT ANDROID)
+ set(SOURCES ${SOURCES}
+ ${CLIENT_ROOT_DIR}/ui/notificationhandler.cpp
+ )
+endif()
+
+file(GLOB COMMON_FILES_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/*.h)
+file(GLOB COMMON_FILES_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/*.cpp)
+
+file(GLOB_RECURSE PAGE_LOGIC_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/pages_logic/*.h)
+file(GLOB_RECURSE PAGE_LOGIC_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/pages_logic/*.cpp)
+
+file(GLOB CONFIGURATORS_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/configurators/*.h)
+file(GLOB CONFIGURATORS_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/configurators/*.cpp)
+
+file(GLOB UI_MODELS_H CONFIGURE_DEPENDS
+ ${CLIENT_ROOT_DIR}/ui/models/*.h
+ ${CLIENT_ROOT_DIR}/ui/models/protocols/*.h
+ ${CLIENT_ROOT_DIR}/ui/models/services/*.h
+ ${CLIENT_ROOT_DIR}/ui/models/api/*.h
+)
+file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS
+ ${CLIENT_ROOT_DIR}/ui/models/*.cpp
+ ${CLIENT_ROOT_DIR}/ui/models/protocols/*.cpp
+ ${CLIENT_ROOT_DIR}/ui/models/services/*.cpp
+ ${CLIENT_ROOT_DIR}/ui/models/api/*.cpp
+)
+
+file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS
+ ${CLIENT_ROOT_DIR}/ui/controllers/*.h
+ ${CLIENT_ROOT_DIR}/ui/controllers/api/*.h
+)
+file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS
+ ${CLIENT_ROOT_DIR}/ui/controllers/*.cpp
+ ${CLIENT_ROOT_DIR}/ui/controllers/api/*.cpp
+)
+
+set(HEADERS ${HEADERS}
+ ${COMMON_FILES_H}
+ ${PAGE_LOGIC_H}
+ ${CONFIGURATORS_H}
+ ${UI_MODELS_H}
+ ${UI_CONTROLLERS_H}
+)
+set(SOURCES ${SOURCES}
+ ${COMMON_FILES_CPP}
+ ${PAGE_LOGIC_CPP}
+ ${CONFIGURATORS_CPP}
+ ${UI_MODELS_CPP}
+ ${UI_CONTROLLERS_CPP}
+)
+
+if(WIN32)
+ set(HEADERS ${HEADERS}
+ ${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.h
+ )
+
+ set(SOURCES ${SOURCES}
+ ${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.cpp
+ )
+
+ set(RESOURCES ${RESOURCES}
+ ${CMAKE_CURRENT_BINARY_DIR}/amneziavpn.rc
+ )
+endif()
+
+if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
+ message("Client desktop build")
+ add_compile_definitions(AMNEZIA_DESKTOP)
+
+ set(HEADERS ${HEADERS}
+ ${CLIENT_ROOT_DIR}/core/ipcclient.h
+ ${CLIENT_ROOT_DIR}/core/privileged_process.h
+ ${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.h
+ ${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.h
+ ${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.h
+ ${CLIENT_ROOT_DIR}/protocols/shadowsocksvpnprotocol.h
+ ${CLIENT_ROOT_DIR}/protocols/wireguardprotocol.h
+ ${CLIENT_ROOT_DIR}/protocols/xrayprotocol.h
+ ${CLIENT_ROOT_DIR}/protocols/awgprotocol.h
+ )
+
+ set(SOURCES ${SOURCES}
+ ${CLIENT_ROOT_DIR}/core/ipcclient.cpp
+ ${CLIENT_ROOT_DIR}/core/privileged_process.cpp
+ ${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.cpp
+ ${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.cpp
+ ${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.cpp
+ ${CLIENT_ROOT_DIR}/protocols/shadowsocksvpnprotocol.cpp
+ ${CLIENT_ROOT_DIR}/protocols/wireguardprotocol.cpp
+ ${CLIENT_ROOT_DIR}/protocols/xrayprotocol.cpp
+ ${CLIENT_ROOT_DIR}/protocols/awgprotocol.cpp
+ )
+endif()
diff --git a/client/configurators/openvpn_configurator.cpp b/client/configurators/openvpn_configurator.cpp
index c4bdf860..fafb7c2b 100644
--- a/client/configurators/openvpn_configurator.cpp
+++ b/client/configurators/openvpn_configurator.cpp
@@ -119,18 +119,21 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPairisSitesSplitTunnelingEnabled()) {
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
+
+#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
// Prevent ipv6 leak
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
+#endif
config.append("block-ipv6\n");
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
// no redirect-gateway
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
-#ifndef Q_OS_ANDROID
+#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
-#endif
// Prevent ipv6 leak
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
+#endif
config.append("block-ipv6\n");
}
}
diff --git a/client/configurators/wireguard_configurator.cpp b/client/configurators/wireguard_configurator.cpp
index f7faaa52..69699998 100644
--- a/client/configurators/wireguard_configurator.cpp
+++ b/client/configurators/wireguard_configurator.cpp
@@ -3,6 +3,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -19,13 +20,17 @@
#include "settings.h"
#include "utilities.h"
-WireguardConfigurator::WireguardConfigurator(std::shared_ptr settings, const QSharedPointer