mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-10-25 20:46:49 +09:00
Compare commits
268
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4723019624 | ||
|
|
1be9078b6c | ||
|
|
520658a295 | ||
|
|
b9ec722abb | ||
|
|
d917c798d7 | ||
|
|
0d168c039f | ||
|
|
af8f265555 | ||
|
|
c0e0d64284 | ||
|
|
9466a71141 | ||
|
|
d28a586a97 | ||
|
|
6fde0b6663 | ||
|
|
d143b9213b | ||
|
|
16433e9e46 | ||
|
|
39479d1999 | ||
|
|
a03b766e33 | ||
|
|
7df2655ba0 | ||
|
|
5c2ca9803d | ||
|
|
4e6af947fa | ||
|
|
f412ac6260 | ||
|
|
b45517bafd | ||
|
|
bda64fa391 | ||
|
|
e7040f7cc8 | ||
|
|
229970e799 | ||
|
|
7b0a9a055f | ||
|
|
06d370f716 | ||
|
|
ce0a4f1f96 | ||
|
|
3240aa3cb3 | ||
|
|
5c5935c738 | ||
|
|
00b2b1abcd | ||
|
|
e0891e1a15 | ||
|
|
f7df621c56 | ||
|
|
0b6dc5bcfc | ||
|
|
cbd6755aa5 | ||
|
|
3afbc248b1 | ||
|
|
30af81fe0a | ||
|
|
427b43c99b | ||
|
|
ed08ac6b46 | ||
|
|
1a2c1fa1b5 | ||
|
|
709fbac231 | ||
|
|
6b80a56f92 | ||
|
|
5c9d45a8a8 | ||
|
|
2edac24945 | ||
|
|
b68c9cc145 | ||
|
|
6f02d4eb62 | ||
|
|
6e60688e70 | ||
|
|
140b5c4292 | ||
|
|
8ee2ebbd20 | ||
|
|
b3eda4106d | ||
|
|
885e22be7c | ||
|
|
83ec073734 | ||
|
|
0160b0f9dc | ||
|
|
f55bd5e1a1 | ||
|
|
4d88eb8e79 | ||
|
|
874de74ac8 | ||
|
|
b3a4b34d48 | ||
|
|
ec4574bfcf | ||
|
|
73cfab166f | ||
|
|
0e8f85057d | ||
|
|
2e4908c557 | ||
|
|
401784414a | ||
|
|
8495124bc8 | ||
|
|
ba6ed540f5 | ||
|
|
19a60ea856 | ||
|
|
aecf1b463c | ||
|
|
ff24ba1011 | ||
|
|
802b708232 | ||
|
|
adeff3efb9 | ||
|
|
0103c1722e | ||
|
|
f9123e7b71 | ||
|
|
0f9ed4e69c | ||
|
|
9dfc95bac0 | ||
|
|
9b7914538f | ||
|
|
85c9cd260d | ||
|
|
73751cc049 | ||
|
|
2b61c48303 | ||
|
|
47b03f2bf4 | ||
|
|
3e02dfef63 | ||
|
|
4176d0130a | ||
|
|
f6175c2c69 | ||
|
|
b8b423ca19 | ||
|
|
3702d69b9d | ||
|
|
3253de9384 | ||
|
|
51070635a5 | ||
|
|
9b533f1ba6 | ||
|
|
9371dd405e | ||
|
|
27d8108e55 | ||
|
|
820a3f0b77 | ||
|
|
3d61b20271 | ||
|
|
eec81f8124 | ||
|
|
d7fbddb97f | ||
|
|
9170cb0318 | ||
|
|
76dec6fa9c | ||
|
|
4e3edcfc0a | ||
|
|
ad21d7ab64 | ||
|
|
f576eb509d | ||
|
|
33e229d0b2 | ||
|
|
78420d617b | ||
|
|
2552e33d64 | ||
|
|
301141c755 | ||
|
|
fac57ac89a | ||
|
|
da5ad0a845 | ||
|
|
60aeefe1c2 | ||
|
|
d527f6a5ce | ||
|
|
2802b42747 | ||
|
|
66c5d2f0a8 | ||
|
|
6dbf4ac62c | ||
|
|
ae2872830b | ||
|
|
6f4a3587e4 | ||
|
|
145f51906e | ||
|
|
12e72bc74b | ||
|
|
65a04799ef | ||
|
|
88cd5825d3 | ||
|
|
ecdad2a315 | ||
|
|
0398ddd6a2 | ||
|
|
50ea4d3b0f | ||
|
|
77be8169f2 | ||
|
|
7a435f76b6 | ||
|
|
42949e0dea | ||
|
|
26db423232 | ||
|
|
645cf52803 | ||
|
|
673b8ad5b2 | ||
|
|
2a03834bb2 | ||
|
|
c1b6149e49 | ||
|
|
0690d86e52 | ||
|
|
bb8a11d110 | ||
|
|
4f7ba4c9a8 | ||
|
|
aa41e4d915 | ||
|
|
bb43b5451f | ||
|
|
69dd415ab5 | ||
|
|
e605f549bd | ||
|
|
a961932b2e | ||
|
|
e8cc80f046 | ||
|
|
67f29ac483 | ||
|
|
c9bde5cdc0 | ||
|
|
e878911819 | ||
|
|
f1a0b7f0ef | ||
|
|
3e0a5104e7 | ||
|
|
8f53d563a4 | ||
|
|
414740ffb7 | ||
|
|
7437d47d92 | ||
|
|
a68f19d72f | ||
|
|
11641c5e22 | ||
|
|
8d3e21d46a | ||
|
|
5ad54bfdc1 | ||
|
|
a8f5e95fb1 | ||
|
|
a4f3d08c02 | ||
|
|
3d2174d84e | ||
|
|
12fbc7d258 | ||
|
|
164b7e2551 | ||
|
|
eafac491d8 | ||
|
|
3cfb6e968d | ||
|
|
375825125f | ||
|
|
a8520e7545 | ||
|
|
ac154cdd83 | ||
|
|
9290775ab5 | ||
|
|
d14e8cdee4 | ||
|
|
7aac9f9d0e | ||
|
|
41aaac7d32 | ||
|
|
c8d2399db9 | ||
|
|
f37c8e5fd4 | ||
|
|
9f5025c10b | ||
|
|
c27d999c41 | ||
|
|
9681bea237 | ||
|
|
8905d6352c | ||
|
|
8599b20678 | ||
|
|
491f09a51b | ||
|
|
0f1519a21f | ||
|
|
1a17f2956a | ||
|
|
d94e27bfa9 | ||
|
|
b892156092 | ||
|
|
7b1b8dc749 | ||
|
|
6d0167dcf3 | ||
|
|
0c2d661e1c | ||
|
|
95d1440d6f | ||
|
|
0cabf60dc4 | ||
|
|
836ca1cc6b | ||
|
|
be799daafe | ||
|
|
ad9d674a03 | ||
|
|
f52c3c430f | ||
|
|
a91ab0e910 | ||
|
|
39d1f1677f | ||
|
|
b0dcae3586 | ||
|
|
ed6351f8f1 | ||
|
|
e486a2fb26 | ||
|
|
195bdb947e | ||
|
|
1576aed1ea | ||
|
|
e66fbc3289 | ||
|
|
b4c89ad58f | ||
|
|
8cc5846808 | ||
|
|
2eaaf01ca1 | ||
|
|
67694c0f96 | ||
|
|
163e5b2c52 | ||
|
|
508f1d3a42 | ||
|
|
d2207a5255 | ||
|
|
bf03f5c9ae | ||
|
|
656223f57d | ||
|
|
dc6e3ec53b | ||
|
|
5835a756ce | ||
|
|
f3f98a50ed | ||
|
|
0f4bb78712 | ||
|
|
c4014518cb | ||
|
|
3605f62feb | ||
|
|
5c3e253067 | ||
|
|
8d43cee52e | ||
|
|
1e64413904 | ||
|
|
6fd1ea26ee | ||
|
|
e619fd4af9 | ||
|
|
e7658f9859 | ||
|
|
3defb09da9 | ||
|
|
a672434909 | ||
|
|
c3fdd977b1 | ||
|
|
dd233f77fc | ||
|
|
02efd9c217 | ||
|
|
20f3c0388a | ||
|
|
ef530780bd | ||
|
|
d7ec611ff4 | ||
|
|
b897e7102e | ||
|
|
1cc5c5384e | ||
|
|
db602ac65b | ||
|
|
eaa209bc3a | ||
|
|
51d4aea9e2 | ||
|
|
9738ada946 | ||
|
|
8ec105bee0 | ||
|
|
4fd0852bb3 | ||
|
|
a53e904f7b | ||
|
|
2d22b52b5d | ||
|
|
426ac49f6f | ||
|
|
c164814abd | ||
|
|
3084892ed8 | ||
|
|
9297f877c4 | ||
|
|
1bf808c9ee | ||
|
|
91f44fb394 | ||
|
|
385a52f676 | ||
|
|
8ef16781eb | ||
|
|
ad5ea1ca44 | ||
|
|
0ba5d754d5 | ||
|
|
ccdcfdce8a | ||
|
|
712fb4d0d3 | ||
|
|
de65a03998 | ||
|
|
6d6710db4a | ||
|
|
c34c3e51ea | ||
|
|
5dc3b64e0b | ||
|
|
e8ceeb6e20 | ||
|
|
d38c7ce6a5 | ||
|
|
e625543b94 | ||
|
|
679bd4e4c9 | ||
|
|
c6a312845a | ||
|
|
ef0530ec6b | ||
|
|
2c98a90d60 | ||
|
|
b90fad6664 | ||
|
|
5a5ea4a018 | ||
|
|
bc68c487ee | ||
|
|
617cdf14ad | ||
|
|
f1c970461f | ||
|
|
2fde47a86f | ||
|
|
4e5f2f44b6 | ||
|
|
e8a2e54d05 | ||
|
|
362a82f944 | ||
|
|
aee82282ac | ||
|
|
8497aeeb91 | ||
|
|
147726ecb0 | ||
|
|
9cfcb714ae | ||
|
|
40725d4155 | ||
|
|
3e03002ead | ||
|
|
4ae9cddcce | ||
|
|
16724645ce | ||
|
|
25f8283edd | ||
|
|
9090ec54e7 |
No files matched your search
@@ -48,14 +48,18 @@ jobs:
|
||||
export QIF_BIN_DIR=${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin
|
||||
bash deploy/build_linux.sh
|
||||
|
||||
- name: 'Pack installer'
|
||||
run: cd deploy && tar -cf AmneziaVPN_Linux_Installer.tar AmneziaVPN_Linux_Installer.bin
|
||||
|
||||
- name: 'Upload installer artifact'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN_Linux_installer
|
||||
path: deploy/AmneziaVPN_Linux_Installer
|
||||
name: AmneziaVPN_Linux_installer.tar
|
||||
path: deploy/AmneziaVPN_Linux_Installer.tar
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Upload unpacked artifact'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN_Linux_unpacked
|
||||
path: deploy/AppDir
|
||||
@@ -110,13 +114,14 @@ jobs:
|
||||
call deploy\\build_windows.bat
|
||||
|
||||
- name: 'Upload installer artifact'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN_Windows_installer
|
||||
path: AmneziaVPN_x${{ env.BUILD_ARCH }}.exe
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Upload unpacked artifact'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN_Windows_unpacked
|
||||
path: deploy\\build_${{ env.BUILD_ARCH }}\\client\\Release
|
||||
@@ -200,7 +205,7 @@ jobs:
|
||||
IOS_NE_PROVISIONING_PROFILE: ${{ secrets.IOS_NE_PROVISIONING_PROFILE }}
|
||||
|
||||
# - name: 'Upload appstore .ipa and dSYMs to artifacts'
|
||||
# uses: actions/upload-artifact@v3
|
||||
# uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: app-store ipa & dsyms
|
||||
# path: |
|
||||
@@ -255,13 +260,14 @@ jobs:
|
||||
bash deploy/build_macos.sh
|
||||
|
||||
- name: 'Upload installer artifact'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN_MacOS_installer
|
||||
path: AmneziaVPN.dmg
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Upload unpacked artifact'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN_MacOS_unpacked
|
||||
path: deploy/build/client/AmneziaVPN.app
|
||||
@@ -275,7 +281,7 @@ jobs:
|
||||
|
||||
env:
|
||||
ANDROID_BUILD_PLATFORM: android-34
|
||||
QT_VERSION: 6.5.3
|
||||
QT_VERSION: 6.6.1
|
||||
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
||||
|
||||
steps:
|
||||
@@ -375,32 +381,44 @@ jobs:
|
||||
ANDROID_KEYSTORE_KEY_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }}
|
||||
ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
|
||||
shell: bash
|
||||
run: ./deploy/build_android.sh --apk all --build-platform ${{ env.ANDROID_BUILD_PLATFORM }}
|
||||
run: ./deploy/build_android.sh --aab --apk all --build-platform ${{ env.ANDROID_BUILD_PLATFORM }}
|
||||
|
||||
- name: 'Upload x86_64 apk'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN-android-x86_64
|
||||
path: deploy/build/AmneziaVPN-x86_64-release.apk
|
||||
compression-level: 0
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Upload x86 apk'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN-android-x86
|
||||
path: deploy/build/AmneziaVPN-x86-release.apk
|
||||
compression-level: 0
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Upload arm64-v8a apk'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN-android-arm64-v8a
|
||||
path: deploy/build/AmneziaVPN-arm64-v8a-release.apk
|
||||
compression-level: 0
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Upload armeabi-v7a apk'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN-android-armeabi-v7a
|
||||
path: deploy/build/AmneziaVPN-armeabi-v7a-release.apk
|
||||
compression-level: 0
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Upload aab'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN-android
|
||||
path: deploy/build/AmneziaVPN-release.aab
|
||||
compression-level: 0
|
||||
retention-days: 7
|
||||
+3
-3
@@ -22,6 +22,6 @@
|
||||
[submodule "client/3rd-prebuilt"]
|
||||
path = client/3rd-prebuilt
|
||||
url = https://github.com/amnezia-vpn/3rd-prebuilt
|
||||
[submodule "client/3rd/awg-apple"]
|
||||
path = client/3rd/awg-apple
|
||||
url = https://github.com/amnezia-vpn/awg-apple
|
||||
[submodule "client/3rd/amneziawg-apple"]
|
||||
path = client/3rd/amneziawg-apple
|
||||
url = https://github.com/amnezia-vpn/amneziawg-apple
|
||||
+2
-2
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
|
||||
project(${PROJECT} VERSION 4.1.0.1
|
||||
project(${PROJECT} VERSION 4.2.1.1
|
||||
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 39)
|
||||
set(APP_ANDROID_VERSION_CODE 43)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
|
||||
@@ -18,7 +18,6 @@ Amnezia is an open-source VPN client, with a key feature that enables you to dep
|
||||
[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://signal.group/...](https://signal.group/#CjQKIB2gUf8QH_IXnOJMGQWMDjYz9cNfmRQipGWLFiIgc4MwEhAKBONrSiWHvoUFbbD0xwdh) - Signal channel
|
||||
|
||||
## Tech
|
||||
|
||||
@@ -36,7 +35,7 @@ AmneziaVPN uses a number of open source projects to work:
|
||||
Make sure to pull all submodules after checking out the repo.
|
||||
|
||||
```bash
|
||||
git submodule update --init
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
## Development
|
||||
@@ -50,7 +49,15 @@ Look 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.4. Install QT for macos in [here](https://doc.qt.io/qt-6/macos.html)
|
||||
2. We use QT to generate the XCode project. we need QT version 6.6.1. Install QT for macos in [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
|
||||
- Qt Shader Tools
|
||||
- Additional Libraries:
|
||||
- Qt Image Formats
|
||||
- Qt Multimedia
|
||||
- Qt Remote Objects
|
||||
|
||||
3. Install cmake is require. We recommend cmake version 3.25. You can install cmake in [here](https://cmake.org/download/)
|
||||
|
||||
@@ -66,10 +73,11 @@ gomobile init
|
||||
5. Build project
|
||||
```bash
|
||||
export QT_BIN_DIR="<PATH-TO-QT-FOLDER>/Qt/<QT-VERSION>/ios/bin"
|
||||
export QT_MACOS_ROOT_DIR="<PATH-TO-QT-FOLDER>/Qt/<QT-VERSION>/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_BIN_DIR
|
||||
$QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR
|
||||
```
|
||||
Replace PATH-TO-QT-FOLDER and QT-VERSION to your environment
|
||||
|
||||
|
||||
+1
-1
Submodule client/3rd-prebuilt updated: fcf3022a27...e568e7d0e8.
+1
Submodule client/3rd/amneziawg-apple added at f23eee4700.
Vendored
-1
Submodule client/3rd/awg-apple deleted from 233eda6760.
Vendored
+1
-1
Submodule client/3rd/qtkeychain updated: 8bbaa6d830...74776e2a3e.
@@ -56,6 +56,7 @@ set(CMAKE_AUTORCC ON)
|
||||
set(AMNEZIAVPN_TS_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru.ts
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_zh_CN.ts
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_fa_IR.ts
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE AMNEZIAVPN_TS_SOURCES *.qrc *.cpp *.h *.ui)
|
||||
@@ -107,7 +108,7 @@ set(HEADERS ${HEADERS}
|
||||
${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/servercontroller.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/qml_register_protocols.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h
|
||||
@@ -146,7 +147,7 @@ set(SOURCES ${SOURCES}
|
||||
${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/servercontroller.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/controllers/serverController.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.cpp
|
||||
|
||||
+389
-366
@@ -1,366 +1,389 @@
|
||||
#include "amnezia_application.h"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QFontDatabase>
|
||||
#include <QMimeData>
|
||||
#include <QQuickStyle>
|
||||
#include <QResource>
|
||||
#include <QStandardPaths>
|
||||
#include <QTextDocument>
|
||||
#include <QTimer>
|
||||
#include <QTranslator>
|
||||
|
||||
#include <QQuickItem>
|
||||
|
||||
#include "logger.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "platforms/ios/QRCodeReaderBase.h"
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#include "platforms/android/android_controller.h"
|
||||
#endif
|
||||
|
||||
#include "protocols/qml_register_protocols.h"
|
||||
|
||||
#if defined(Q_OS_IOS)
|
||||
#include "platforms/ios/ios_controller.h"
|
||||
#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);
|
||||
|
||||
// Fix config file permissions
|
||||
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
||||
{
|
||||
QSettings s(ORGANIZATION_NAME, APPLICATION_NAME);
|
||||
s.setValue("permFixed", true);
|
||||
}
|
||||
|
||||
QString configLoc1 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/"
|
||||
+ ORGANIZATION_NAME + "/" + APPLICATION_NAME + ".conf";
|
||||
QFile::setPermissions(configLoc1, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
|
||||
|
||||
QString configLoc2 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/"
|
||||
+ ORGANIZATION_NAME + "/" + APPLICATION_NAME + "/" + APPLICATION_NAME + ".conf";
|
||||
QFile::setPermissions(configLoc2, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
|
||||
#endif
|
||||
|
||||
m_settings = std::shared_ptr<Settings>(new Settings);
|
||||
}
|
||||
|
||||
AmneziaApplication::~AmneziaApplication()
|
||||
{
|
||||
m_vpnConnectionThread.quit();
|
||||
m_vpnConnectionThread.wait(3000);
|
||||
|
||||
if (m_engine) {
|
||||
QObject::disconnect(m_engine, 0, 0, 0);
|
||||
delete m_engine;
|
||||
}
|
||||
}
|
||||
|
||||
void AmneziaApplication::init()
|
||||
{
|
||||
m_engine = new QQmlApplicationEngine;
|
||||
|
||||
const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml"));
|
||||
QObject::connect(
|
||||
m_engine, &QQmlApplicationEngine::objectCreated, this,
|
||||
[url](QObject *obj, const QUrl &objUrl) {
|
||||
if (!obj && url == objUrl)
|
||||
QCoreApplication::exit(-1);
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance());
|
||||
|
||||
m_configurator = std::shared_ptr<VpnConfigurator>(new VpnConfigurator(m_settings, this));
|
||||
m_vpnConnection.reset(new VpnConnection(m_settings, m_configurator));
|
||||
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
|
||||
m_vpnConnectionThread.start();
|
||||
|
||||
initModels();
|
||||
loadTranslator();
|
||||
initControllers();
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
connect(AndroidController::instance(), &AndroidController::initialized, this,
|
||||
[this](bool status, bool connected, const QDateTime &connectionDate) {
|
||||
if (connected) {
|
||||
m_connectionController->onConnectionStateChanged(Vpn::ConnectionState::Connected);
|
||||
if (m_vpnConnection)
|
||||
m_vpnConnection->restoreConnection();
|
||||
}
|
||||
});
|
||||
if (!AndroidController::instance()->initialize()) {
|
||||
qCritical() << QString("Init failed");
|
||||
if (m_vpnConnection)
|
||||
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, [this](QString data) {
|
||||
m_pageController->replaceStartPage();
|
||||
m_importController->extractConfigFromData(data);
|
||||
m_pageController->goToPageViewConfig();
|
||||
});
|
||||
#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);
|
||||
});
|
||||
#endif
|
||||
|
||||
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(),
|
||||
&ConnectionController::openConnection);
|
||||
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
|
||||
&ConnectionController::closeConnection);
|
||||
connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(),
|
||||
&NotificationHandler::onTranslationsUpdated);
|
||||
|
||||
m_engine->load(url);
|
||||
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
|
||||
|
||||
if (m_settings->isSaveLogs()) {
|
||||
if (!Logger::init()) {
|
||||
qWarning() << "Initialization of debug subsystem failed";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (m_parser.isSet("a"))
|
||||
m_pageController->showOnStartup();
|
||||
else
|
||||
emit m_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();
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
// Android TextField clipboard workaround
|
||||
// https://bugreports.qt.io/browse/QTBUG-113461
|
||||
#ifdef Q_OS_ANDROID
|
||||
QObject::connect(qApp, &QGuiApplication::applicationStateChanged, [](Qt::ApplicationState state) {
|
||||
if (state == Qt::ApplicationActive) {
|
||||
if (qApp->clipboard()->mimeData()->formats().contains("text/html")) {
|
||||
QTextDocument doc;
|
||||
doc.setHtml(qApp->clipboard()->mimeData()->html());
|
||||
qApp->clipboard()->setText(doc.toPlainText());
|
||||
}
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void AmneziaApplication::registerTypes()
|
||||
{
|
||||
qRegisterMetaType<ServerCredentials>("ServerCredentials");
|
||||
|
||||
qRegisterMetaType<DockerContainer>("DockerContainer");
|
||||
qRegisterMetaType<TransportProto>("TransportProto");
|
||||
qRegisterMetaType<Proto>("Proto");
|
||||
qRegisterMetaType<ServiceType>("ServiceType");
|
||||
|
||||
declareQmlProtocolEnum();
|
||||
declareQmlContainerEnum();
|
||||
|
||||
qmlRegisterType<QRCodeReader>("QRCodeReader", 1, 0, "QRCodeReader");
|
||||
|
||||
m_containerProps.reset(new ContainerProps());
|
||||
qmlRegisterSingletonInstance("ContainerProps", 1, 0, "ContainerProps", m_containerProps.get());
|
||||
|
||||
m_protocolProps.reset(new ProtocolProps());
|
||||
qmlRegisterSingletonInstance("ProtocolProps", 1, 0, "ProtocolProps", m_protocolProps.get());
|
||||
|
||||
qmlRegisterSingletonType(QUrl("qrc:/ui/qml/Filters/ContainersModelFilters.qml"), "ContainersModelFilters", 1, 0,
|
||||
"ContainersModelFilters");
|
||||
|
||||
//
|
||||
Vpn::declareQmlVpnConnectionStateEnum();
|
||||
PageLoader::declareQmlPageEnum();
|
||||
}
|
||||
|
||||
void AmneziaApplication::loadFonts()
|
||||
{
|
||||
QQuickStyle::setStyle("Basic");
|
||||
|
||||
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);
|
||||
m_parser.addHelpOption();
|
||||
m_parser.addVersionOption();
|
||||
|
||||
QCommandLineOption c_autostart { { "a", "autostart" }, "System autostart" };
|
||||
m_parser.addOption(c_autostart);
|
||||
|
||||
QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" };
|
||||
m_parser.addOption(c_cleanup);
|
||||
|
||||
m_parser.process(*this);
|
||||
|
||||
if (m_parser.isSet(c_cleanup)) {
|
||||
Logger::cleanUp();
|
||||
QTimer::singleShot(100, this, [this] { quit(); });
|
||||
exec();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
|
||||
{
|
||||
return m_engine;
|
||||
}
|
||||
|
||||
void AmneziaApplication::initModels()
|
||||
{
|
||||
m_containersModel.reset(new ContainersModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
|
||||
connect(m_vpnConnection.get(), &VpnConnection::newVpnConfigurationCreated, m_containersModel.get(),
|
||||
&ContainersModel::updateContainersConfig);
|
||||
|
||||
m_serversModel.reset(new ServersModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
|
||||
connect(m_serversModel.get(), &ServersModel::currentlyProcessedServerIndexChanged, m_containersModel.get(),
|
||||
&ContainersModel::setCurrentlyProcessedServerIndex);
|
||||
connect(m_serversModel.get(), &ServersModel::defaultServerIndexChanged, m_containersModel.get(),
|
||||
&ContainersModel::setCurrentlyProcessedServerIndex);
|
||||
connect(m_containersModel.get(), &ContainersModel::containersModelUpdated, m_serversModel.get(),
|
||||
&ServersModel::updateContainersConfig);
|
||||
|
||||
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_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());
|
||||
|
||||
#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());
|
||||
}
|
||||
|
||||
void AmneziaApplication::initControllers()
|
||||
{
|
||||
m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_vpnConnection));
|
||||
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
|
||||
|
||||
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_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_settings, m_configurator));
|
||||
m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
|
||||
|
||||
m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_settings));
|
||||
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
|
||||
if (m_settingsController->isAutoStartEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
|
||||
QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
|
||||
}
|
||||
|
||||
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
|
||||
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
||||
|
||||
m_systemController.reset(new SystemController(m_settings));
|
||||
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
|
||||
}
|
||||
#include "amnezia_application.h"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QFontDatabase>
|
||||
#include <QMimeData>
|
||||
#include <QQuickStyle>
|
||||
#include <QResource>
|
||||
#include <QStandardPaths>
|
||||
#include <QTextDocument>
|
||||
#include <QTimer>
|
||||
#include <QTranslator>
|
||||
|
||||
#include <QQuickItem>
|
||||
|
||||
#include "logger.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "platforms/ios/QRCodeReaderBase.h"
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#include "platforms/android/android_controller.h"
|
||||
#endif
|
||||
|
||||
#include "protocols/qml_register_protocols.h"
|
||||
|
||||
#if defined(Q_OS_IOS)
|
||||
#include "platforms/ios/ios_controller.h"
|
||||
#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);
|
||||
|
||||
// Fix config file permissions
|
||||
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
||||
{
|
||||
QSettings s(ORGANIZATION_NAME, APPLICATION_NAME);
|
||||
s.setValue("permFixed", true);
|
||||
}
|
||||
|
||||
QString configLoc1 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/"
|
||||
+ ORGANIZATION_NAME + "/" + APPLICATION_NAME + ".conf";
|
||||
QFile::setPermissions(configLoc1, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
|
||||
|
||||
QString configLoc2 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/"
|
||||
+ ORGANIZATION_NAME + "/" + APPLICATION_NAME + "/" + APPLICATION_NAME + ".conf";
|
||||
QFile::setPermissions(configLoc2, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
|
||||
#endif
|
||||
|
||||
m_settings = std::shared_ptr<Settings>(new Settings);
|
||||
}
|
||||
|
||||
AmneziaApplication::~AmneziaApplication()
|
||||
{
|
||||
m_vpnConnectionThread.quit();
|
||||
m_vpnConnectionThread.wait(3000);
|
||||
|
||||
if (m_engine) {
|
||||
QObject::disconnect(m_engine, 0, 0, 0);
|
||||
delete m_engine;
|
||||
}
|
||||
}
|
||||
|
||||
void AmneziaApplication::init()
|
||||
{
|
||||
m_engine = new QQmlApplicationEngine;
|
||||
|
||||
const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml"));
|
||||
QObject::connect(
|
||||
m_engine, &QQmlApplicationEngine::objectCreated, this,
|
||||
[url](QObject *obj, const QUrl &objUrl) {
|
||||
if (!obj && url == objUrl)
|
||||
QCoreApplication::exit(-1);
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance());
|
||||
|
||||
m_configurator = std::shared_ptr<VpnConfigurator>(new VpnConfigurator(m_settings, this));
|
||||
m_vpnConnection.reset(new VpnConnection(m_settings, m_configurator));
|
||||
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);
|
||||
|
||||
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();
|
||||
});
|
||||
#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);
|
||||
});
|
||||
#endif
|
||||
|
||||
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(),
|
||||
&ConnectionController::openConnection);
|
||||
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
|
||||
&ConnectionController::closeConnection);
|
||||
connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(),
|
||||
&NotificationHandler::onTranslationsUpdated);
|
||||
|
||||
m_engine->load(url);
|
||||
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
if (m_settings->isSaveLogs()) {
|
||||
if (!Logger::init()) {
|
||||
qWarning() << "Initialization of debug subsystem failed";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (m_parser.isSet("a"))
|
||||
m_pageController->showOnStartup();
|
||||
else
|
||||
emit m_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();
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
// Android TextArea clipboard workaround
|
||||
// Text from TextArea always has "text/html" mime-type:
|
||||
// /qt/6.6.1/Src/qtdeclarative/src/quick/items/qquicktextcontrol.cpp:1865
|
||||
// Next, html is created for this mime-type:
|
||||
// /qt/6.6.1/Src/qtdeclarative/src/quick/items/qquicktextcontrol.cpp:1885
|
||||
// And this html goes to the Androids clipboard, i.e. text from TextArea is always copied as richText:
|
||||
// /qt/6.6.1/Src/qtbase/src/plugins/platforms/android/androidjniclipboard.cpp:46
|
||||
// So we catch all the copies to the clipboard and clear them from "text/html"
|
||||
#ifdef Q_OS_ANDROID
|
||||
connect(QGuiApplication::clipboard(), &QClipboard::dataChanged, []() {
|
||||
auto clipboard = QGuiApplication::clipboard();
|
||||
if (clipboard->mimeData()->hasHtml()) {
|
||||
clipboard->setText(clipboard->text());
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void AmneziaApplication::registerTypes()
|
||||
{
|
||||
qRegisterMetaType<ServerCredentials>("ServerCredentials");
|
||||
|
||||
qRegisterMetaType<DockerContainer>("DockerContainer");
|
||||
qRegisterMetaType<TransportProto>("TransportProto");
|
||||
qRegisterMetaType<Proto>("Proto");
|
||||
qRegisterMetaType<ServiceType>("ServiceType");
|
||||
|
||||
declareQmlProtocolEnum();
|
||||
declareQmlContainerEnum();
|
||||
|
||||
qmlRegisterType<QRCodeReader>("QRCodeReader", 1, 0, "QRCodeReader");
|
||||
|
||||
m_containerProps.reset(new ContainerProps());
|
||||
qmlRegisterSingletonInstance("ContainerProps", 1, 0, "ContainerProps", m_containerProps.get());
|
||||
|
||||
m_protocolProps.reset(new ProtocolProps());
|
||||
qmlRegisterSingletonInstance("ProtocolProps", 1, 0, "ProtocolProps", m_protocolProps.get());
|
||||
|
||||
qmlRegisterSingletonType(QUrl("qrc:/ui/qml/Filters/ContainersModelFilters.qml"), "ContainersModelFilters", 1, 0,
|
||||
"ContainersModelFilters");
|
||||
|
||||
//
|
||||
Vpn::declareQmlVpnConnectionStateEnum();
|
||||
PageLoader::declareQmlPageEnum();
|
||||
}
|
||||
|
||||
void AmneziaApplication::loadFonts()
|
||||
{
|
||||
QQuickStyle::setStyle("Basic");
|
||||
|
||||
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);
|
||||
m_parser.addHelpOption();
|
||||
m_parser.addVersionOption();
|
||||
|
||||
QCommandLineOption c_autostart { { "a", "autostart" }, "System autostart" };
|
||||
m_parser.addOption(c_autostart);
|
||||
|
||||
QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" };
|
||||
m_parser.addOption(c_cleanup);
|
||||
|
||||
m_parser.process(*this);
|
||||
|
||||
if (m_parser.isSet(c_cleanup)) {
|
||||
Logger::cleanUp();
|
||||
QTimer::singleShot(100, this, [this] { quit(); });
|
||||
exec();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
|
||||
{
|
||||
return m_engine;
|
||||
}
|
||||
|
||||
void AmneziaApplication::initModels()
|
||||
{
|
||||
m_containersModel.reset(new ContainersModel(this));
|
||||
m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.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::defaultContainerChanged, m_containersModel.get(),
|
||||
&ContainersModel::setDefaultContainer);
|
||||
m_containersModel->setDefaultContainer(m_serversModel->getDefaultContainer()); // make better?
|
||||
|
||||
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_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());
|
||||
|
||||
#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_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);
|
||||
|
||||
connect(m_configurator.get(), &VpnConfigurator::newVpnConfigCreated, this,
|
||||
[this](const QString &clientId, const QString &clientName, const DockerContainer container,
|
||||
ServerCredentials credentials) {
|
||||
m_serversModel->reloadContainerConfig();
|
||||
m_clientManagementModel->appendClient(clientId, clientName, container, credentials);
|
||||
emit m_configurator->clientModelUpdated();
|
||||
});
|
||||
}
|
||||
|
||||
void AmneziaApplication::initControllers()
|
||||
{
|
||||
m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_vpnConnection));
|
||||
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
|
||||
|
||||
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_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_configurator));
|
||||
m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
|
||||
|
||||
m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, 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_systemController.reset(new SystemController(m_settings));
|
||||
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
|
||||
|
||||
m_cloudController.reset(new ApiController(m_serversModel, m_containersModel));
|
||||
m_engine->rootContext()->setContextProperty("ApiController", m_cloudController.get());
|
||||
}
|
||||
+127
-123
@@ -1,123 +1,127 @@
|
||||
#ifndef AMNEZIA_APPLICATION_H
|
||||
#define AMNEZIA_APPLICATION_H
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
#include <QThread>
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
#include <QGuiApplication>
|
||||
#else
|
||||
#include <QApplication>
|
||||
#endif
|
||||
|
||||
#include "settings.h"
|
||||
#include "vpnconnection.h"
|
||||
|
||||
#include "configurators/vpn_configurator.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/models/containers_model.h"
|
||||
#include "ui/models/languageModel.h"
|
||||
#include "ui/models/protocols/cloakConfigModel.h"
|
||||
#include "ui/notificationhandler.h"
|
||||
#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_model.h"
|
||||
#include "ui/models/servers_model.h"
|
||||
#include "ui/models/services/sftpConfigModel.h"
|
||||
#include "ui/models/sites_model.h"
|
||||
|
||||
#define amnApp (static_cast<AmneziaApplication *>(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"
|
||||
#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;
|
||||
|
||||
signals:
|
||||
void translationsUpdated();
|
||||
|
||||
private:
|
||||
void initModels();
|
||||
void initControllers();
|
||||
|
||||
QQmlApplicationEngine *m_engine {};
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
std::shared_ptr<VpnConfigurator> m_configurator;
|
||||
|
||||
QSharedPointer<ContainerProps> m_containerProps;
|
||||
QSharedPointer<ProtocolProps> m_protocolProps;
|
||||
|
||||
QSharedPointer<QTranslator> m_translator;
|
||||
QCommandLineParser m_parser;
|
||||
|
||||
QSharedPointer<ContainersModel> m_containersModel;
|
||||
QSharedPointer<ServersModel> m_serversModel;
|
||||
QSharedPointer<LanguageModel> m_languageModel;
|
||||
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
||||
QSharedPointer<SitesModel> m_sitesModel;
|
||||
|
||||
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
|
||||
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
|
||||
QScopedPointer<CloakConfigModel> m_cloakConfigModel;
|
||||
QScopedPointer<WireGuardConfigModel> m_wireGuardConfigModel;
|
||||
QScopedPointer<AwgConfigModel> m_awgConfigModel;
|
||||
#ifdef Q_OS_WINDOWS
|
||||
QScopedPointer<Ikev2ConfigModel> m_ikev2ConfigModel;
|
||||
#endif
|
||||
|
||||
QScopedPointer<SftpConfigModel> m_sftpConfigModel;
|
||||
|
||||
QSharedPointer<VpnConnection> m_vpnConnection;
|
||||
QThread m_vpnConnectionThread;
|
||||
QScopedPointer<NotificationHandler> m_notificationHandler;
|
||||
|
||||
QScopedPointer<ConnectionController> m_connectionController;
|
||||
QScopedPointer<PageController> m_pageController;
|
||||
QScopedPointer<InstallController> m_installController;
|
||||
QScopedPointer<ImportController> m_importController;
|
||||
QScopedPointer<ExportController> m_exportController;
|
||||
QScopedPointer<SettingsController> m_settingsController;
|
||||
QScopedPointer<SitesController> m_sitesController;
|
||||
QScopedPointer<SystemController> m_systemController;
|
||||
};
|
||||
|
||||
#endif // AMNEZIA_APPLICATION_H
|
||||
#ifndef AMNEZIA_APPLICATION_H
|
||||
#define AMNEZIA_APPLICATION_H
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
#include <QThread>
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
#include <QGuiApplication>
|
||||
#else
|
||||
#include <QApplication>
|
||||
#endif
|
||||
|
||||
#include "settings.h"
|
||||
#include "vpnconnection.h"
|
||||
|
||||
#include "configurators/vpn_configurator.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/apiController.h"
|
||||
#include "ui/models/containers_model.h"
|
||||
#include "ui/models/languageModel.h"
|
||||
#include "ui/models/protocols/cloakConfigModel.h"
|
||||
#include "ui/notificationhandler.h"
|
||||
#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_model.h"
|
||||
#include "ui/models/servers_model.h"
|
||||
#include "ui/models/services/sftpConfigModel.h"
|
||||
#include "ui/models/sites_model.h"
|
||||
#include "ui/models/clientManagementModel.h"
|
||||
|
||||
#define amnApp (static_cast<AmneziaApplication *>(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"
|
||||
#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;
|
||||
|
||||
signals:
|
||||
void translationsUpdated();
|
||||
|
||||
private:
|
||||
void initModels();
|
||||
void initControllers();
|
||||
|
||||
QQmlApplicationEngine *m_engine {};
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
std::shared_ptr<VpnConfigurator> m_configurator;
|
||||
|
||||
QSharedPointer<ContainerProps> m_containerProps;
|
||||
QSharedPointer<ProtocolProps> m_protocolProps;
|
||||
|
||||
QSharedPointer<QTranslator> m_translator;
|
||||
QCommandLineParser m_parser;
|
||||
|
||||
QSharedPointer<ContainersModel> m_containersModel;
|
||||
QSharedPointer<ServersModel> m_serversModel;
|
||||
QSharedPointer<LanguageModel> m_languageModel;
|
||||
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
||||
QSharedPointer<SitesModel> m_sitesModel;
|
||||
QSharedPointer<ClientManagementModel> m_clientManagementModel;
|
||||
|
||||
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
|
||||
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
|
||||
QScopedPointer<CloakConfigModel> m_cloakConfigModel;
|
||||
QScopedPointer<WireGuardConfigModel> m_wireGuardConfigModel;
|
||||
QScopedPointer<AwgConfigModel> m_awgConfigModel;
|
||||
#ifdef Q_OS_WINDOWS
|
||||
QScopedPointer<Ikev2ConfigModel> m_ikev2ConfigModel;
|
||||
#endif
|
||||
|
||||
QScopedPointer<SftpConfigModel> m_sftpConfigModel;
|
||||
|
||||
QSharedPointer<VpnConnection> m_vpnConnection;
|
||||
QThread m_vpnConnectionThread;
|
||||
QScopedPointer<NotificationHandler> m_notificationHandler;
|
||||
|
||||
QScopedPointer<ConnectionController> m_connectionController;
|
||||
QScopedPointer<PageController> m_pageController;
|
||||
QScopedPointer<InstallController> m_installController;
|
||||
QScopedPointer<ImportController> m_importController;
|
||||
QScopedPointer<ExportController> m_exportController;
|
||||
QScopedPointer<SettingsController> m_settingsController;
|
||||
QScopedPointer<SitesController> m_sitesController;
|
||||
QScopedPointer<SystemController> m_systemController;
|
||||
QScopedPointer<ApiController> m_cloudController;
|
||||
};
|
||||
|
||||
#endif // AMNEZIA_APPLICATION_H
|
||||
@@ -1,6 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- Leave package attribute for androiddeployqt -->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.amnezia.vpn"
|
||||
android:versionName="-- %%INSERT_VERSION_NAME%% --"
|
||||
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
|
||||
@@ -15,31 +17,33 @@
|
||||
<!-- %%INSERT_FEATURES -->
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<!-- To request network state -->
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" android:maxSdkVersion="30" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<!-- Enable when VPN-per-app mode will be implemented -->
|
||||
<!-- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> -->
|
||||
|
||||
<application
|
||||
android:name=".qt.AmneziaApp"
|
||||
android:name=".AmneziaApplication"
|
||||
android:label="-- %%INSERT_APP_NAME%% --"
|
||||
android:allowNativeHeapPointerTagging="false"
|
||||
android:icon="@drawable/icon"
|
||||
android:roundIcon="@drawable/icon_round"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar">
|
||||
android:icon="@mipmap/icon"
|
||||
android:roundIcon="@mipmap/icon_round"
|
||||
android:theme="@style/NoActionBar"
|
||||
android:fullBackupContent="@xml/backup_content"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
tools:targetApi="s">
|
||||
|
||||
<activity
|
||||
android:name=".qt.VPNActivity"
|
||||
android:name=".AmneziaActivity"
|
||||
android:configChanges="uiMode|screenSize|smallestScreenSize|screenLayout|orientation|density
|
||||
|fontScale|layoutDirection|locale|keyboard|keyboardHidden|navigation|mcc|mnc"
|
||||
android:launchMode="singleInstance"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:exported="true">
|
||||
|
||||
<intent-filter>
|
||||
@@ -48,7 +52,7 @@
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="org.amnezia.vpn.qt.IMPORT_CONFIG" />
|
||||
<action android:name="org.amnezia.vpn.IMPORT_CONFIG" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
|
||||
@@ -62,57 +66,65 @@
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".qt.CameraActivity"
|
||||
android:name=".CameraActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:launchMode="singleTask"
|
||||
android:taskAffinity=""
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".qt.ImportConfigActivity"
|
||||
android:exported="true">
|
||||
android:name=".VpnRequestActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:launchMode="singleTask"
|
||||
android:taskAffinity=""
|
||||
android:exported="false"
|
||||
android:theme="@style/Translucent" />
|
||||
|
||||
<intent-filter android:label="AmneziaVPN">
|
||||
<activity
|
||||
android:name=".ImportConfigActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:launchMode="singleTask"
|
||||
android:taskAffinity=""
|
||||
android:exported="true"
|
||||
android:theme="@style/Translucent">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:mimeType="application/octet-stream" />
|
||||
<data android:mimeType="text/plain" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:scheme="vpn" android:host="*" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:scheme="file" />
|
||||
<data android:scheme="content" />
|
||||
<data android:mimeType="*/*" />
|
||||
<data android:host="*" />
|
||||
|
||||
<data android:pathPattern=".*\\.vpn" />
|
||||
<data android:pathPattern=".*\\..*\\.vpn" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\.vpn" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.vpn" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.vpn" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:label="AmneziaVPN">
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:scheme="file" />
|
||||
<data android:scheme="content" />
|
||||
<data android:mimeType="*/*" />
|
||||
<data android:host="*" />
|
||||
<data android:pathPattern=".*\\.cfg" />
|
||||
<data android:pathPattern=".*\\..*\\.cfg" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\.cfg" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.cfg" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.cfg" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:label="AmneziaVPN">
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:scheme="file" />
|
||||
<data android:scheme="content" />
|
||||
<data android:mimeType="*/*" />
|
||||
<data android:host="*" />
|
||||
<data android:pathPattern=".*\\.conf" />
|
||||
<data android:pathPattern=".*\\..*\\.conf" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\.conf" />
|
||||
@@ -122,11 +134,11 @@
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".VPNService"
|
||||
android:process=":QtOnlyProcess"
|
||||
android:name=".AmneziaVpnService"
|
||||
android:process=":amneziaVpnService"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||
android:foregroundServiceType="specialUse"
|
||||
android:exported="true">
|
||||
android:exported="false">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.net.VpnService" />
|
||||
@@ -135,23 +147,12 @@
|
||||
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="vpn" />
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".qt.VPNPermissionHelper"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||
android:foregroundServiceType="specialUse"
|
||||
android:exported="true">
|
||||
|
||||
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="vpn" />
|
||||
</service>
|
||||
|
||||
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="org.amnezia.vpn.fileprovider"
|
||||
android:authorities="org.amnezia.vpn.qtprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/fileprovider" />
|
||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/qtprovider_paths" />
|
||||
</provider>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,17 +0,0 @@
|
||||
[proxy_all]
|
||||
|
||||
[bypass_list]
|
||||
0.0.0.0/8
|
||||
10.0.0.0/8
|
||||
100.64.0.0/10
|
||||
127.0.0.0/8
|
||||
169.254.0.0/16
|
||||
172.16.0.0/12
|
||||
192.0.0.0/29
|
||||
192.0.2.0/24
|
||||
192.88.99.0/24
|
||||
192.168.0.0/16
|
||||
198.18.0.0/15
|
||||
198.51.100.0/24
|
||||
203.0.113.0/24
|
||||
224.0.0.0/3
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,18 @@
|
||||
plugins {
|
||||
id(libs.plugins.android.library.get().pluginId)
|
||||
id(libs.plugins.kotlin.android.get().pluginId)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "org.amnezia.vpn.protocol.awg"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":utils"))
|
||||
compileOnly(project(":protocolApi"))
|
||||
implementation(project(":wireguard"))
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package org.amnezia.vpn.protocol.awg
|
||||
|
||||
import org.amnezia.vpn.protocol.wireguard.Wireguard
|
||||
import org.json.JSONObject
|
||||
|
||||
/**
|
||||
* Config example:
|
||||
* {
|
||||
* "protocol": "awg",
|
||||
* "description": "Server 1",
|
||||
* "dns1": "1.1.1.1",
|
||||
* "dns2": "1.0.0.1",
|
||||
* "hostName": "100.100.100.0",
|
||||
* "splitTunnelSites": [
|
||||
* ],
|
||||
* "splitTunnelType": 0,
|
||||
* "awg_config_data": {
|
||||
* "H1": "969537490",
|
||||
* "H2": "481688153",
|
||||
* "H3": "2049399200",
|
||||
* "H4": "52029755",
|
||||
* "Jc": "3",
|
||||
* "Jmax": "1000",
|
||||
* "Jmin": "50",
|
||||
* "S1": "49",
|
||||
* "S2": "60",
|
||||
* "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
|
||||
* Jc = 3
|
||||
* Jmin = 50
|
||||
* Jmax = 1000
|
||||
* S1 = 49
|
||||
* S2 = 60
|
||||
* H1 = 969537490
|
||||
* H2 = 481688153
|
||||
* H3 = 2049399200
|
||||
* H4 = 52029755
|
||||
*
|
||||
* [Peer]
|
||||
* PublicKey = publicKeyBase64
|
||||
* PresharedKey = presharedKeyBase64
|
||||
* AllowedIPs = 0.0.0.0/0, ::/0
|
||||
* Endpoint = 100.100.100.0:12345
|
||||
* PersistentKeepalive = 25
|
||||
* "
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
class Awg : Wireguard() {
|
||||
|
||||
override val ifName: String = "awg0"
|
||||
|
||||
override fun parseConfig(config: JSONObject): AwgConfig {
|
||||
val configDataJson = config.getJSONObject("awg_config_data")
|
||||
val configData = parseConfigData(configDataJson.getString("config"))
|
||||
return AwgConfig.build {
|
||||
configWireguard(configData)
|
||||
configSplitTunneling(config)
|
||||
configData["Jc"]?.let { setJc(it.toInt()) }
|
||||
configData["Jmin"]?.let { setJmin(it.toInt()) }
|
||||
configData["Jmax"]?.let { setJmax(it.toInt()) }
|
||||
configData["S1"]?.let { setS1(it.toInt()) }
|
||||
configData["S2"]?.let { setS2(it.toInt()) }
|
||||
configData["H1"]?.let { setH1(it.toLong()) }
|
||||
configData["H2"]?.let { setH2(it.toLong()) }
|
||||
configData["H3"]?.let { setH3(it.toLong()) }
|
||||
configData["H4"]?.let { setH4(it.toLong()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package org.amnezia.vpn.protocol.awg
|
||||
|
||||
import org.amnezia.vpn.protocol.BadConfigException
|
||||
import org.amnezia.vpn.protocol.wireguard.WireguardConfig
|
||||
|
||||
class AwgConfig private constructor(
|
||||
wireguardConfigBuilder: WireguardConfig.Builder,
|
||||
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
|
||||
) : WireguardConfig(wireguardConfigBuilder) {
|
||||
|
||||
private constructor(builder: Builder) : this(
|
||||
builder,
|
||||
builder.jc,
|
||||
builder.jmin,
|
||||
builder.jmax,
|
||||
builder.s1,
|
||||
builder.s2,
|
||||
builder.h1,
|
||||
builder.h2,
|
||||
builder.h3,
|
||||
builder.h4
|
||||
)
|
||||
|
||||
override fun appendDeviceLine(sb: StringBuilder) = with(sb) {
|
||||
super.appendDeviceLine(this)
|
||||
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")
|
||||
}
|
||||
|
||||
class Builder : WireguardConfig.Builder() {
|
||||
|
||||
private var _jc: Int? = null
|
||||
internal var jc: Int
|
||||
get() = _jc ?: throw BadConfigException("AWG: parameter jc is undefined")
|
||||
private set(value) { _jc = value }
|
||||
|
||||
private var _jmin: Int? = null
|
||||
internal var jmin: Int
|
||||
get() = _jmin ?: throw BadConfigException("AWG: parameter jmin is undefined")
|
||||
private set(value) { _jmin = value }
|
||||
|
||||
private var _jmax: Int? = null
|
||||
internal var jmax: Int
|
||||
get() = _jmax ?: throw BadConfigException("AWG: parameter jmax is undefined")
|
||||
private set(value) { _jmax = value }
|
||||
|
||||
private var _s1: Int? = null
|
||||
internal var s1: Int
|
||||
get() = _s1 ?: throw BadConfigException("AWG: parameter s1 is undefined")
|
||||
private set(value) { _s1 = value }
|
||||
|
||||
private var _s2: Int? = null
|
||||
internal var s2: Int
|
||||
get() = _s2 ?: throw BadConfigException("AWG: parameter s2 is undefined")
|
||||
private set(value) { _s2 = value }
|
||||
|
||||
private var _h1: Long? = null
|
||||
internal var h1: Long
|
||||
get() = _h1 ?: throw BadConfigException("AWG: parameter h1 is undefined")
|
||||
private set(value) { _h1 = value }
|
||||
|
||||
private var _h2: Long? = null
|
||||
internal var h2: Long
|
||||
get() = _h2 ?: throw BadConfigException("AWG: parameter h2 is undefined")
|
||||
private set(value) { _h2 = value }
|
||||
|
||||
private var _h3: Long? = null
|
||||
internal var h3: Long
|
||||
get() = _h3 ?: throw BadConfigException("AWG: parameter h3 is undefined")
|
||||
private set(value) { _h3 = value }
|
||||
|
||||
private var _h4: Long? = null
|
||||
internal var h4: Long
|
||||
get() = _h4 ?: throw BadConfigException("AWG: parameter h4 is undefined")
|
||||
private set(value) { _h4 = value }
|
||||
|
||||
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(): AwgConfig = configBuild().run { AwgConfig(this@Builder) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
inline fun build(block: Builder.() -> Unit): AwgConfig = Builder().apply(block).build()
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
@@ -11,13 +13,12 @@ kotlin {
|
||||
// get values from gradle or local properties
|
||||
val qtTargetSdkVersion: String by gradleProperties
|
||||
val qtTargetAbiList: String by gradleProperties
|
||||
val qtAndroidDir: String by gradleProperties
|
||||
val outputBaseName: String by gradleProperties
|
||||
|
||||
android {
|
||||
namespace = "org.amnezia.vpn"
|
||||
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
viewBinding = true
|
||||
}
|
||||
|
||||
@@ -42,8 +43,9 @@ android {
|
||||
sourceSets {
|
||||
getByName("main") {
|
||||
manifest.srcFile("AndroidManifest.xml")
|
||||
java.setSrcDirs(listOf("$qtAndroidDir/src", "src"))
|
||||
res.setSrcDirs(listOf("$qtAndroidDir/res", "res"))
|
||||
java.setSrcDirs(listOf("src"))
|
||||
res.setSrcDirs(listOf("res"))
|
||||
// androyddeployqt creates the folders below
|
||||
assets.setSrcDirs(listOf("assets"))
|
||||
jniLibs.setSrcDirs(listOf("libs"))
|
||||
}
|
||||
@@ -76,17 +78,37 @@ android {
|
||||
isUniversalApk = false
|
||||
}
|
||||
}
|
||||
|
||||
// fix for Qt Creator to allow deploying the application to a device
|
||||
// to enable this fix, add the line outputBaseName=android-build to local.properties
|
||||
if (outputBaseName.isNotEmpty()) {
|
||||
applicationVariants.all {
|
||||
outputs.map { it as BaseVariantOutputImpl }
|
||||
.forEach { output ->
|
||||
if (output.outputFileName.endsWith(".apk")) {
|
||||
output.outputFileName = "$outputBaseName-${buildType.name}.apk"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lint {
|
||||
disable += "InvalidFragmentVersionForActivityResult"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
|
||||
implementation(project(":qt"))
|
||||
implementation(project(":utils"))
|
||||
implementation(project(":protocolApi"))
|
||||
implementation(project(":wireguard"))
|
||||
implementation(project(":awg"))
|
||||
implementation(project(":openvpn"))
|
||||
implementation(project(":cloak"))
|
||||
implementation(libs.androidx.core)
|
||||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.androidx.security.crypto)
|
||||
implementation(libs.androidx.activity)
|
||||
implementation(libs.kotlinx.coroutines)
|
||||
implementation(libs.bundles.androidx.camera)
|
||||
implementation(libs.google.mlkit)
|
||||
implementation(project(":shadowsocks"))
|
||||
// todo: remove after finish refactoring
|
||||
implementation(libs.androidx.constraintlayout)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
plugins {
|
||||
id(libs.plugins.android.library.get().pluginId)
|
||||
id(libs.plugins.kotlin.android.get().pluginId)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "org.amnezia.vpn.protocol.cloak"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":utils"))
|
||||
compileOnly(project(":protocolApi"))
|
||||
implementation(project(":openvpn"))
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
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.protocol.openvpn.OpenVpnConfig
|
||||
import org.amnezia.vpn.util.net.InetNetwork
|
||||
import org.amnezia.vpn.util.net.parseInetAddress
|
||||
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 parseConfig(config: JSONObject): ClientAPI_Config {
|
||||
val openVpnConfig = ClientAPI_Config()
|
||||
|
||||
val openVpnConfigStr = config.getJSONObject("openvpn_config_data").getString("config")
|
||||
val cloakConfigJson = checkCloakJson(config.getJSONObject("cloak_config_data"))
|
||||
val cloakConfigStr = Base64.encodeToString(cloakConfigJson.toString().toByteArray(), Base64.DEFAULT)
|
||||
|
||||
val configStr = "$openVpnConfigStr\n<cloak>\n$cloakConfigStr\n</cloak>\n"
|
||||
|
||||
openVpnConfig.usePluggableTransports = true
|
||||
openVpnConfig.content = configStr
|
||||
return openVpnConfig
|
||||
}
|
||||
|
||||
override fun configPluggableTransport(configBuilder: OpenVpnConfig.Builder, config: JSONObject) {
|
||||
// exclude remote server ip from vpn routes
|
||||
val remoteServer = config.getString("hostName")
|
||||
val remoteServerAddress = InetNetwork(parseInetAddress(remoteServer))
|
||||
configBuilder.excludeRoute(remoteServerAddress)
|
||||
}
|
||||
|
||||
private fun checkCloakJson(cloakConfigJson: JSONObject): JSONObject {
|
||||
cloakConfigJson.put("NumConn", 1)
|
||||
cloakConfigJson.put("ProxyMethod", "openvpn")
|
||||
if (cloakConfigJson.has("port")) {
|
||||
val port = cloakConfigJson["port"]
|
||||
cloakConfigJson.remove("port")
|
||||
cloakConfigJson.put("RemotePort", port)
|
||||
}
|
||||
if (cloakConfigJson.has("remote")) {
|
||||
val remote = cloakConfigJson["remote"]
|
||||
cloakConfigJson.remove("remote")
|
||||
cloakConfigJson.put("RemoteHost", remote)
|
||||
}
|
||||
return cloakConfigJson
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
[versions]
|
||||
agp = "8.1.3"
|
||||
agp = "8.2.0"
|
||||
kotlin = "1.9.20"
|
||||
androidx-core = "1.12.0"
|
||||
androidx-appcompat = "1.6.1"
|
||||
androidx-camera = "1.2.3"
|
||||
androidx-activity = "1.8.1"
|
||||
androidx-annotation = "1.7.0"
|
||||
androidx-camera = "1.3.0"
|
||||
androidx-security-crypto = "1.1.0-alpha06"
|
||||
kotlinx-coroutines = "1.7.3"
|
||||
google-mlkit = "17.2.0"
|
||||
|
||||
[libraries]
|
||||
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
|
||||
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
|
||||
androidx-activity = { module = "androidx.activity:activity-ktx", version.ref = "androidx-activity" }
|
||||
androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" }
|
||||
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" }
|
||||
@@ -18,8 +20,6 @@ androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "
|
||||
androidx-security-crypto = { module = "androidx.security:security-crypto-ktx", version.ref = "androidx-security-crypto" }
|
||||
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
|
||||
google-mlkit = { module = "com.google.mlkit:barcode-scanning", version.ref = "google-mlkit" }
|
||||
# todo: remove after finish refactoring
|
||||
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version = "2.1.4" }
|
||||
|
||||
[bundles]
|
||||
androidx-camera = [
|
||||
|
||||
@@ -24,7 +24,7 @@ private class PropertyDelegate(
|
||||
private val localProperties: Properties = localProperties(rootDir)
|
||||
) : ReadOnlyProperty<Any?, String> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): String =
|
||||
providers.gradleProperty(property.name).orNull ?: localProperties.getProperty(property.name)
|
||||
providers.gradleProperty(property.name).orNull ?: localProperties.getProperty(property.name).orEmpty()
|
||||
}
|
||||
|
||||
private lateinit var settingsPropertyDelegate: ReadOnlyProperty<Any?, String>
|
||||
|
||||
Binary file not shown.
+3
-1
@@ -1,5 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -0,0 +1,18 @@
|
||||
plugins {
|
||||
id(libs.plugins.android.library.get().pluginId)
|
||||
id(libs.plugins.kotlin.android.get().pluginId)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "org.amnezia.vpn.protocol.openvpn"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":utils"))
|
||||
compileOnly(project(":protocolApi"))
|
||||
implementation(libs.kotlinx.coroutines)
|
||||
}
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_Config {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_Config obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_ConnectionInfo {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_ConnectionInfo obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_DynamicChallenge {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_DynamicChallenge obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_EvalConfig {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_EvalConfig obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_Event {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_Event obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_ExternalPKIBase {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_ExternalPKIBase obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -20,6 +20,18 @@ public class ClientAPI_ExternalPKICertRequest extends ClientAPI_ExternalPKIReque
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_ExternalPKICertRequest obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_ExternalPKIRequestBase {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_ExternalPKIRequestBase obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -20,6 +20,18 @@ public class ClientAPI_ExternalPKISignRequest extends ClientAPI_ExternalPKIReque
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_ExternalPKISignRequest obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_InterfaceStats {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_InterfaceStats obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_KeyValue {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_KeyValue obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_LLVector extends java.util.AbstractList<Long> implements
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_LLVector obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_LogInfo {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_LogInfo obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_MergeConfig {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_MergeConfig obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -20,6 +20,18 @@ public class ClientAPI_OpenVPNClient extends ClientAPI_TunBuilderBase {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_OpenVPNClient obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_OpenVPNClientHelper {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_OpenVPNClientHelper obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_ProvideCreds {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_ProvideCreds obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_RemoteOverride {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_RemoteOverride obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_ServerEntry {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_ServerEntry obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_ServerEntryVector extends java.util.AbstractList<ClientAP
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_ServerEntryVector obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_SessionToken {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_SessionToken obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_Status {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_Status obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_StringVec extends java.util.AbstractList<String> implemen
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_StringVec obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_TransportStats {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_TransportStats obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+15
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -21,6 +21,18 @@ public class ClientAPI_TunBuilderBase {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(ClientAPI_TunBuilderBase obj) {
|
||||
long ptr = 0;
|
||||
if (obj != null) {
|
||||
if (!obj.swigCMemOwn)
|
||||
throw new RuntimeException("Cannot release ownership as memory is not owned");
|
||||
ptr = obj.swigCPtr;
|
||||
obj.swigCMemOwn = false;
|
||||
obj.delete();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
delete();
|
||||
+7
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -22,5 +22,9 @@ public class SWIGTYPE_p_std__string {
|
||||
protected static long getCPtr(SWIGTYPE_p_std__string obj) {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(SWIGTYPE_p_std__string obj) {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -22,5 +22,9 @@ public class SWIGTYPE_p_std__vectorT_openvpn__ClientAPI__KeyValue_t {
|
||||
protected static long getCPtr(SWIGTYPE_p_std__vectorT_openvpn__ClientAPI__KeyValue_t obj) {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
|
||||
protected static long swigRelease(SWIGTYPE_p_std__vectorT_openvpn__ClientAPI__KeyValue_t obj) {
|
||||
return (obj == null) ? 0 : obj.swigCPtr;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.1
|
||||
* This file was automatically generated by SWIG (https://www.swig.org).
|
||||
* Version 4.1.1
|
||||
*
|
||||
* Do not make changes to this file unless you know what you are doing--modify
|
||||
* Do not make changes to this file unless you know what you are doing - modify
|
||||
* the SWIG interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
package org.amnezia.vpn.protocol.openvpn
|
||||
|
||||
import android.content.Context
|
||||
import android.net.VpnService.Builder
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import net.openvpn.ovpn3.ClientAPI_Config
|
||||
import org.amnezia.vpn.protocol.BadConfigException
|
||||
import org.amnezia.vpn.protocol.Protocol
|
||||
import org.amnezia.vpn.protocol.ProtocolState
|
||||
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
||||
import org.amnezia.vpn.protocol.Statistics
|
||||
import org.amnezia.vpn.protocol.VpnStartException
|
||||
import org.amnezia.vpn.util.net.getLocalNetworks
|
||||
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 lateinit var context: Context
|
||||
private var openVpnClient: OpenVpnClient? = null
|
||||
private lateinit var scope: CoroutineScope
|
||||
|
||||
override val statistics: Statistics
|
||||
get() {
|
||||
openVpnClient?.let { client ->
|
||||
val stats = client.transport_stats()
|
||||
return Statistics.build {
|
||||
setRxBytes(stats.bytesIn)
|
||||
setTxBytes(stats.bytesOut)
|
||||
}
|
||||
}
|
||||
return Statistics.EMPTY_STATISTICS
|
||||
}
|
||||
|
||||
override fun initialize(context: Context, state: MutableStateFlow<ProtocolState>, onError: (String) -> Unit) {
|
||||
super.initialize(context, state, onError)
|
||||
loadSharedLibrary(context, "ovpn3")
|
||||
this.context = context
|
||||
scope = CoroutineScope(Dispatchers.IO)
|
||||
}
|
||||
|
||||
override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
|
||||
val configBuilder = OpenVpnConfig.Builder()
|
||||
|
||||
openVpnClient = OpenVpnClient(
|
||||
configBuilder = configBuilder,
|
||||
state = state,
|
||||
getLocalNetworks = { ipv6 -> getLocalNetworks(context, ipv6) },
|
||||
establish = makeEstablish(vpnBuilder),
|
||||
protect = protect,
|
||||
onError = onError
|
||||
)
|
||||
|
||||
try {
|
||||
openVpnClient?.let { client ->
|
||||
val openVpnConfig = parseConfig(config)
|
||||
val evalConfig = client.eval_config(openVpnConfig)
|
||||
if (evalConfig.error) {
|
||||
throw BadConfigException("OpenVPN config parse error: ${evalConfig.message}")
|
||||
}
|
||||
configPluggableTransport(configBuilder, config)
|
||||
configBuilder.configSplitTunneling(config)
|
||||
|
||||
scope.launch {
|
||||
val status = client.connect()
|
||||
if (status.error) {
|
||||
state.value = DISCONNECTED
|
||||
onError("OpenVpn connect() error: ${status.status}: ${status.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
openVpnClient = null
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
override fun stopVpn() {
|
||||
openVpnClient?.stop()
|
||||
openVpnClient = null
|
||||
}
|
||||
|
||||
override fun reconnectVpn(vpnBuilder: Builder) {
|
||||
openVpnClient?.let {
|
||||
it.establish = makeEstablish(vpnBuilder)
|
||||
it.reconnect(0)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun parseConfig(config: JSONObject): ClientAPI_Config {
|
||||
val openVpnConfig = ClientAPI_Config()
|
||||
openVpnConfig.content = config.getJSONObject("openvpn_config_data").getString("config")
|
||||
return openVpnConfig
|
||||
}
|
||||
|
||||
protected open fun configPluggableTransport(configBuilder: OpenVpnConfig.Builder, config: JSONObject) {}
|
||||
|
||||
private fun makeEstablish(vpnBuilder: Builder): (OpenVpnConfig.Builder) -> Int = { configBuilder ->
|
||||
val openVpnConfig = configBuilder.build()
|
||||
buildVpnInterface(openVpnConfig, vpnBuilder)
|
||||
|
||||
vpnBuilder.establish().use { tunFd ->
|
||||
if (tunFd == null) {
|
||||
throw VpnStartException("Create VPN interface: permission not granted or revoked")
|
||||
}
|
||||
return@use tunFd.detachFd()
|
||||
}
|
||||
}
|
||||
}
|
||||
+426
@@ -0,0 +1,426 @@
|
||||
package org.amnezia.vpn.protocol.openvpn
|
||||
|
||||
import android.net.ProxyInfo
|
||||
import android.os.Build
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.getAndUpdate
|
||||
import net.openvpn.ovpn3.ClientAPI_Config
|
||||
import net.openvpn.ovpn3.ClientAPI_EvalConfig
|
||||
import net.openvpn.ovpn3.ClientAPI_Event
|
||||
import net.openvpn.ovpn3.ClientAPI_LogInfo
|
||||
import net.openvpn.ovpn3.ClientAPI_OpenVPNClient
|
||||
import net.openvpn.ovpn3.ClientAPI_Status
|
||||
import net.openvpn.ovpn3.ClientAPI_StringVec
|
||||
import net.openvpn.ovpn3.ClientAPI_TransportStats
|
||||
import org.amnezia.vpn.protocol.ProtocolState
|
||||
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
|
||||
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
||||
import org.amnezia.vpn.protocol.ProtocolState.RECONNECTING
|
||||
import org.amnezia.vpn.util.Log
|
||||
import org.amnezia.vpn.util.net.InetNetwork
|
||||
import org.amnezia.vpn.util.net.parseInetAddress
|
||||
|
||||
private const val TAG = "OpenVpnClient"
|
||||
private const val EMULATED_EXCLUDE_ROUTES = (1 shl 16)
|
||||
|
||||
class OpenVpnClient(
|
||||
private val configBuilder: OpenVpnConfig.Builder,
|
||||
private val state: MutableStateFlow<ProtocolState>,
|
||||
private val getLocalNetworks: (Boolean) -> List<InetNetwork>,
|
||||
internal var establish: (OpenVpnConfig.Builder) -> Int,
|
||||
private val protect: (Int) -> Boolean,
|
||||
private val onError: (String) -> Unit
|
||||
) : ClientAPI_OpenVPNClient() {
|
||||
|
||||
/**************************************************************************
|
||||
* Tun builder callbacks
|
||||
**************************************************************************/
|
||||
|
||||
// Tun builder methods, loosely based on the Android VpnService.Builder
|
||||
// abstraction. These methods comprise an abstraction layer that
|
||||
// allows the OpenVPN C++ core to call out to external methods for
|
||||
// establishing the tunnel, adding routes, etc.
|
||||
|
||||
// All methods returning bool use the return
|
||||
// value to indicate success (true) or fail (false).
|
||||
// tun_builder_new() should be called first, then arbitrary setter methods,
|
||||
// and finally tun_builder_establish to return the socket descriptor
|
||||
// for the session. IP addresses are pre-validated before being passed to
|
||||
// these methods.
|
||||
// This interface is based on Android's VpnService.Builder.
|
||||
|
||||
// Callback to construct a new tun builder
|
||||
// Should be called first.
|
||||
override fun tun_builder_new(): Boolean {
|
||||
Log.d(TAG, "tun_builder_new")
|
||||
configBuilder.clearAddresses()
|
||||
return true
|
||||
}
|
||||
|
||||
// Callback to set MTU of the VPN interface
|
||||
// Never called more than once per tun_builder session.
|
||||
override fun tun_builder_set_mtu(mtu: Int): Boolean {
|
||||
Log.d(TAG, "tun_builder_set_mtu: $mtu")
|
||||
configBuilder.setMtu(mtu)
|
||||
return true
|
||||
}
|
||||
|
||||
// Callback to add network address to VPN interface
|
||||
// May be called more than once per tun_builder session
|
||||
override fun tun_builder_add_address(
|
||||
address: String, prefix_length: Int,
|
||||
gateway: String, ipv6: Boolean, net30: Boolean
|
||||
): Boolean {
|
||||
Log.d(TAG, "tun_builder_add_address: $address, $prefix_length, $gateway, $ipv6, $net30")
|
||||
configBuilder.addAddress(InetNetwork(address, prefix_length))
|
||||
return true
|
||||
}
|
||||
|
||||
// Callback to add route to VPN interface
|
||||
// May be called more than once per tun_builder session
|
||||
// metric is optional and should be ignored if < 0
|
||||
override fun tun_builder_add_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean {
|
||||
Log.d(TAG, "tun_builder_add_route: $address, $prefix_length, $metric, $ipv6")
|
||||
if (address == "remote_host") return false
|
||||
configBuilder.addRoute(InetNetwork(address, prefix_length))
|
||||
return true
|
||||
}
|
||||
|
||||
// Callback to exclude route from VPN interface
|
||||
// May be called more than once per tun_builder session
|
||||
// metric is optional and should be ignored if < 0
|
||||
override fun tun_builder_exclude_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean {
|
||||
Log.d(TAG, "tun_builder_exclude_route: $address, $prefix_length, $metric, $ipv6")
|
||||
configBuilder.excludeRoute(InetNetwork(address, prefix_length))
|
||||
return true
|
||||
}
|
||||
|
||||
// Callback to add DNS server to VPN interface
|
||||
// May be called more than once per tun_builder session
|
||||
// If reroute_dns is true, all DNS traffic should be routed over the
|
||||
// tunnel, while if false, only DNS traffic that matches an added search
|
||||
// domain should be routed.
|
||||
// Guaranteed to be called after tun_builder_reroute_gw.
|
||||
override fun tun_builder_add_dns_server(address: String, ipv6: Boolean): Boolean {
|
||||
Log.d(TAG, "tun_builder_add_dns_server: $address, $ipv6")
|
||||
configBuilder.addDnsServer(parseInetAddress(address))
|
||||
return true
|
||||
}
|
||||
|
||||
// Optional callback that indicates whether traffic of a certain
|
||||
// address family (AF_INET or AF_INET6) should be
|
||||
// blocked or allowed, to prevent unencrypted packet leakage when
|
||||
// the tunnel is IPv4-only/IPv6-only, but the local machine
|
||||
// has connectivity with the other protocol to the internet.
|
||||
// Controlled by "block-ipv6" and block-ipv6 config var.
|
||||
// If addresses are added for a family this setting should be
|
||||
// ignored for that family
|
||||
// See also Android's VPNService.Builder.allowFamily method
|
||||
/* override fun tun_builder_set_allow_family(af: Int, allow: Boolean): Boolean {
|
||||
Log.d(TAG, "tun_builder_set_allow_family: $af, $allow")
|
||||
return true
|
||||
} */
|
||||
|
||||
// Callback to set address of remote server
|
||||
// Never called more than once per tun_builder session.
|
||||
override fun tun_builder_set_remote_address(address: String, ipv6: Boolean): Boolean {
|
||||
Log.d(TAG, "tun_builder_set_remote_address: $address, $ipv6")
|
||||
return true
|
||||
}
|
||||
|
||||
// Optional callback that indicates OSI layer, should be 2 or 3.
|
||||
// Defaults to 3.
|
||||
override fun tun_builder_set_layer(layer: Int): Boolean {
|
||||
Log.d(TAG, "tun_builder_set_layer: $layer")
|
||||
return layer == 3
|
||||
}
|
||||
|
||||
// Callback to set the session name
|
||||
// Never called more than once per tun_builder session.
|
||||
override fun tun_builder_set_session_name(name: String): Boolean {
|
||||
Log.d(TAG, "tun_builder_set_session_name: $name")
|
||||
return true
|
||||
}
|
||||
|
||||
// Callback to establish the VPN tunnel, returning a file descriptor
|
||||
// to the tunnel, which the caller will henceforth own. Returns -1
|
||||
// if the tunnel could not be established.
|
||||
// Always called last after tun_builder session has been configured.
|
||||
override fun tun_builder_establish(): Int {
|
||||
Log.d(TAG, "tun_builder_establish")
|
||||
return establish(configBuilder)
|
||||
}
|
||||
|
||||
// Callback to reroute default gateway to VPN interface.
|
||||
// ipv4 is true if the default route to be added should be IPv4.
|
||||
// ipv6 is true if the default route to be added should be IPv6.
|
||||
// flags are defined in RGWFlags (rgwflags.hpp).
|
||||
// Never called more than once per tun_builder session.
|
||||
override fun tun_builder_reroute_gw(ipv4: Boolean, ipv6: Boolean, flags: Long): Boolean {
|
||||
Log.d(TAG, "tun_builder_reroute_gw: $ipv4, $ipv6, $flags")
|
||||
if ((flags and EMULATED_EXCLUDE_ROUTES.toLong()) != 0L) return true
|
||||
if (ipv4) {
|
||||
configBuilder.addRoute(InetNetwork("0.0.0.0", 0))
|
||||
}
|
||||
if (ipv6) {
|
||||
configBuilder.addRoute(InetNetwork("::", 0))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Callback to add search domain to DNS resolver
|
||||
// May be called more than once per tun_builder session
|
||||
// See tun_builder_add_dns_server above for description of
|
||||
// reroute_dns parameter.
|
||||
// Guaranteed to be called after tun_builder_reroute_gw.
|
||||
override fun tun_builder_add_search_domain(domain: String): Boolean {
|
||||
Log.d(TAG, "tun_builder_add_search_domain: $domain")
|
||||
configBuilder.setSearchDomain(domain)
|
||||
return true
|
||||
}
|
||||
|
||||
// Callback to set the HTTP proxy
|
||||
// Never called more than once per tun_builder session.
|
||||
override fun tun_builder_set_proxy_http(host: String, port: Int): Boolean {
|
||||
Log.d(TAG, "tun_builder_set_proxy_http: $host, $port")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
try {
|
||||
configBuilder.setHttpProxy(ProxyInfo.buildDirectProxy(host, port))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Could not set proxy: ${e.message}")
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Callback to set the HTTPS proxy
|
||||
// Never called more than once per tun_builder session.
|
||||
override fun tun_builder_set_proxy_https(host: String, port: Int): Boolean {
|
||||
Log.d(TAG, "tun_builder_set_proxy_https: $host, $port")
|
||||
return false
|
||||
}
|
||||
|
||||
// When the exclude local network option is enabled this
|
||||
// function is called to get a list of local networks so routes
|
||||
// to exclude them from the VPN network are generated
|
||||
// This should be a list of CIDR networks (e.g. 192.168.0.0/24)
|
||||
override fun tun_builder_get_local_networks(ipv6: Boolean): ClientAPI_StringVec {
|
||||
Log.d(TAG, "tun_builder_get_local_networks: $ipv6")
|
||||
val networks = ClientAPI_StringVec()
|
||||
for (address in getLocalNetworks(ipv6)) {
|
||||
networks.add(address.toString())
|
||||
}
|
||||
return networks
|
||||
}
|
||||
|
||||
// Optional callback to set default value for route metric.
|
||||
// Guaranteed to be called before other methods that deal
|
||||
// with routes such as tun_builder_add_route and
|
||||
// tun_builder_reroute_gw. Route metric is ignored
|
||||
// if < 0.
|
||||
/* override fun tun_builder_set_route_metric_default(metric: Int): Boolean {
|
||||
Log.d(TAG, "tun_builder_set_route_metric_default: $metric")
|
||||
return super.tun_builder_set_route_metric_default(metric)
|
||||
} */
|
||||
|
||||
// Callback to add a host which should bypass the proxy
|
||||
// May be called more than once per tun_builder session
|
||||
/* override fun tun_builder_add_proxy_bypass(bypass_host: String): Boolean {
|
||||
Log.d(TAG, "tun_builder_add_proxy_bypass: $bypass_host")
|
||||
return super.tun_builder_add_proxy_bypass(bypass_host)
|
||||
} */
|
||||
|
||||
// Callback to set the proxy "Auto Config URL"
|
||||
// Never called more than once per tun_builder session.
|
||||
/* override fun tun_builder_set_proxy_auto_config_url(url: String): Boolean {
|
||||
Log.d(TAG, "tun_builder_set_proxy_auto_config_url: $url")
|
||||
return super.tun_builder_set_proxy_auto_config_url(url)
|
||||
} */
|
||||
|
||||
// Callback to add Windows WINS server to VPN interface.
|
||||
// WINS server addresses are always IPv4.
|
||||
// May be called more than once per tun_builder session.
|
||||
// Guaranteed to be called after tun_builder_reroute_gw.
|
||||
/* override fun tun_builder_add_wins_server(address: String): Boolean {
|
||||
Log.d(TAG, "tun_builder_add_wins_server: $address")
|
||||
return super.tun_builder_add_wins_server(address)
|
||||
} */
|
||||
|
||||
// Optional callback to set a DNS suffix on tun/tap adapter.
|
||||
// Currently only implemented on Windows, where it will
|
||||
// set the "Connection-specific DNS Suffix" property on
|
||||
// the TAP driver.
|
||||
/* override fun tun_builder_set_adapter_domain_suffix(name: String): Boolean {
|
||||
Log.d(TAG, "tun_builder_set_adapter_domain_suffix: $name")
|
||||
return super.tun_builder_set_adapter_domain_suffix(name)
|
||||
} */
|
||||
|
||||
// Return true if tun interface may be persisted, i.e. rolled
|
||||
// into a new session with properties untouched. This method
|
||||
// is only called after all other tests of persistence
|
||||
// allowability succeed, therefore it can veto persistence.
|
||||
// If persistence is ultimately enabled,
|
||||
// tun_builder_establish_lite() will be called. Otherwise,
|
||||
// tun_builder_establish() will be called.
|
||||
/* override fun tun_builder_persist(): Boolean {
|
||||
Log.d(TAG, "tun_builder_persist")
|
||||
return super.tun_builder_persist()
|
||||
} */
|
||||
|
||||
// Indicates a reconnection with persisted tun state.
|
||||
/* override fun tun_builder_establish_lite() {
|
||||
Log.d(TAG, "tun_builder_establish_lite")
|
||||
super.tun_builder_establish_lite()
|
||||
} */
|
||||
|
||||
// Indicates that tunnel is being torn down.
|
||||
// If disconnect == true, then the teardown is occurring
|
||||
// prior to final disconnect.
|
||||
/* override fun tun_builder_teardown(disconnect: Boolean) {
|
||||
Log.d(TAG, "tun_builder_teardown: $disconnect")
|
||||
super.tun_builder_teardown(disconnect)
|
||||
} */
|
||||
|
||||
/**************************************************************************
|
||||
* Connection control methods
|
||||
**************************************************************************/
|
||||
|
||||
// Parse OpenVPN configuration file.
|
||||
override fun eval_config(arg0: ClientAPI_Config): ClientAPI_EvalConfig {
|
||||
Log.d(TAG, "eval_config")
|
||||
return super.eval_config(arg0)
|
||||
}
|
||||
|
||||
// Primary VPN client connect method, doesn't return until disconnect.
|
||||
// Should be called by a worker thread. This method will make callbacks
|
||||
// to event() and log() functions. Make sure to call eval_config()
|
||||
// and possibly provide_creds() as well before this function.
|
||||
override fun connect(): ClientAPI_Status {
|
||||
Log.d(TAG, "connect")
|
||||
return super.connect()
|
||||
}
|
||||
|
||||
// Callback to "protect" a socket from being routed through the tunnel.
|
||||
// Will be called from the thread executing connect().
|
||||
// The remote and ipv6 are the remote host this socket will connect to
|
||||
override fun socket_protect(socket: Int, remote: String, ipv6: Boolean): Boolean {
|
||||
Log.d(TAG, "socket_protect: $socket, $remote, $ipv6")
|
||||
return protect(socket)
|
||||
}
|
||||
|
||||
// Stop the client. Only meaningful when connect() is running.
|
||||
// May be called asynchronously from a different thread
|
||||
// when connect() is running.
|
||||
override fun stop() {
|
||||
Log.d(TAG, "stop")
|
||||
super.stop()
|
||||
}
|
||||
|
||||
// Pause the client -- useful to avoid continuous reconnection attempts
|
||||
// when network is down. May be called from a different thread
|
||||
// when connect() is running.
|
||||
override fun pause(reason: String) {
|
||||
Log.d(TAG, "pause: $reason")
|
||||
super.pause(reason)
|
||||
}
|
||||
|
||||
// Resume the client after it has been paused. May be called from a
|
||||
// different thread when connect() is running.
|
||||
override fun resume() {
|
||||
Log.d(TAG, "resume")
|
||||
super.resume()
|
||||
}
|
||||
|
||||
// Do a disconnect/reconnect cycle n seconds from now. May be called
|
||||
// from a different thread when connect() is running.
|
||||
override fun reconnect(seconds: Int) {
|
||||
Log.d(TAG, "reconnect: $seconds")
|
||||
super.reconnect(seconds)
|
||||
}
|
||||
|
||||
// When a connection is close to timeout, the core will call this
|
||||
// method. If it returns false, the core will disconnect with a
|
||||
// CONNECTION_TIMEOUT event. If true, the core will enter a PAUSE
|
||||
// state.
|
||||
override fun pause_on_connection_timeout(): Boolean {
|
||||
Log.d(TAG, "pause_on_connection_timeout")
|
||||
return false
|
||||
}
|
||||
|
||||
// Return information about the most recent connection. Should be called
|
||||
// after an event of type "CONNECTED".
|
||||
/* override fun connection_info(): ClientAPI_ConnectionInfo {
|
||||
Log.d(TAG, "connection_info")
|
||||
return super.connection_info()
|
||||
} */
|
||||
|
||||
/**************************************************************************
|
||||
* Status callbacks
|
||||
**************************************************************************/
|
||||
|
||||
// Callback for delivering events during connect() call.
|
||||
// Will be called from the thread executing connect().
|
||||
override fun event(event: ClientAPI_Event) {
|
||||
val name = event.name
|
||||
val info = event.info
|
||||
Log.d(TAG, "OpenVpn event: $name: $info")
|
||||
when (name) {
|
||||
"COMPRESSION_ENABLED", "WARN" -> Log.w(TAG, "$name: $info")
|
||||
"CONNECTED" -> state.value = CONNECTED
|
||||
"DISCONNECTED" -> state.value = DISCONNECTED
|
||||
"RECONNECTING" -> {
|
||||
state.getAndUpdate { state ->
|
||||
if (state == DISCONNECTED || state == CONNECTED) RECONNECTING
|
||||
else state
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event.error || event.fatal) {
|
||||
state.value = DISCONNECTED
|
||||
onError("OpenVpn ${if (event.error) "ERROR" else "FATAL"}: $name: $info")
|
||||
}
|
||||
}
|
||||
|
||||
// Callback for logging.
|
||||
// Will be called from the thread executing connect().
|
||||
override fun log(arg0: ClientAPI_LogInfo) {
|
||||
arg0.text.dropLastWhile { it == '\n' }.let {
|
||||
Log.d(TAG, "OpenVpnLog: $it")
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* Stats methods
|
||||
**************************************************************************/
|
||||
|
||||
// return transport stats only
|
||||
override fun transport_stats(): ClientAPI_TransportStats {
|
||||
Log.d(TAG, "transport_stats")
|
||||
return super.transport_stats()
|
||||
}
|
||||
|
||||
// return a stats value, index should be >= 0 and < stats_n()
|
||||
/* override fun stats_value(index: Int): Long {
|
||||
Log.d(TAG, "stats_value: $index")
|
||||
return super.stats_value(index)
|
||||
} */
|
||||
|
||||
// return all stats in a bundle
|
||||
/* override fun stats_bundle(): ClientAPI_LLVector {
|
||||
Log.d(TAG, "stats_bundle")
|
||||
return super.stats_bundle()
|
||||
} */
|
||||
|
||||
// return tun stats only
|
||||
/* override fun tun_stats(): ClientAPI_InterfaceStats {
|
||||
Log.d(TAG, "tun_stats")
|
||||
return super.tun_stats()
|
||||
} */
|
||||
|
||||
// post control channel message
|
||||
/* override fun post_cc_msg(msg: String) {
|
||||
Log.d(TAG, "post_cc_msg: $msg")
|
||||
super.post_cc_msg(msg)
|
||||
} */
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package org.amnezia.vpn.protocol.openvpn
|
||||
|
||||
import org.amnezia.vpn.protocol.ProtocolConfig
|
||||
|
||||
private const val OPENVPN_DEFAULT_MTU = 1500
|
||||
|
||||
class OpenVpnConfig private constructor(
|
||||
protocolConfigBuilder: ProtocolConfig.Builder
|
||||
) : ProtocolConfig(protocolConfigBuilder) {
|
||||
|
||||
class Builder : ProtocolConfig.Builder(false) {
|
||||
override var mtu: Int = OPENVPN_DEFAULT_MTU
|
||||
|
||||
override fun build(): OpenVpnConfig = configBuild().run { OpenVpnConfig(this@Builder) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
inline fun build(block: Builder.() -> Unit): OpenVpnConfig = Builder().apply(block).build()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
plugins {
|
||||
id(libs.plugins.android.library.get().pluginId)
|
||||
id(libs.plugins.kotlin.android.get().pluginId)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "org.amnezia.vpn.protocol"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":utils"))
|
||||
implementation(libs.androidx.annotation)
|
||||
implementation(libs.kotlinx.coroutines)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
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)
|
||||
class VpnException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
|
||||
@@ -0,0 +1,191 @@
|
||||
package org.amnezia.vpn.protocol
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.net.IpPrefix
|
||||
import android.net.VpnService
|
||||
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
|
||||
import org.json.JSONObject
|
||||
|
||||
private const val TAG = "Protocol"
|
||||
|
||||
const val VPN_SESSION_NAME = "AmneziaVPN"
|
||||
|
||||
private const val SPLIT_TUNNEL_DISABLE = 0
|
||||
private const val SPLIT_TUNNEL_INCLUDE = 1
|
||||
private const val SPLIT_TUNNEL_EXCLUDE = 2
|
||||
|
||||
abstract class Protocol {
|
||||
|
||||
abstract val statistics: Statistics
|
||||
protected lateinit var state: MutableStateFlow<ProtocolState>
|
||||
protected lateinit var onError: (String) -> Unit
|
||||
|
||||
open fun initialize(context: Context, state: MutableStateFlow<ProtocolState>, onError: (String) -> Unit) {
|
||||
this.state = state
|
||||
this.onError = onError
|
||||
}
|
||||
|
||||
abstract fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean)
|
||||
|
||||
abstract fun stopVpn()
|
||||
|
||||
abstract fun reconnectVpn(vpnBuilder: Builder)
|
||||
|
||||
protected fun ProtocolConfig.Builder.configSplitTunneling(config: JSONObject) {
|
||||
if (!allowSplitTunneling) {
|
||||
Log.i(TAG, "Global address split tunneling is prohibited, " +
|
||||
"only tunneling from the protocol config is used")
|
||||
return
|
||||
}
|
||||
|
||||
val splitTunnelType = config.optInt("splitTunnelType")
|
||||
if (splitTunnelType == SPLIT_TUNNEL_DISABLE) return
|
||||
val splitTunnelSites = config.getJSONArray("splitTunnelSites")
|
||||
val addressHandlerFunc = when (splitTunnelType) {
|
||||
SPLIT_TUNNEL_INCLUDE -> ::includeAddress
|
||||
SPLIT_TUNNEL_EXCLUDE -> ::excludeAddress
|
||||
|
||||
else -> throw BadConfigException("Unexpected value of the 'splitTunnelType' parameter: $splitTunnelType")
|
||||
}
|
||||
|
||||
for (i in 0 until splitTunnelSites.length()) {
|
||||
val address = InetNetwork.parse(splitTunnelSites.getString(i))
|
||||
addressHandlerFunc(address)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun buildVpnInterface(config: ProtocolConfig, vpnBuilder: Builder) {
|
||||
vpnBuilder.setSession(VPN_SESSION_NAME)
|
||||
|
||||
for (addr in config.addresses) {
|
||||
Log.d(TAG, "addAddress: $addr")
|
||||
vpnBuilder.addAddress(addr)
|
||||
}
|
||||
|
||||
for (addr in config.dnsServers) {
|
||||
Log.d(TAG, "addDnsServer: $addr")
|
||||
vpnBuilder.addDnsServer(addr)
|
||||
}
|
||||
// fix for Samsung android ignoring DNS servers outside the VPN route range
|
||||
if (Build.BRAND == "samsung") {
|
||||
for (addr in config.dnsServers) {
|
||||
Log.d(TAG, "addRoute: $addr")
|
||||
vpnBuilder.addRoute(InetNetwork(addr))
|
||||
}
|
||||
}
|
||||
|
||||
config.searchDomain?.let {
|
||||
Log.d(TAG, "addSearchDomain: $it")
|
||||
vpnBuilder.addSearchDomain(it)
|
||||
}
|
||||
|
||||
for (addr in config.routes) {
|
||||
Log.d(TAG, "addRoute: $addr")
|
||||
vpnBuilder.addRoute(addr)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
for (addr in config.excludedRoutes) {
|
||||
Log.d(TAG, "excludeRoute: $addr")
|
||||
vpnBuilder.excludeRoute(addr)
|
||||
}
|
||||
}
|
||||
|
||||
for (app in config.excludedApplications) {
|
||||
Log.d(TAG, "addDisallowedApplication: $app")
|
||||
vpnBuilder.addDisallowedApplication(app)
|
||||
}
|
||||
|
||||
Log.d(TAG, "setMtu: ${config.mtu}")
|
||||
vpnBuilder.setMtu(config.mtu)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
config.httpProxy?.let {
|
||||
Log.d(TAG, "setHttpProxy: $it")
|
||||
vpnBuilder.setHttpProxy(it)
|
||||
}
|
||||
}
|
||||
|
||||
if (config.allowAllAF) {
|
||||
Log.d(TAG, "allowFamily")
|
||||
vpnBuilder.allowFamily(OsConstants.AF_INET)
|
||||
vpnBuilder.allowFamily(OsConstants.AF_INET6)
|
||||
}
|
||||
|
||||
Log.d(TAG, "setBlocking: ${config.blockingMode}")
|
||||
vpnBuilder.setBlocking(config.blockingMode)
|
||||
vpnBuilder.setUnderlyingNetworks(null)
|
||||
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<String>()
|
||||
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)
|
||||
private fun VpnService.Builder.addRoute(addr: InetNetwork) = addRoute(addr.address, addr.mask)
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||
private fun VpnService.Builder.excludeRoute(addr: InetNetwork) = excludeRoute(IpPrefix(addr.address, addr.mask))
|
||||
@@ -0,0 +1,172 @@
|
||||
package org.amnezia.vpn.protocol
|
||||
|
||||
import android.net.ProxyInfo
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import java.net.InetAddress
|
||||
import org.amnezia.vpn.util.net.InetNetwork
|
||||
import org.amnezia.vpn.util.net.IpRange
|
||||
import org.amnezia.vpn.util.net.IpRangeSet
|
||||
|
||||
open class ProtocolConfig protected constructor(
|
||||
val addresses: Set<InetNetwork>,
|
||||
val dnsServers: Set<InetAddress>,
|
||||
val searchDomain: String?,
|
||||
val routes: Set<InetNetwork>,
|
||||
val excludedRoutes: Set<InetNetwork>,
|
||||
val includedAddresses: Set<InetNetwork>,
|
||||
val excludedAddresses: Set<InetNetwork>,
|
||||
val excludedApplications: Set<String>,
|
||||
val httpProxy: ProxyInfo?,
|
||||
val allowAllAF: Boolean,
|
||||
val blockingMode: Boolean,
|
||||
val mtu: Int
|
||||
) {
|
||||
|
||||
protected constructor(builder: Builder) : this(
|
||||
builder.addresses,
|
||||
builder.dnsServers,
|
||||
builder.searchDomain,
|
||||
builder.routes,
|
||||
builder.excludedRoutes,
|
||||
builder.includedAddresses,
|
||||
builder.excludedAddresses,
|
||||
builder.excludedApplications,
|
||||
builder.httpProxy,
|
||||
builder.allowAllAF,
|
||||
builder.blockingMode,
|
||||
builder.mtu
|
||||
)
|
||||
|
||||
open class Builder(blockingMode: Boolean) {
|
||||
internal val addresses: MutableSet<InetNetwork> = hashSetOf()
|
||||
internal val dnsServers: MutableSet<InetAddress> = hashSetOf()
|
||||
internal val routes: MutableSet<InetNetwork> = hashSetOf()
|
||||
internal val excludedRoutes: MutableSet<InetNetwork> = hashSetOf()
|
||||
internal val includedAddresses: MutableSet<InetNetwork> = hashSetOf()
|
||||
internal val excludedAddresses: MutableSet<InetNetwork> = hashSetOf()
|
||||
internal val excludedApplications: MutableSet<String> = hashSetOf()
|
||||
|
||||
internal var searchDomain: String? = null
|
||||
private set
|
||||
|
||||
internal var httpProxy: ProxyInfo? = null
|
||||
private set
|
||||
|
||||
internal var allowAllAF: Boolean = false
|
||||
private set
|
||||
|
||||
internal var blockingMode: Boolean = blockingMode
|
||||
private set
|
||||
|
||||
internal var allowSplitTunneling: Boolean = true
|
||||
private set
|
||||
|
||||
open var mtu: Int = 0
|
||||
protected set
|
||||
|
||||
fun addAddress(addr: InetNetwork) = apply { this.addresses += addr }
|
||||
fun addAddresses(addresses: Collection<InetNetwork>) = apply { this.addresses += addresses }
|
||||
fun clearAddresses() = apply { this.addresses.clear() }
|
||||
|
||||
fun addDnsServer(dnsServer: InetAddress) = apply { this.dnsServers += dnsServer }
|
||||
fun addDnsServers(dnsServers: Collection<InetAddress>) = apply { this.dnsServers += dnsServers }
|
||||
|
||||
fun setSearchDomain(domain: String) = apply { this.searchDomain = domain }
|
||||
|
||||
fun addRoute(route: InetNetwork) = apply { this.routes += route }
|
||||
fun addRoutes(routes: Collection<InetNetwork>) = apply { this.routes += routes }
|
||||
fun removeRoute(route: InetNetwork) = apply { this.routes.remove(route) }
|
||||
fun clearRoutes() = apply { this.routes.clear() }
|
||||
|
||||
fun excludeRoute(route: InetNetwork) = apply { this.excludedRoutes += route }
|
||||
fun excludeRoutes(routes: Collection<InetNetwork>) = apply { this.excludedRoutes += routes }
|
||||
|
||||
fun includeAddress(addr: InetNetwork) = apply { this.includedAddresses += addr }
|
||||
fun includeAddresses(addresses: Collection<InetNetwork>) = apply { this.includedAddresses += addresses }
|
||||
|
||||
fun excludeAddress(addr: InetNetwork) = apply { this.excludedAddresses += addr }
|
||||
fun excludeAddresses(addresses: Collection<InetNetwork>) = apply { this.excludedAddresses += addresses }
|
||||
|
||||
fun excludeApplication(application: String) = apply { this.excludedApplications += application }
|
||||
fun excludeApplications(applications: Collection<String>) = apply { this.excludedApplications += applications }
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
fun setHttpProxy(httpProxy: ProxyInfo) = apply { this.httpProxy = httpProxy }
|
||||
|
||||
fun setAllowAllAF(allowAllAF: Boolean) = apply { this.allowAllAF = allowAllAF }
|
||||
|
||||
fun setBlockingMode(blockingMode: Boolean) = apply { this.blockingMode = blockingMode }
|
||||
|
||||
fun disableSplitTunneling() = apply { this.allowSplitTunneling = false }
|
||||
|
||||
fun setMtu(mtu: Int) = apply { this.mtu = mtu }
|
||||
|
||||
private fun processSplitTunneling() {
|
||||
if (includedAddresses.isNotEmpty() && excludedAddresses.isNotEmpty()) {
|
||||
throw BadConfigException("Config contains addresses for inclusive and exclusive split tunneling at the same time")
|
||||
}
|
||||
|
||||
if (includedAddresses.isNotEmpty()) {
|
||||
// remove default routes, if any
|
||||
removeRoute(InetNetwork("0.0.0.0", 0))
|
||||
removeRoute(InetNetwork("::", 0))
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
// for older versions of Android, add the default route to the excluded routes
|
||||
// to correctly build the excluded subnets list later
|
||||
excludeRoute(InetNetwork("0.0.0.0", 0))
|
||||
}
|
||||
addRoutes(includedAddresses)
|
||||
} else if (excludedAddresses.isNotEmpty()) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
// default routes are required for split tunneling in newer versions of Android
|
||||
addRoute(InetNetwork("0.0.0.0", 0))
|
||||
addRoute(InetNetwork("::", 0))
|
||||
}
|
||||
excludeRoutes(excludedAddresses)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processExcludedRoutes() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
// for older versions of Android, build a list of subnets without excluded routes
|
||||
// and add them to routes
|
||||
val ipRangeSet = IpRangeSet()
|
||||
ipRangeSet.remove(IpRange("127.0.0.0", 8))
|
||||
excludedRoutes.forEach {
|
||||
ipRangeSet.remove(IpRange(it))
|
||||
}
|
||||
// remove default routes, if any
|
||||
removeRoute(InetNetwork("0.0.0.0", 0))
|
||||
removeRoute(InetNetwork("::", 0))
|
||||
ipRangeSet.subnets().forEach(::addRoute)
|
||||
addRoute(InetNetwork("2000::", 3))
|
||||
}
|
||||
}
|
||||
|
||||
private fun validate() {
|
||||
val errorMessage = StringBuilder()
|
||||
|
||||
with(errorMessage) {
|
||||
if (addresses.isEmpty()) appendLine("VPN interface network address not specified.")
|
||||
if (routes.isEmpty()) appendLine("VPN interface route not specified.")
|
||||
if (mtu == 0) appendLine("MTU not set.")
|
||||
}
|
||||
|
||||
if (errorMessage.isNotEmpty()) throw BadConfigException(errorMessage.toString())
|
||||
}
|
||||
|
||||
protected fun configBuild() {
|
||||
processSplitTunneling()
|
||||
processExcludedRoutes()
|
||||
validate()
|
||||
}
|
||||
|
||||
open fun build(): ProtocolConfig = configBuild().run { ProtocolConfig(this@Builder) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
inline fun build(blockingMode: Boolean, block: Builder.() -> Unit): ProtocolConfig =
|
||||
Builder(blockingMode).apply(block).build()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.amnezia.vpn.protocol
|
||||
|
||||
// keep synchronized with client/platforms/android/android_controller.h ConnectionState
|
||||
enum class ProtocolState {
|
||||
CONNECTED,
|
||||
CONNECTING,
|
||||
DISCONNECTED,
|
||||
DISCONNECTING,
|
||||
RECONNECTING,
|
||||
UNKNOWN
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.amnezia.vpn.protocol
|
||||
|
||||
import android.os.Bundle
|
||||
|
||||
private const val RX_BYTES_KEY = "rxBytes"
|
||||
private const val TX_BYTES_KEY = "txBytes"
|
||||
|
||||
@Suppress("DataClassPrivateConstructor")
|
||||
data class Statistics private constructor(
|
||||
val rxBytes: Long = 0L,
|
||||
val txBytes: Long = 0L
|
||||
) {
|
||||
|
||||
private constructor(builder: Builder) : this(builder.rxBytes, builder.txBytes)
|
||||
|
||||
@Suppress("SuspiciousEqualsCombination")
|
||||
fun isEmpty(): Boolean = this === EMPTY_STATISTICS || this == EMPTY_STATISTICS
|
||||
|
||||
class Builder {
|
||||
var rxBytes: Long = 0L
|
||||
private set
|
||||
|
||||
var txBytes: Long = 0L
|
||||
private set
|
||||
|
||||
fun setRxBytes(rxBytes: Long) = apply { this.rxBytes = rxBytes }
|
||||
fun setTxBytes(txBytes: Long) = apply { this.txBytes = txBytes }
|
||||
|
||||
fun build(): Statistics =
|
||||
if (rxBytes + txBytes == 0L) EMPTY_STATISTICS
|
||||
else Statistics(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val EMPTY_STATISTICS: Statistics = Statistics()
|
||||
|
||||
inline fun build(block: Builder.() -> Unit): Statistics = Builder().apply(block).build()
|
||||
}
|
||||
}
|
||||
|
||||
fun Bundle.putStatistics(statistics: Statistics) {
|
||||
putLong(RX_BYTES_KEY, statistics.rxBytes)
|
||||
putLong(TX_BYTES_KEY, statistics.txBytes)
|
||||
}
|
||||
|
||||
fun Bundle.getStatistics(): Statistics =
|
||||
Statistics.build {
|
||||
setRxBytes(getLong(RX_BYTES_KEY))
|
||||
setTxBytes(getLong(TX_BYTES_KEY))
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.amnezia.vpn.protocol
|
||||
|
||||
import android.os.Bundle
|
||||
|
||||
private const val STATE_KEY = "state"
|
||||
|
||||
@Suppress("DataClassPrivateConstructor")
|
||||
data class Status private constructor(
|
||||
val state: ProtocolState
|
||||
) {
|
||||
private constructor(builder: Builder) : this(builder.state)
|
||||
|
||||
class Builder {
|
||||
lateinit var state: ProtocolState
|
||||
private set
|
||||
|
||||
fun setState(state: ProtocolState) = apply { this.state = state }
|
||||
|
||||
fun build(): Status = Status(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
inline fun build(block: Builder.() -> Unit): Status = Builder().apply(block).build()
|
||||
}
|
||||
}
|
||||
|
||||
fun Bundle.putStatus(status: Status) {
|
||||
putInt(STATE_KEY, status.state.ordinal)
|
||||
}
|
||||
|
||||
fun Bundle.getStatus(): Status =
|
||||
Status.build {
|
||||
setState(ProtocolState.entries[getInt(STATE_KEY)])
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
plugins {
|
||||
id(libs.plugins.android.library.get().pluginId)
|
||||
id("property-delegate")
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
|
||||
val qtAndroidDir: String by gradleProperties
|
||||
|
||||
android {
|
||||
namespace = "org.qtproject.qt.android.binding"
|
||||
|
||||
sourceSets {
|
||||
getByName("main") {
|
||||
java.setSrcDirs(listOf("$qtAndroidDir/src"))
|
||||
res.setSrcDirs(listOf("$qtAndroidDir/res"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar", "*.aar"))))
|
||||
}
|
||||
File renamed without changes.
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".CameraActivity">
|
||||
|
||||
<androidx.camera.view.PreviewView
|
||||
android:id="@+id/viewFinder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.camera.view.PreviewView
|
||||
android:id="@+id/viewFinder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</FrameLayout>
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+8
-7
@@ -1,16 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.AppCompat.Translucent" parent="Theme.AppCompat.Dialog">
|
||||
<style name="NoActionBar">
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
<style name="Translucent" parent="NoActionBar">
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:windowFrame">@null</item>
|
||||
<item name="android:windowIsFloating">true</item>
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="android:windowAnimationStyle">@null</item>
|
||||
<item name="android:backgroundDimEnabled">false</item>
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowCloseOnTouchOutside">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
<style name="ThemeOverlay.AppCompat.DayNight" parent="ThemeOverlay.AppCompat.Light"/>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<full-backup-content>
|
||||
<exclude domain="sharedpref" path="." />
|
||||
</full-backup-content>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<exclude domain="sharedpref" path="." />
|
||||
</cloud-backup>
|
||||
<device-transfer>
|
||||
<exclude domain="sharedpref" path="." />
|
||||
</device-transfer>
|
||||
</data-extraction-rules>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<cache-path name="cache" path="/" />
|
||||
</paths>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<files-path name="files_path" path="/"/>
|
||||
</paths>
|
||||
@@ -1,132 +0,0 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1000,
|
||||
"identityHash": "14b379f7776710b79b9d617090efe40e",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "Profile",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `host` TEXT NOT NULL, `remotePort` INTEGER NOT NULL, `password` TEXT NOT NULL, `method` TEXT NOT NULL, `remoteDns` TEXT NOT NULL, `udpdns` INTEGER NOT NULL, `ipv6` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "host",
|
||||
"columnName": "host",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "remotePort",
|
||||
"columnName": "remotePort",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "password",
|
||||
"columnName": "password",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "method",
|
||||
"columnName": "method",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "remoteDns",
|
||||
"columnName": "remoteDns",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "udpdns",
|
||||
"columnName": "udpdns",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "ipv6",
|
||||
"columnName": "ipv6",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tx",
|
||||
"columnName": "tx",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "rx",
|
||||
"columnName": "rx",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userOrder",
|
||||
"columnName": "userOrder",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "KeyValuePair",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `valueType` INTEGER NOT NULL, `value` BLOB NOT NULL, PRIMARY KEY(`key`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "key",
|
||||
"columnName": "key",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "valueType",
|
||||
"columnName": "valueType",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "value",
|
||||
"columnName": "value",
|
||||
"affinity": "BLOB",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"key"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '14b379f7776710b79b9d617090efe40e')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 3,
|
||||
"identityHash": "f1aab1fb633378621635c344dbc8ac7b",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "KeyValuePair",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `valueType` INTEGER NOT NULL, `value` BLOB NOT NULL, PRIMARY KEY(`key`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "key",
|
||||
"columnName": "key",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "valueType",
|
||||
"columnName": "valueType",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "value",
|
||||
"columnName": "value",
|
||||
"affinity": "BLOB",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"key"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f1aab1fb633378621635c344dbc8ac7b')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,6 @@ pluginManagement {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
// for jsocks todo: remove after finish refactoring
|
||||
maven("https://jitpack.io")
|
||||
}
|
||||
|
||||
includeBuild("./gradle/plugins")
|
||||
@@ -18,22 +16,26 @@ dependencyResolutionManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
// for jsocks todo: remove after finish refactoring
|
||||
maven("https://jitpack.io")
|
||||
}
|
||||
}
|
||||
|
||||
includeBuild("./gradle/plugins")
|
||||
|
||||
plugins {
|
||||
id("com.android.settings") version "8.1.3"
|
||||
id("com.android.settings") version "8.2.0"
|
||||
id("settings-property-delegate")
|
||||
}
|
||||
|
||||
rootProject.name = "AmneziaVPN"
|
||||
rootProject.buildFileName = "build.gradle.kts"
|
||||
|
||||
include(":shadowsocks")
|
||||
include(":qt")
|
||||
include(":utils")
|
||||
include(":protocolApi")
|
||||
include(":wireguard")
|
||||
include(":awg")
|
||||
include(":openvpn")
|
||||
include(":cloak")
|
||||
|
||||
// get values from gradle or local properties
|
||||
val androidBuildToolsVersion: String by gradleProperties
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
//apply plugin: 'com.novoda.bintray-release'
|
||||
|
||||
android {
|
||||
buildFeatures {
|
||||
aidl true
|
||||
androidResources true
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
namespace "org.amnezia.vpn.shadowsocks.core"
|
||||
versionCode 1
|
||||
versionName "1.0.0"
|
||||
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
}
|
||||
|
||||
//def lifecycleVersion = '2.0.0'
|
||||
def roomVersion = "2.4.3"
|
||||
//def preferencexVersion = '1.0.0'
|
||||
dependencies {
|
||||
|
||||
implementation 'androidx.lifecycle:lifecycle-common-java8:2.4.0'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.30-M1"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
|
||||
|
||||
implementation "androidx.core:core-ktx:1.2.0"
|
||||
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-core-ktx:2.5.1"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
|
||||
implementation "androidx.room:room-runtime:$roomVersion" // runtime
|
||||
implementation "androidx.preference:preference:1.1.0"
|
||||
implementation "androidx.work:work-runtime-ktx:2.7.1"
|
||||
implementation "androidx.browser:browser:1.3.0-alpha01"
|
||||
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
|
||||
implementation "com.google.android.material:material:1.2.0-alpha05"
|
||||
implementation "com.google.code.gson:gson:2.8.5"
|
||||
|
||||
implementation "dnsjava:dnsjava:2.1.9"
|
||||
implementation "com.github.kruton:jsocks:1.0.0"
|
||||
implementation "com.afollestad.material-dialogs:core:2.6.0"
|
||||
// api "com.takisoft.preferencex:preferencex:1.0.0"
|
||||
implementation 'com.takisoft.preferencex:preferencex:1.1.0'
|
||||
api 'com.github.kruton:jsocks:1.0.0'
|
||||
|
||||
kapt "androidx.room:room-compiler:$roomVersion"
|
||||
kapt "androidx.lifecycle:lifecycle-compiler:2.4.0"
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
## ArchLinux install package via pacman: perl-net-cidr-lite
|
||||
use strict;
|
||||
use warnings;
|
||||
use Net::CIDR::Lite;
|
||||
my $cidr = Net::CIDR::Lite->new;
|
||||
while (my $line=<>) {
|
||||
$cidr->add($line);
|
||||
}
|
||||
foreach my $line( @{$cidr->list} ) {
|
||||
print "<item>$line</item>\n";
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- encoding: utf8 -*-
|
||||
|
||||
import sys
|
||||
|
||||
import IPy
|
||||
|
||||
|
||||
def main():
|
||||
china_list_set = IPy.IPSet()
|
||||
for line in sys.stdin:
|
||||
china_list_set.add(IPy.IP(line))
|
||||
|
||||
# 输出结果
|
||||
for ip in china_list_set:
|
||||
print '<item>' + str(ip) + '</item>'
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,121 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import pkgutil
|
||||
import urlparse
|
||||
import socket
|
||||
import logging
|
||||
from argparse import ArgumentParser
|
||||
from datetime import date
|
||||
|
||||
__all__ = ['main']
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('-i', '--input', dest='input', required=True,
|
||||
help='path to gfwlist', metavar='GFWLIST')
|
||||
parser.add_argument('-f', '--file', dest='output', required=True,
|
||||
help='path to output acl', metavar='ACL')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def decode_gfwlist(content):
|
||||
# decode base64 if have to
|
||||
try:
|
||||
return content.decode('base64')
|
||||
except:
|
||||
return content
|
||||
|
||||
|
||||
def get_hostname(something):
|
||||
try:
|
||||
# quite enough for GFW
|
||||
if not something.startswith('http:'):
|
||||
something = 'http://' + something
|
||||
r = urlparse.urlparse(something)
|
||||
return r.hostname
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
return None
|
||||
|
||||
|
||||
def add_domain_to_set(s, something):
|
||||
hostname = get_hostname(something)
|
||||
if hostname is not None:
|
||||
if hostname.startswith('.'):
|
||||
hostname = hostname.lstrip('.')
|
||||
if hostname.endswith('/'):
|
||||
hostname = hostname.rstrip('/')
|
||||
if hostname:
|
||||
s.add(hostname)
|
||||
|
||||
|
||||
def parse_gfwlist(content):
|
||||
gfwlist = content.splitlines(False)
|
||||
domains = set()
|
||||
for line in gfwlist:
|
||||
if line.find('.*') >= 0:
|
||||
continue
|
||||
elif line.find('*') >= 0:
|
||||
line = line.replace('*', '/')
|
||||
if line.startswith('!'):
|
||||
continue
|
||||
elif line.startswith('['):
|
||||
continue
|
||||
elif line.startswith('@'):
|
||||
# ignore white list
|
||||
continue
|
||||
elif line.startswith('||'):
|
||||
add_domain_to_set(domains, line.lstrip('||'))
|
||||
elif line.startswith('|'):
|
||||
add_domain_to_set(domains, line.lstrip('|'))
|
||||
elif line.startswith('.'):
|
||||
add_domain_to_set(domains, line.lstrip('.'))
|
||||
else:
|
||||
add_domain_to_set(domains, line)
|
||||
# TODO: reduce ['www.google.com', 'google.com'] to ['google.com']
|
||||
return domains
|
||||
|
||||
|
||||
def generate_acl(domains):
|
||||
header ="""#
|
||||
# GFW list from https://github.com/gfwlist/gfwlist/blob/master/gfwlist.txt
|
||||
# updated on DATE
|
||||
#
|
||||
|
||||
[bypass_all]
|
||||
|
||||
[proxy_list]
|
||||
|
||||
"""
|
||||
header = header.replace('DATE', str(date.today()))
|
||||
proxy_content = ""
|
||||
ip_content = ""
|
||||
|
||||
for domain in sorted(domains):
|
||||
try:
|
||||
socket.inet_aton(domain)
|
||||
ip_content += (domain + "\n")
|
||||
except socket.error:
|
||||
domain = domain.replace('.', '\.')
|
||||
proxy_content += ('(^|\.)' + domain + '$\n')
|
||||
|
||||
proxy_content = header + ip_content + proxy_content
|
||||
|
||||
return proxy_content
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
with open(args.input, 'rb') as f:
|
||||
content = f.read()
|
||||
content = decode_gfwlist(content)
|
||||
domains = parse_gfwlist(content)
|
||||
acl_content = generate_acl(domains)
|
||||
with open(args.output, 'wb') as f:
|
||||
f.write(acl_content)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<lint>
|
||||
<issue id="ImpliedQuantity" severity="warning" />
|
||||
<issue id="ExtraTranslation" severity="warning" />
|
||||
<issue id="MissingDefaultResource" severity="warning" />
|
||||
<issue id="MissingTranslation" severity="informational" />
|
||||
</lint>
|
||||
@@ -1,131 +0,0 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:ignore="MissingLeanbackLauncher">
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.touchscreen"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:fullBackupContent="@xml/backup_descriptor"
|
||||
android:fullBackupOnly="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:supportsRtl="true"
|
||||
tools:targetApi="n">
|
||||
|
||||
<meta-data
|
||||
android:name="android.webkit.WebView.EnableSafeBrowsing"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="com.google.android.backup.api_key"
|
||||
android:value="AEdPqrEAAAAI_zVxZthz2HDuz9toTvkYvL0L5GA-OjeUIfBeXg" />
|
||||
|
||||
<!-- <service-->
|
||||
<!-- android:name="org.amnezia.vpn.shadowsocks.core.bg.ShadowsocksVpnService"-->
|
||||
<!-- android:directBootAware="true"-->
|
||||
<!-- android:exported="false"-->
|
||||
<!-- android:label="@string/app_name"-->
|
||||
<!-- android:permission="android.permission.BIND_VPN_SERVICE"-->
|
||||
<!-- android:process=":BG"-->
|
||||
<!-- tools:targetApi="n">-->
|
||||
<!-- <intent-filter>-->
|
||||
<!-- <action android:name="android.net.VpnService" />-->
|
||||
<!-- </intent-filter>-->
|
||||
<!-- </service>-->
|
||||
|
||||
<!-- <service-->
|
||||
<!-- android:name="org.amnezia.vpn.shadowsocks.core.bg.TransproxyService"-->
|
||||
<!-- android:directBootAware="true"-->
|
||||
<!-- android:exported="false"-->
|
||||
<!-- android:process=":QtOnlyProcess"-->
|
||||
<!-- tools:targetApi="n" />-->
|
||||
|
||||
<!-- <service-->
|
||||
<!-- android:name="org.amnezia.vpn.shadowsocks.core.bg.ProxyService"-->
|
||||
<!-- android:directBootAware="true"-->
|
||||
<!-- android:exported="false"-->
|
||||
<!-- android:process=":QtOnlyProcess"-->
|
||||
<!-- tools:targetApi="n" />-->
|
||||
|
||||
<!-- <activity-->
|
||||
<!-- android:name="org.amnezia.vpn.shadowsocks.core.VpnRequestActivity"-->
|
||||
<!-- android:excludeFromRecents="true"-->
|
||||
<!-- android:launchMode="singleTask"-->
|
||||
<!-- android:taskAffinity=""-->
|
||||
<!-- android:theme="@style/Theme.AppCompat.Translucent" />-->
|
||||
|
||||
<receiver
|
||||
android:name="org.amnezia.vpn.shadowsocks.core.BootReceiver"
|
||||
android:directBootAware="true"
|
||||
android:enabled="false"
|
||||
android:process=":QtOnlyProcess"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/work/workmanager/src/main/AndroidManifest.xml -->
|
||||
<provider
|
||||
android:name="androidx.work.impl.WorkManagerInitializer"
|
||||
android:authorities="androidx.work.impl.WorkManagerInitializer"
|
||||
tools:node="remove" />
|
||||
|
||||
<service
|
||||
android:name="androidx.work.impl.background.systemalarm.SystemAlarmService"
|
||||
android:directBootAware="true"
|
||||
android:process=":QtOnlyProcess"
|
||||
tools:replace="android:directBootAware" />
|
||||
<service
|
||||
android:name="androidx.work.impl.background.systemjob.SystemJobService"
|
||||
android:directBootAware="true"
|
||||
android:process=":QtOnlyProcess"
|
||||
tools:replace="android:directBootAware" />
|
||||
|
||||
<receiver
|
||||
android:name="androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver"
|
||||
android:directBootAware="true"
|
||||
android:process=":QtOnlyProcess"
|
||||
tools:replace="android:directBootAware" />
|
||||
<receiver
|
||||
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy"
|
||||
android:directBootAware="true"
|
||||
android:process=":QtOnlyProcess"
|
||||
tools:replace="android:directBootAware" />
|
||||
<receiver
|
||||
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy"
|
||||
android:directBootAware="true"
|
||||
android:process=":QtOnlyProcess"
|
||||
tools:replace="android:directBootAware" />
|
||||
<receiver
|
||||
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$StorageNotLowProxy"
|
||||
android:directBootAware="true"
|
||||
android:process=":QtOnlyProcess"
|
||||
tools:replace="android:directBootAware" />
|
||||
<receiver
|
||||
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$NetworkStateProxy"
|
||||
android:directBootAware="true"
|
||||
android:process=":QtOnlyProcess"
|
||||
tools:replace="android:directBootAware" />
|
||||
<receiver
|
||||
android:name="androidx.work.impl.background.systemalarm.RescheduleReceiver"
|
||||
android:directBootAware="true"
|
||||
android:process=":QtOnlyProcess"
|
||||
tools:replace="android:directBootAware" />
|
||||
<receiver
|
||||
android:name="androidx.work.impl.background.systemalarm.ConstraintProxyUpdateReceiver"
|
||||
android:directBootAware="true"
|
||||
android:process=":QtOnlyProcess"
|
||||
tools:replace="android:directBootAware" />
|
||||
</application>
|
||||
</manifest>
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
package org.amnezia.vpn.shadowsocks.core.aidl;
|
||||
|
||||
import org.amnezia.vpn.shadowsocks.core.aidl.IShadowsocksServiceCallback;
|
||||
|
||||
interface IShadowsocksService {
|
||||
int getState();
|
||||
String getProfileName();
|
||||
|
||||
void registerCallback(in IShadowsocksServiceCallback cb);
|
||||
void startListeningForBandwidth(in IShadowsocksServiceCallback cb, long timeout);
|
||||
oneway void stopListeningForBandwidth(in IShadowsocksServiceCallback cb);
|
||||
oneway void unregisterCallback(in IShadowsocksServiceCallback cb);
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
package org.amnezia.vpn.shadowsocks.core.aidl;
|
||||
|
||||
import org.amnezia.vpn.shadowsocks.core.aidl.TrafficStats;
|
||||
|
||||
//"oneway" unexpected. xinlake
|
||||
interface IShadowsocksServiceCallback {
|
||||
oneway void stateChanged(int state, String profileName, String msg);
|
||||
oneway void trafficUpdated(long profileId, in TrafficStats stats);
|
||||
// Traffic data has persisted to database, listener should refetch their data from database
|
||||
oneway void trafficPersisted(long profileId);
|
||||
}
|
||||
|
||||
//oneway interface IShadowsocksServiceCallback {
|
||||
// void stateChanged(int state, String profileName, String msg);
|
||||
// void trafficUpdated(long profileId, in TrafficStats stats);
|
||||
// // Traffic data has persisted to database, listener should refetch their data from database
|
||||
// void trafficPersisted(long profileId);
|
||||
//}
|
||||
Loaded 100 of 363 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user