Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 330c341922 | |||
| 4ddddec978 | |||
| 77d46dfeb3 | |||
| 74caca67ee | |||
| 71ec3336eb | |||
| 2f8f759765 | |||
| f7004f62dd | |||
| 7ea1677df6 | |||
| d5062674f2 | |||
| 33efa56f25 | |||
| 930e227a9e | |||
| bdaa56f734 | |||
| c430cca538 | |||
| fc8dfce90d | |||
| beca12ae40 | |||
| 109512d83e | |||
| 1fb21cfbfc | |||
| ff5fc4cd2a | |||
| 3f600c0088 | |||
| adc07a2b6a | |||
| 7e3134cdbb | |||
| 95b3b0eae3 | |||
| 61c27af17c | |||
| 2fa82a05d9 | |||
| f8408e863a | |||
| 3c0ac8170d | |||
| 591e0fea80 | |||
| 9a67d2684e | |||
| 2d624b3b59 | |||
| cc93898c60 | |||
| 3f265b899e | |||
| 799c3ec6e6 | |||
| 32b355a54e | |||
| 82e831f6d8 | |||
| a19e69ae61 | |||
| 995a60c503 | |||
| 0bb4ad2fbe |
@@ -2,4 +2,3 @@ deploy/data/windows/x64/tap/windows_7/OemVista.inf eol=crlf
|
||||
deploy/data/windows/x64/tap/windows_10/OemVista.inf eol=crlf
|
||||
deploy/data/windows/x32/tap/windows_7/OemVista.inf eol=crlf
|
||||
deploy/data/windows/x32/tap/windows_10/OemVista.inf eol=crlf
|
||||
client/3rd/* linguist-vendored
|
||||
|
||||
@@ -9,8 +9,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
QT_VERSION: 6.5.1
|
||||
QIF_VERSION: 4.6
|
||||
QT_VERSION: 6.5.0
|
||||
QIF_VERSION: 4.5
|
||||
|
||||
steps:
|
||||
- name: 'Install Qt'
|
||||
@@ -63,8 +63,8 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
|
||||
env:
|
||||
QT_VERSION: 6.5.1
|
||||
QIF_VERSION: 4.6
|
||||
QT_VERSION: 6.5.0
|
||||
QIF_VERSION: 4.5
|
||||
BUILD_ARCH: 64
|
||||
|
||||
steps:
|
||||
@@ -124,7 +124,7 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
|
||||
env:
|
||||
QT_VERSION: 6.5.1
|
||||
QT_VERSION: 6.5.0
|
||||
|
||||
steps:
|
||||
- name: 'Setup xcode'
|
||||
@@ -199,8 +199,8 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
|
||||
env:
|
||||
QT_VERSION: 6.5.1
|
||||
QIF_VERSION: 4.6
|
||||
QT_VERSION: 6.5.0
|
||||
QIF_VERSION: 4.5
|
||||
|
||||
steps:
|
||||
- name: 'Setup xcode'
|
||||
@@ -262,7 +262,7 @@ jobs:
|
||||
arch: ['android_x86_64', 'android_x86'] #, 'android_armv7', 'android_arm64_v8a']
|
||||
|
||||
env:
|
||||
QT_VERSION: 6.5.1
|
||||
QT_VERSION: 6.5.0
|
||||
|
||||
steps:
|
||||
- name: 'Install desktop Qt'
|
||||
|
||||
@@ -58,6 +58,3 @@
|
||||
[submodule "client/3rd/mbedtls"]
|
||||
path = client/3rd/mbedtls
|
||||
url = https://github.com/Mbed-TLS/mbedtls.git
|
||||
[submodule "client/3rd/wireguard-go"]
|
||||
path = client/3rd/wireguard-go
|
||||
url = https://github.com/WireGuard/wireguard-go/
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
FROM gitpod/workspace-full-vnc
|
||||
|
||||
USER gitpod
|
||||
|
||||
RUN sudo apt-get -q update \
|
||||
&& sudo apt-get install -yq \
|
||||
build-essential \
|
||||
libgl1-mesa-dev \
|
||||
libgstreamer-gl1.0-0 \
|
||||
libpulse-dev \
|
||||
libsecret-1-dev \
|
||||
libxcb-glx0 \
|
||||
libxcb-icccm4 \
|
||||
libxcb-image0 \
|
||||
libxcb-keysyms1 \
|
||||
libxcb-randr0 \
|
||||
libxcb-render-util0 \
|
||||
libxcb-render0 \
|
||||
libxcb-shape0 \
|
||||
libxcb-shm0 \
|
||||
libxcb-sync1 \
|
||||
libxcb-util1 \
|
||||
libxcb-xfixes0 \
|
||||
libxcb-xinerama0 \
|
||||
libxcb1 \
|
||||
libxkbcommon-dev \
|
||||
libxkbcommon-x11-0 \
|
||||
libxcb-xkb-dev \
|
||||
p7zip-full \
|
||||
&& sudo rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN sudo pip3 install aqtinstall
|
||||
|
||||
ARG QT_VERSION=6.4.1
|
||||
ARG QT_ARCH=gcc_64
|
||||
|
||||
ARG QT_DIR=/opt/qt
|
||||
RUN sudo aqt install-qt --outputdir ${QT_DIR} linux desktop ${QT_VERSION} ${QT_ARCH} --modules \
|
||||
qtremoteobjects \
|
||||
qt5compat \
|
||||
qtshadertools
|
||||
ENV QT_BIN_DIR=${QT_DIR}/${QT_VERSION}/${QT_ARCH}/bin
|
||||
|
||||
ARG QIF_VERSION=4.5
|
||||
ARG QIF_DIR=/opt/qif
|
||||
RUN sudo aqt install-tool --outputdir ${QIF_DIR} linux desktop tools_ifw
|
||||
ENV QIF_BIN_DIR=${QIF_DIR}/Tools/QtInstallerFramework/${QIF_VERSION}/bin
|
||||
@@ -1,8 +0,0 @@
|
||||
tasks:
|
||||
- init: >-
|
||||
deploy/build_linux.sh
|
||||
image:
|
||||
file: .gitpod.Dockerfile
|
||||
vscode:
|
||||
extensions:
|
||||
- llvm-vs-code-extensions.vscode-clangd
|
||||
@@ -2,31 +2,13 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
|
||||
project(${PROJECT} VERSION 3.0.8.1
|
||||
project(${PROJECT} VERSION 3.0.7.2
|
||||
DESCRIPTION "AmneziaVPN"
|
||||
HOMEPAGE_URL "https://amnezia.org/"
|
||||
)
|
||||
set(RELEASE_DATE "2023-07-15")
|
||||
set(RELEASE_DATE "2023-05-15")
|
||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
||||
set(MZ_PLATFORM_NAME "windows")
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
||||
set(MZ_PLATFORM_NAME "macos")
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Android")
|
||||
set(MZ_PLATFORM_NAME "android")
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "iOS")
|
||||
set(MZ_PLATFORM_NAME "ios")
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
|
||||
set(MZ_PLATFORM_NAME "wasm")
|
||||
endif()
|
||||
|
||||
set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if(ANDROID)
|
||||
set(QT_ANDROID_BUILD_ALL_ABIS ON)
|
||||
endif()
|
||||
|
||||
@@ -1,25 +1,16 @@
|
||||
# Amnezia VPN
|
||||
## _The best client for self-hosted VPN_
|
||||
|
||||
[](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml?query=branch:dev)
|
||||
[](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
|
||||
[]
|
||||
|
||||
Amnezia is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
|
||||
Amnezia is a VPN client with the key feature of deploying your own VPN server on you virtual server.
|
||||
|
||||
## Features
|
||||
- Very easy to use - enter your ip address, ssh login and password, and Amnezia will automatically install VPN docker containers to your server and connect to VPN.
|
||||
- Very easy to use - enter your ip address, ssh login and password, and Amnezia client will automatically install VPN docker containers to your server and connect to VPN.
|
||||
- OpenVPN, ShadowSocks, WireGuard, IKEv2 protocols support.
|
||||
- Masking VPN with OpenVPN over Cloak plugin
|
||||
- Split tunneling support - add any sites to client to enable VPN only for them (only for desktops)
|
||||
- Windows, MacOS, Linux, Android, iOS releases.
|
||||
|
||||
## Links
|
||||
[https://amnezia.org](https://amnezia.org) - project website
|
||||
[https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
|
||||
[https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Telegram support channel (English)
|
||||
[https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Telegram support channel (Russian)
|
||||
[https://signal.group/...](https://signal.group/#CjQKIB2gUf8QH_IXnOJMGQWMDjYz9cNfmRQipGWLFiIgc4MwEhAKBONrSiWHvoUFbbD0xwdh) - Signal channel
|
||||
|
||||
## Tech
|
||||
|
||||
AmneziaVPN uses a number of open source projects to work:
|
||||
@@ -129,13 +120,14 @@ You may face compiling issues in QT Creator after you've worked in Android Studi
|
||||
## License
|
||||
GPL v.3
|
||||
|
||||
## Contacts
|
||||
[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
|
||||
[https://amnezia.org](https://amnezia.org) - project website
|
||||
|
||||
## Donate
|
||||
Bitcoin: bc1qn9rhsffuxwnhcuuu4qzrwp4upkrq94xnh8r26u
|
||||
XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3
|
||||
payeer.com: P2561305
|
||||
ko-fi.com: [https://ko-fi.com/amnezia_vpn](https://ko-fi.com/amnezia_vpn)
|
||||
|
||||
|
||||
## etc
|
||||
This project is tested with BrowserStack.
|
||||
We express our gratitude to [BrowserStack](https://www.browserstack.com) for supporting our project.
|
||||
|
||||
@@ -3,6 +3,9 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
set(PROJECT AmneziaVPN)
|
||||
project(${PROJECT})
|
||||
|
||||
set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if(ANDROID)
|
||||
# For a some reason cmake do not applying GNU_SOURCE/BSD_SOURCE flags on Android platform.
|
||||
@@ -16,14 +19,14 @@ set_property(GLOBAL PROPERTY AUTOMOC_TARGETS_FOLDER "Autogen")
|
||||
set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "Autogen")
|
||||
|
||||
set(PACKAGES
|
||||
Widgets Core Gui Network Xml
|
||||
RemoteObjects Quick Svg QuickControls2
|
||||
Core5Compat Concurrent LinguistTools
|
||||
Widgets Core Gui Network Xml
|
||||
RemoteObjects Quick Svg QuickControls2
|
||||
Core5Compat Concurrent
|
||||
)
|
||||
if(IOS)
|
||||
set(PACKAGES
|
||||
${PACKAGES}
|
||||
Multimedia
|
||||
set(PACKAGES
|
||||
${PACKAGES}
|
||||
Multimedia
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -36,25 +39,13 @@ set(LIBS ${LIBS}
|
||||
Qt6::Core5Compat Qt6::Concurrent
|
||||
)
|
||||
if(IOS)
|
||||
set(LIBS
|
||||
${LIBS}
|
||||
Qt6::Multimedia
|
||||
)
|
||||
set(LIBS
|
||||
${LIBS}
|
||||
Qt6::Multimedia
|
||||
)
|
||||
endif()
|
||||
|
||||
qt_standard_project_setup()
|
||||
qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
|
||||
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
|
||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep)
|
||||
endif()
|
||||
|
||||
qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc)
|
||||
|
||||
qt6_add_translations(${PROJECT} TS_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru.ts
|
||||
)
|
||||
|
||||
if(IOS)
|
||||
#execute_process(COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/scripts/run-build-cloak.sh)
|
||||
@@ -71,7 +62,6 @@ if(IS_CI)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cmake/3rdparty.cmake)
|
||||
|
||||
include_directories(
|
||||
@@ -92,8 +82,8 @@ set(HEADERS ${HEADERS}
|
||||
${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}/platforms/linux/leakdetector.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
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/pages.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/property_helper.h
|
||||
@@ -104,19 +94,6 @@ set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.h
|
||||
)
|
||||
|
||||
# Mozilla headres
|
||||
set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/models/server.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/ipaddress.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/leakdetector.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/controllerimpl.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/localsocketcontroller.h
|
||||
)
|
||||
|
||||
include_directories(mozilla)
|
||||
include_directories(mozilla/shared)
|
||||
include_directories(mozilla/models)
|
||||
|
||||
if(NOT IOS)
|
||||
set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/MobileUtils.h
|
||||
@@ -132,6 +109,7 @@ set(SOURCES ${SOURCES}
|
||||
${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}/platforms/linux/leakdetector.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/uilogic.cpp
|
||||
@@ -140,18 +118,6 @@ set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.cpp
|
||||
)
|
||||
|
||||
# Mozilla sources
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/models/server.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/ipaddress.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/leakdetector.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/localsocketcontroller.cpp
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_definitions(${PROJECT} PRIVATE "MZ_DEBUG")
|
||||
endif()
|
||||
|
||||
if(NOT IOS)
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/MobileUtils.cpp
|
||||
@@ -184,7 +150,11 @@ set(SOURCES ${SOURCES}
|
||||
${UI_MODELS_CPP}
|
||||
)
|
||||
|
||||
qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc)
|
||||
|
||||
if(WIN32)
|
||||
add_compile_definitions(MVPN_WINDOWS)
|
||||
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/windows/amneziavpn.rc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/amneziavpn.rc
|
||||
@@ -235,9 +205,36 @@ if(APPLE)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${BUILD_VPN_DEVELOPMENT_TEAM})
|
||||
set(CMAKE_XCODE_ATTRIBUTE_GROUP_ID_IOS ${BUILD_IOS_GROUP_IDENTIFIER})
|
||||
|
||||
if(NOT IOS)
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
|
||||
|
||||
message("MAC build")
|
||||
set(HEADERS ${HEADERS} ${CMAKE_CURRENT_LIST_DIR}/ui/macos_util.h)
|
||||
set(SOURCES ${SOURCES} ${CMAKE_CURRENT_LIST_DIR}/ui/macos_util.mm)
|
||||
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||
add_compile_definitions(MVPN_MACOS)
|
||||
# ICON = $$PWD/images/app.icns
|
||||
|
||||
find_library(FW_COCOA Cocoa)
|
||||
find_library(FW_APPLICATIONSERVICES ApplicationServices)
|
||||
find_library(FW_CORESERVICES CoreServices)
|
||||
find_library(FW_FOUNDATION Foundation)
|
||||
find_library(FW_APPKIT AppKit)
|
||||
find_library(FW_SECURITY Security)
|
||||
|
||||
set(LIBS ${LIBS}
|
||||
${FW_COCOA} ${FW_APPLICATIONSERVICES}
|
||||
${FW_FOUNDATION} ${FW_APPKIT}
|
||||
${FW_SECURITY} ${FW_CORESERVICES}
|
||||
${LIB_LIBCRYPTO}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(LINUX AND NOT ANDROID)
|
||||
add_compile_definitions(MVPN_LINUX)
|
||||
|
||||
link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/linux)
|
||||
endif()
|
||||
|
||||
@@ -272,6 +269,8 @@ if(ANDROID)
|
||||
# As QAndroidBinder is not yet implemented with a public api
|
||||
set(LIBS ${LIBS} Qt6::CorePrivate)
|
||||
|
||||
add_compile_definitions(MVPN_ANDROID)
|
||||
|
||||
link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/android)
|
||||
|
||||
set(HEADERS ${HEADERS}
|
||||
@@ -292,12 +291,179 @@ if(ANDROID)
|
||||
endif()
|
||||
|
||||
if(IOS)
|
||||
include(cmake/ios.cmake)
|
||||
include(cmake/ios-arch-fixup.cmake)
|
||||
elseif(APPLE AND NOT IOS)
|
||||
include(cmake/golang.cmake)
|
||||
message("Client iOS build")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS ShaderTools)
|
||||
set(LIBS ${LIBS} Qt6::ShaderTools)
|
||||
|
||||
find_library(FW_AUTHENTICATIONSERVICES AuthenticationServices)
|
||||
find_library(FW_UIKIT UIKit)
|
||||
find_library(FW_AVFOUNDATION AVFoundation)
|
||||
find_library(FW_FOUNDATION Foundation)
|
||||
find_library(FW_STOREKIT StoreKit)
|
||||
find_library(FW_USERNOTIFICATIONS UserNotifications)
|
||||
|
||||
set(LIBS ${LIBS}
|
||||
${FW_AUTHENTICATIONSERVICES} ${FW_UIKIT}
|
||||
${FW_AVFOUNDATION} ${FW_FOUNDATION} ${FW_STOREKIT}
|
||||
${FW_USERNOTIFICATIONS}
|
||||
)
|
||||
|
||||
add_compile_definitions(MVPN_IOS)
|
||||
|
||||
set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/ios_vpnprotocol.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/iosnotificationhandler.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/bigint.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/bigintipv6addr.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/ipaddress.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/ipaddressrange.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QtAppDelegate.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QtAppDelegate-C-Interface.h
|
||||
)
|
||||
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/ios_vpnprotocol.mm
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/iosnotificationhandler.mm
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/iosglue.mm
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/ipaddress.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/ipaddressrange.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.mm
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QtAppDelegate.mm
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/MobileUtils.mm
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CMAKE_OSX_SYSROOT STREQUAL "iphoneos")
|
||||
message("Building for iPhone OS")
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 13.0)
|
||||
endif()
|
||||
|
||||
qt_add_executable(${PROJECT} ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC})
|
||||
qt_add_translations(${PROJECT} TS_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru.ts)
|
||||
|
||||
if(APPLE AND NOT IOS)
|
||||
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
|
||||
endif()
|
||||
|
||||
if(IOS)
|
||||
enable_language(OBJC)
|
||||
enable_language(OBJCXX)
|
||||
enable_language(Swift)
|
||||
|
||||
include(cmake/osxtools.cmake)
|
||||
include(cmake/macos.cmake)
|
||||
|
||||
set(LIBS ${LIBS}
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/OpenSSL/lib/ios/iphone/libcrypto.a
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/OpenSSL/lib/ios/iphone/libssl.a
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT} PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
|
||||
|
||||
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||
|
||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_LIST_DIR}/ios/app/Info.plist.in
|
||||
MACOSX_BUNDLE_ICON_FILE "AppIcon"
|
||||
MACOSX_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPN"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
MACOSX_BUNDLE_LONG_VERSION_STRING "${APPLE_PROJECT_VERSION}-${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
|
||||
|
||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_LIST_DIR}/ios/app/main.entitlements"
|
||||
XCODE_ATTRIBUTE_MARKETING_VERSION "${APPLE_PROJECT_VERSION}"
|
||||
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPN"
|
||||
XCODE_ATTRIBUTE_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||
|
||||
XCODE_GENERATE_SCHEME TRUE
|
||||
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
|
||||
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
|
||||
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
||||
|
||||
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON
|
||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||
|
||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
|
||||
|
||||
XCODE_EMBED_APP_EXTENSIONS networkextension
|
||||
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "match AppStore org.amnezia.AmneziaVPN"
|
||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "match Development org.amnezia.AmneziaVPN"
|
||||
)
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||
XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "${CMAKE_CURRENT_LIST_DIR}/platforms/ios/WireGuard-Bridging-Header.h"
|
||||
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
|
||||
XCODE_ATTRIBUTE_SWIFT_OBJC_INTERFACE_HEADER_NAME "AmneziaVPN-Swift.h"
|
||||
)
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
target_compile_options(${PROJECT} PRIVATE
|
||||
-DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\"
|
||||
-DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\"
|
||||
)
|
||||
|
||||
target_sources(${PROJECT} PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/iosvpnprotocol.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/ioslogger.swift
|
||||
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/Shared/Keychain.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/IPAddressRange.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/InterfaceConfiguration.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/Shared/Model/NETunnelProviderProtocol+Extension.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/TunnelConfiguration.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/Shared/Model/TunnelConfiguration+WgQuickConfig.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/Endpoint.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/Shared/Model/String+ArrayConversion.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/PeerConfiguration.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/DNSServer.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardApp/LocalizationHelper.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/Shared/FileManager+Extension.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardKitC/x25519.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/PrivateKey.swift
|
||||
)
|
||||
|
||||
target_sources(${PROJECT} PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR}/ios/app/launch.png
|
||||
${CMAKE_CURRENT_LIST_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard
|
||||
${CMAKE_CURRENT_LIST_DIR}/ios/Media.xcassets
|
||||
)
|
||||
set_source_files_properties(
|
||||
${CMAKE_CURRENT_LIST_DIR}/ios/app/launch.png
|
||||
${CMAKE_CURRENT_LIST_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard
|
||||
${CMAKE_CURRENT_LIST_DIR}/ios/Media.xcassets
|
||||
PROPERTIES MACOSX_PACKAGE_LOCATION "Resources"
|
||||
)
|
||||
|
||||
|
||||
add_subdirectory(ios/networkextension)
|
||||
add_dependencies(${PROJECT} networkextension)
|
||||
|
||||
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
||||
"${CMAKE_CURRENT_LIST_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework"
|
||||
)
|
||||
|
||||
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_LIST_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos)
|
||||
target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_LIST_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework")
|
||||
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
@@ -324,6 +490,7 @@ if(ANDROID)
|
||||
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/AmneziaApp.kt
|
||||
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/PackageManagerHelper.java
|
||||
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/VPNActivity.kt
|
||||
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/VPNApplication.java
|
||||
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/VPNClientBinder.kt
|
||||
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/VPNPermissionHelper.kt
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
@@ -352,7 +519,11 @@ if(ANDROID)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${PROJECT} PRIVATE ${LIBS})
|
||||
target_compile_definitions(${PROJECT} PRIVATE "MZ_$<UPPER_CASE:${MZ_PLATFORM_NAME}>")
|
||||
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
|
||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep)
|
||||
endif()
|
||||
|
||||
# deploy artifacts required to run the application to the debug build folder
|
||||
if(WIN32)
|
||||
@@ -386,7 +557,6 @@ if(WIN32)
|
||||
COMMAND_EXPAND_LISTS
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC})
|
||||
qt_finalize_target(${PROJECT})
|
||||
if(IOS)
|
||||
include(cmake/ios-arch-fixup.cmake)
|
||||
endif()
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
#include "amnezia_application.h"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QFontDatabase>
|
||||
#include <QMimeData>
|
||||
#include <QStandardPaths>
|
||||
#include <QTextDocument>
|
||||
#include <QTimer>
|
||||
#include <QTranslator>
|
||||
|
||||
@@ -38,8 +35,6 @@
|
||||
#include "ui/pages_logic/protocols/OpenVpnLogic.h"
|
||||
#include "ui/pages_logic/protocols/ShadowSocksLogic.h"
|
||||
|
||||
#include "protocols/qml_register_protocols.h"
|
||||
|
||||
#if defined(Q_OS_IOS)
|
||||
#include "platforms/ios/QtAppDelegate-C-Interface.h"
|
||||
#endif
|
||||
@@ -56,7 +51,7 @@
|
||||
setQuitOnLastWindowClosed(false);
|
||||
|
||||
// Fix config file permissions
|
||||
#ifdef Q_OS_LINUX && !defined(Q_OS_ANDROID)
|
||||
#ifdef Q_OS_LINUX
|
||||
{
|
||||
QSettings s(ORGANIZATION_NAME, APPLICATION_NAME);
|
||||
s.setValue("permFixed", true);
|
||||
@@ -69,6 +64,7 @@
|
||||
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);
|
||||
@@ -139,19 +135,6 @@ void AmneziaApplication::init()
|
||||
}
|
||||
#endif
|
||||
|
||||
// Android TextField clipboard workaround
|
||||
// https://bugreports.qt.io/browse/QTBUG-113461
|
||||
#ifdef Q_OS_ANDROID
|
||||
QObject::connect(qApp, &QApplication::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()
|
||||
|
||||
@@ -136,9 +136,9 @@ android {
|
||||
defaultConfig {
|
||||
resConfig "en"
|
||||
minSdkVersion = 24
|
||||
targetSdkVersion = 34
|
||||
versionCode 25 // Change to a higher number
|
||||
versionName "3.0.8" // Change to a higher number
|
||||
targetSdkVersion = 31
|
||||
versionCode 10 // Change to a higher number
|
||||
versionName "2.0.10" // Change to a higher number
|
||||
|
||||
javaCompileOptions.annotationProcessorOptions.arguments = [
|
||||
"room.schemaLocation": "${qtAndroidDir}/schemas".toString()
|
||||
|
||||
@@ -93,7 +93,7 @@ object NotificationUtil {
|
||||
val prefs = Prefs.get(service)
|
||||
val message =
|
||||
"" + prefs.getString("fallbackNotificationMessage", "Running in the Background")
|
||||
val header = "" + prefs.getString("fallbackNotificationHeader", "Amnezia VPN")
|
||||
val header = "" + prefs.getString("fallbackNotificationHeader", "Mozilla VPN")
|
||||
|
||||
// Create the Intent that Should be Fired if the User Clicks the notification
|
||||
val mainActivityName = "org.amnezia.vpn.qt.VPNActivity"
|
||||
|
||||
@@ -11,14 +11,14 @@ import android.util.Log as nativeLog
|
||||
|
||||
/*
|
||||
* Drop in replacement for android.util.Log
|
||||
* Also stores a copy of all logs in tmp/amnezia_daemon_logs.txt
|
||||
* Also stores a copy of all logs in tmp/mozilla_daemon_logs.txt
|
||||
*/
|
||||
class Log {
|
||||
val LOG_MAX_FILE_SIZE = 204800
|
||||
private var file: File
|
||||
private constructor(context: Context) {
|
||||
val tempDIR = context.cacheDir
|
||||
file = File(tempDIR, "amnezia_daemon_logs.txt")
|
||||
file = File(tempDIR, "mozilla_daemon_logs.txt")
|
||||
if (file.length() > LOG_MAX_FILE_SIZE) {
|
||||
file.writeText("")
|
||||
}
|
||||
|
||||
@@ -168,8 +168,11 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
NotificationUtil.show(this) // Go foreground
|
||||
super.onCreate()
|
||||
// Log.v(tag, "Aman: onCreate....................")
|
||||
// Log.v(tag, "Aman: onCreate....................")
|
||||
// Log.v(tag, "Aman: onCreate....................")
|
||||
// NotificationUtil.show(this) // Go foreground
|
||||
}
|
||||
|
||||
override fun onUnbind(intent: Intent?): Boolean {
|
||||
|
||||
@@ -196,7 +196,6 @@ class VPNServiceBinder(service: VPNService) : Binder() {
|
||||
|
||||
/**
|
||||
* The codes we Are Using in case of [dispatchEvent]
|
||||
* Qt codes in the androidvpnactivity.h
|
||||
*/
|
||||
object EVENTS {
|
||||
const val init = 0
|
||||
|
||||
@@ -7,7 +7,7 @@ import org.qtproject.qt.android.bindings.QtActivity
|
||||
import org.qtproject.qt.android.bindings.QtApplication
|
||||
import android.app.Application
|
||||
|
||||
class AmneziaApp: org.qtproject.qt.android.bindings.QtApplication() {
|
||||
class AmneziaApp: Application() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
@@ -19,4 +19,4 @@ class AmneziaApp: org.qtproject.qt.android.bindings.QtApplication() {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
Core.updateNotificationChannels()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,7 +111,7 @@ public class PackageManagerHelper {
|
||||
|
||||
// Returns List of all Packages that can classify themselves as browsers
|
||||
private static List<String> getBrowserIDs(PackageManager pm) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.amnezia.org/"));
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.mozilla.org/"));
|
||||
intent.addCategory(Intent.CATEGORY_BROWSABLE);
|
||||
// We've tried using PackageManager.MATCH_DEFAULT_ONLY flag and found that browsers that
|
||||
// are not set as the default browser won't be matched even if they had CATEGORY_DEFAULT set
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
package org.amnezia.vpn.qt;
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.ComponentName
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
@@ -71,6 +73,10 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
|
||||
@JvmStatic fun saveFileAs(fileContent: String, suggestedName: String) {
|
||||
VPNActivity.getInstance().saveFile(fileContent, suggestedName)
|
||||
}
|
||||
|
||||
@JvmStatic fun putTextToClipboard(text: String) {
|
||||
VPNActivity.getInstance().putToClipboard(text)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -103,6 +109,18 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
|
||||
startActivityForResult(intent, CREATE_FILE_ACTION_CODE)
|
||||
}
|
||||
|
||||
override fun getSystemService(name: String): Any? {
|
||||
return if (Build.VERSION.SDK_INT >= 29 && name == "clipboard") {
|
||||
// QT will always attempt to read the clipboard if content is there.
|
||||
// since we have no use of the clipboard in android 10+
|
||||
// we _can_ return null
|
||||
// And we definitely should since android 12 displays clipboard access.
|
||||
null
|
||||
} else {
|
||||
super.getSystemService(name)
|
||||
}
|
||||
}
|
||||
|
||||
external fun handleBackButton(): Boolean
|
||||
|
||||
external fun onServiceMessage(actionCode: Int, body: String?)
|
||||
@@ -299,4 +317,15 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
|
||||
|
||||
tmpFileContentToSave = ""
|
||||
}
|
||||
|
||||
private fun putToClipboard(text: String) {
|
||||
this.runOnUiThread {
|
||||
val clipboard = applicationContext.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager?
|
||||
|
||||
if (clipboard != null) {
|
||||
val clip: ClipData = ClipData.newPlainText("", text)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.amnezia.vpn.qt;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import androidx.annotation.NonNull;
|
||||
import org.amnezia.vpn.shadowsocks.core.Core;
|
||||
import org.amnezia.vpn.shadowsocks.core.VpnManager;
|
||||
|
||||
public class VPNApplication extends org.qtproject.qt.android.bindings.QtApplication {
|
||||
private static VPNApplication instance;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
VPNApplication.instance = this;
|
||||
// Core.INSTANCE.init(this, VPNActivity.class);
|
||||
// VpnManager.Companion.getInstance().init(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
// Core.INSTANCE.updateNotificationChannels();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.ftylitak.qzxing;
|
||||
|
||||
public class NativeFunctions {
|
||||
public static native void onPermissionsGranted();
|
||||
public static native void onPermissionsDenied();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.ftylitak.qzxing;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.pm.PackageManager;
|
||||
import org.qtproject.qt.android.bindings.QtActivity;
|
||||
import static org.ftylitak.qzxing.Utilities.REQUEST_CAMERA;
|
||||
|
||||
public class QZXingLiveActivity extends QtActivity {
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode,
|
||||
String permissions[], int[] grantResults) {
|
||||
switch (requestCode) {
|
||||
case REQUEST_CAMERA: {
|
||||
// If request is cancelled, the result arrays are empty.
|
||||
if (grantResults.length > 0
|
||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
NativeFunctions.onPermissionsGranted();
|
||||
} else {
|
||||
NativeFunctions.onPermissionsDenied();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.ftylitak.qzxing;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.pm.PackageManager;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Utilities {
|
||||
|
||||
public static final int REQUEST_CAMERA = 0;
|
||||
|
||||
public static final String[] requiredPermissionsModifyPhoneState = {
|
||||
Manifest.permission.CAMERA,
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
};
|
||||
|
||||
public static void checkAndRequestPermissionList(Activity activity, String[] permissions) {
|
||||
ArrayList<String> permissionsToRequest = new ArrayList<>();
|
||||
for (int i = 0; i < permissions.length; i++) {
|
||||
if (ContextCompat.checkSelfPermission(activity, permissions[i])
|
||||
!= PackageManager.PERMISSION_GRANTED)
|
||||
permissionsToRequest.add(permissions[i]);
|
||||
}
|
||||
|
||||
if (permissionsToRequest.size() != 0)
|
||||
ActivityCompat.requestPermissions(activity,
|
||||
permissionsToRequest.toArray(new String[0]),
|
||||
REQUEST_CAMERA);
|
||||
else
|
||||
NativeFunctions.onPermissionsGranted();
|
||||
}
|
||||
|
||||
public static void requestQZXingPermissions(Activity activity) {
|
||||
checkAndRequestPermissionList(activity, requiredPermissionsModifyPhoneState);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -16,7 +16,7 @@ function(build_go_archive OUTPUT_NAME MODULE_FILE)
|
||||
string(REGEX REPLACE "\\.[^/]*$" ".h" GOBUILD_HEADER_FILE ${OUTPUT_NAME})
|
||||
get_filename_component(GOBUILD_MODULE_ABS ${MODULE_FILE} ABSOLUTE)
|
||||
get_filename_component(GOBUILD_MODULE_DIR ${GOBUILD_MODULE_ABS} DIRECTORY)
|
||||
set(GOBUILD_ARGS -buildmode=c-archive -buildvcs=false -trimpath -v)
|
||||
set(GOBUILD_ARGS -buildmode=c-archive -trimpath -v)
|
||||
if(IS_DIRECTORY ${GOBUILD_MODULE_DIR}/vendor)
|
||||
list(APPEND GOBUILD_ARGS -mod vendor)
|
||||
endif()
|
||||
@@ -48,8 +48,6 @@ function(build_go_archive OUTPUT_NAME MODULE_FILE)
|
||||
DEPENDS ${MODULE_FILE} ${GOBUILD_SOURCES}
|
||||
WORKING_DIRECTORY ${GOBUILD_MODULE_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E env GOCACHE=${GOCACHE}
|
||||
CC=${CMAKE_C_COMPILER}
|
||||
CXX=${CMAKE_CXX_COMPILER}
|
||||
CGO_ENABLED=1
|
||||
CGO_CFLAGS="${GOBUILD_CGO_CFLAGS}"
|
||||
CGO_LDFLAGS="${GOBUILD_CGO_LDFLAGS}"
|
||||
@@ -63,65 +61,64 @@ endfunction(build_go_archive)
|
||||
function(add_go_library GOTARGET SOURCE)
|
||||
cmake_parse_arguments(GOLANG
|
||||
""
|
||||
""
|
||||
"GOOS;GOARCH"
|
||||
"CGO_CFLAGS;CGO_LDFLAGS"
|
||||
${ARGN})
|
||||
get_filename_component(SRC_NAME ${SOURCE} NAME)
|
||||
get_filename_component(DIR_NAME ${SOURCE} DIRECTORY)
|
||||
get_filename_component(DIR_ABSOLUTE ${DIR_NAME} ABSOLUTE)
|
||||
|
||||
file(GLOB_RECURSE SRC_DEPS ${DIR_NAME}/*.go)
|
||||
set(HEADER_NAME "${GOTARGET}.h")
|
||||
set(ARCHIVE_NAME "${GOTARGET}${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||
|
||||
set(GOCACHE ${CMAKE_BINARY_DIR}/go-cache)
|
||||
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_BINARY_DIR}/go-cache)
|
||||
set(GOFLAGS -buildmode=c-archive -trimpath -v)
|
||||
if(IS_DIRECTORY ${DIR_NAME}/vendor)
|
||||
set(GOFLAGS ${GOFLAGS} -mod vendor)
|
||||
endif()
|
||||
|
||||
## Add extras to the CGO compiler and linker flags.
|
||||
execute_process(OUTPUT_VARIABLE DEFAULT_CGO_CFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND ${GOLANG_BUILD_TOOL} env CGO_CFLAGS)
|
||||
execute_process(OUTPUT_VARIABLE DEFAULT_CGO_LDFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND ${GOLANG_BUILD_TOOL} env CGO_LDFLAGS)
|
||||
separate_arguments(DEFAULT_CGO_CFLAGS NATIVE_COMMAND ${DEFAULT_CGO_CFLAGS})
|
||||
separate_arguments(DEFAULT_CGO_LDFLAGS NATIVE_COMMAND ${DEFAULT_CGO_LDFLAGS})
|
||||
|
||||
## The actual commands that do the building.
|
||||
if((CMAKE_SYSTEM_NAME STREQUAL "Darwin") AND CMAKE_OSX_ARCHITECTURES)
|
||||
foreach(OSXARCH ${CMAKE_OSX_ARCHITECTURES})
|
||||
string(REPLACE "x86_64" "amd64" GOARCH ${OSXARCH})
|
||||
build_go_archive(${CMAKE_CURRENT_BINARY_DIR}/${OSXARCH}/${ARCHIVE_NAME} ${DIR_NAME}/go.mod
|
||||
GOARCH ${GOARCH}
|
||||
CGO_CFLAGS ${DEFAULT_CGO_CFLAGS} ${GOLANG_CGO_CFLAGS} -arch ${OSXARCH}
|
||||
CGO_LDFLAGS ${DEFAULT_CGO_LDFLAGS} ${GOLANG_CGO_LDFLAGS} -arch ${OSXARCH}
|
||||
)
|
||||
list(APPEND ARCH_ARCHIVE_FILES ${CMAKE_CURRENT_BINARY_DIR}/${OSXARCH}/${ARCHIVE_NAME})
|
||||
list(APPEND ARCH_HEADER_FILES ${CMAKE_CURRENT_BINARY_DIR}/${OSXARCH}/${HEADER_NAME})
|
||||
endforeach()
|
||||
|
||||
list(GET ARCH_HEADER_FILES 0 FIRST_HEADER_FILE)
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ARCHIVE_NAME} ${CMAKE_CURRENT_BINARY_DIR}/${HEADER_NAME}
|
||||
DEPENDS ${ARCH_ARCHIVE_FILES} ${ARCH_HEADER_FILES}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND lipo -create -output ${ARCHIVE_NAME} ${ARCH_ARCHIVE_FILES}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${FIRST_HEADER_FILE} ${HEADER_NAME}
|
||||
)
|
||||
else()
|
||||
## Regular single architecture build
|
||||
build_go_archive(${CMAKE_CURRENT_BINARY_DIR}/${ARCHIVE_NAME} ${DIR_NAME}/go.mod
|
||||
CGO_CFLAGS ${DEFAULT_CGO_CFLAGS} ${GOLANG_CGO_CFLAGS}
|
||||
CGO_LDFLAGS ${DEFAULT_CGO_LDFLAGS} ${GOLANG_CGO_LDFLAGS}
|
||||
)
|
||||
list(PREPEND GOLANG_CGO_CFLAGS ${DEFAULT_CGO_CFLAGS})
|
||||
list(PREPEND GOLANG_CGO_LDFLAGS ${DEFAULT_CGO_LDFLAGS})
|
||||
if(NOT GOLANG_GOOS)
|
||||
execute_process(OUTPUT_VARIABLE GOLANG_GOOS OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND ${GOLANG_BUILD_TOOL} env GOOS)
|
||||
endif()
|
||||
if(NOT GOLANG_GOARCH)
|
||||
execute_process(OUTPUT_VARIABLE GOLANG_GOARCH OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND ${GOLANG_BUILD_TOOL} env GOARCH)
|
||||
endif()
|
||||
|
||||
set_source_files_properties({CMAKE_CURRENT_BINARY_DIR}/${HEADER_NAME} PROPERTIES
|
||||
GENERATED TRUE
|
||||
SKIP_AUTOGEN TRUE
|
||||
if(APPLE AND CMAKE_OSX_SYSROOT)
|
||||
execute_process(OUTPUT_VARIABLE SDKROOT OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND xcrun --sdk ${CMAKE_OSX_SYSROOT} --show-sdk-path)
|
||||
list(APPEND GOLANG_CGO_CFLAGS -isysroot ${SDKROOT})
|
||||
list(APPEND GOLANG_CGO_LDFLAGS -isysroot ${SDKROOT})
|
||||
endif()
|
||||
|
||||
## The actual commands that do the building.
|
||||
add_custom_target(golang_${GOTARGET}
|
||||
BYPRODUCTS ${ARCHIVE_NAME} ${HEADER_NAME}
|
||||
WORKING_DIRECTORY ${DIR_ABSOLUTE}
|
||||
SOURCES ${SRC_DEPS} ${DIR_NAME}/go.mod
|
||||
COMMAND ${CMAKE_COMMAND} -E env GOCACHE=${GOCACHE}
|
||||
CGO_ENABLED=1
|
||||
CGO_CFLAGS="${GOLANG_CGO_CFLAGS}"
|
||||
CGO_LDFLAGS="${GOLANG_CGO_LDFLAGS}"
|
||||
GOOS="${GOLANG_GOOS}"
|
||||
GOARCH="${GOLANG_GOARCH}"
|
||||
${GOLANG_BUILD_TOOL} build ${GOFLAGS} -o ${CMAKE_CURRENT_BINARY_DIR}/${ARCHIVE_NAME} ${SRC_NAME}
|
||||
)
|
||||
set_target_properties(golang_${GOTARGET} PROPERTIES FOLDER "Libs")
|
||||
|
||||
## Wrap up the built library as an imported target.
|
||||
add_library(${GOTARGET} STATIC IMPORTED GLOBAL)
|
||||
add_dependencies(${GOTARGET} golang_${GOTARGET})
|
||||
set_target_properties(${GOTARGET} PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}
|
||||
INTERFACE_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${HEADER_NAME}
|
||||
IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/${ARCHIVE_NAME})
|
||||
|
||||
## Some dependency glue to ensure we actually build the library.
|
||||
add_custom_target(${GOTARGET}_builder DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ARCHIVE_NAME})
|
||||
add_dependencies(${GOTARGET} ${GOTARGET}_builder)
|
||||
set_target_properties(${GOTARGET}_builder PROPERTIES FOLDER "Libs")
|
||||
|
||||
endfunction(add_go_library)
|
||||
endfunction(add_go_library)
|
||||
@@ -1,157 +0,0 @@
|
||||
message("Client iOS build")
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 13.0)
|
||||
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
|
||||
|
||||
enable_language(OBJC)
|
||||
enable_language(OBJCXX)
|
||||
enable_language(Swift)
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS ShaderTools)
|
||||
set(LIBS ${LIBS} Qt6::ShaderTools)
|
||||
|
||||
find_library(FW_AUTHENTICATIONSERVICES AuthenticationServices)
|
||||
find_library(FW_UIKIT UIKit)
|
||||
find_library(FW_AVFOUNDATION AVFoundation)
|
||||
find_library(FW_FOUNDATION Foundation)
|
||||
find_library(FW_STOREKIT StoreKit)
|
||||
find_library(FW_USERNOTIFICATIONS UserNotifications)
|
||||
|
||||
set(LIBS ${LIBS}
|
||||
${FW_AUTHENTICATIONSERVICES} ${FW_UIKIT}
|
||||
${FW_AVFOUNDATION} ${FW_FOUNDATION} ${FW_STOREKIT}
|
||||
${FW_USERNOTIFICATIONS}
|
||||
)
|
||||
|
||||
|
||||
set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/protocols/ios_vpnprotocol.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.h
|
||||
#${CMAKE_CURRENT_SOURCE_DIR}/mozilla/shared/bigint.h
|
||||
#${CMAKE_CURRENT_SOURCE_DIR}/mozilla/shared/bigintipv6addr.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mozilla/shared/ipaddress.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ipaddressrange.h # TODO need refactor
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate-C-Interface.h
|
||||
)
|
||||
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/protocols/ios_vpnprotocol.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mozilla/shared/ipaddress.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ipaddressrange.cpp # TODO need refactor
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/MobileUtils.mm
|
||||
)
|
||||
|
||||
#set(SOURCES ${SOURCES}
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/Keychain.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/IPAddressRange.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/InterfaceConfiguration.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/Model/NETunnelProviderProtocol+Extension.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/TunnelConfiguration.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/Model/TunnelConfiguration+WgQuickConfig.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/Endpoint.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/Model/String+ArrayConversion.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/PeerConfiguration.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/DNSServer.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardApp/LocalizationHelper.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/FileManager+Extension.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKitC/x25519.c
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/PrivateKey.swift
|
||||
#)
|
||||
|
||||
set(LIBS ${LIBS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenSSL/lib/ios/iphone/libcrypto.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenSSL/lib/ios/iphone/libssl.a
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT} PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
|
||||
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Info.plist.in
|
||||
MACOSX_BUNDLE_ICON_FILE "AppIcon"
|
||||
MACOSX_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPN"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
MACOSX_BUNDLE_LONG_VERSION_STRING "${APPLE_PROJECT_VERSION}-${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
|
||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/ios/app/main.entitlements"
|
||||
XCODE_ATTRIBUTE_MARKETING_VERSION "${APPLE_PROJECT_VERSION}"
|
||||
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPN"
|
||||
XCODE_ATTRIBUTE_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||
XCODE_GENERATE_SCHEME TRUE
|
||||
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
|
||||
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
|
||||
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
||||
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON
|
||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
|
||||
XCODE_EMBED_APP_EXTENSIONS networkextension
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "match AppStore org.amnezia.AmneziaVPN"
|
||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "match Development org.amnezia.AmneziaVPN"
|
||||
)
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||
XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/WireGuard-Bridging-Header.h"
|
||||
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
|
||||
XCODE_ATTRIBUTE_SWIFT_OBJC_INTERFACE_HEADER_NAME "AmneziaVPN-Swift.h"
|
||||
)
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
|
||||
)
|
||||
target_include_directories(${PROJECT} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_compile_options(${PROJECT} PRIVATE
|
||||
-DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\"
|
||||
-DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\"
|
||||
)
|
||||
target_sources(${PROJECT} PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosvpnprotocol.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ioslogger.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/Keychain.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/IPAddressRange.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/InterfaceConfiguration.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/Model/NETunnelProviderProtocol+Extension.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/TunnelConfiguration.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/Model/TunnelConfiguration+WgQuickConfig.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/Endpoint.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/Model/String+ArrayConversion.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/PeerConfiguration.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/DNSServer.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardApp/LocalizationHelper.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/FileManager+Extension.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKitC/x25519.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/PrivateKey.swift
|
||||
)
|
||||
|
||||
target_sources(${PROJECT} PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Media.xcassets
|
||||
)
|
||||
|
||||
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Media.xcassets
|
||||
)
|
||||
|
||||
add_subdirectory(ios/networkextension)
|
||||
add_dependencies(${PROJECT} networkextension)
|
||||
|
||||
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework"
|
||||
)
|
||||
|
||||
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos)
|
||||
target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework")
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
message("MAC build")
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||
|
||||
|
||||
set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.h
|
||||
)
|
||||
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.mm
|
||||
)
|
||||
|
||||
set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns)
|
||||
set(MACOSX_BUNDLE_ICON_FILE app.icns)
|
||||
set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
set(SOURCES ${SOURCES} ${ICON_FILE})
|
||||
|
||||
|
||||
|
||||
find_library(FW_SYSTEMCONFIG SystemConfiguration)
|
||||
find_library(FW_SERVICEMGMT ServiceManagement)
|
||||
find_library(FW_SECURITY Security)
|
||||
find_library(FW_COREWLAN CoreWLAN)
|
||||
find_library(FW_NETWORK Network)
|
||||
find_library(FW_USER_NOTIFICATIONS UserNotifications)
|
||||
|
||||
target_link_libraries(${PROJECT} PRIVATE ${FW_SYSTEMCONFIG})
|
||||
target_link_libraries(${PROJECT} PRIVATE ${FW_SERVICEMGMT})
|
||||
target_link_libraries(${PROJECT} PRIVATE ${FW_SECURITY})
|
||||
target_link_libraries(${PROJECT} PRIVATE ${FW_COREWLAN})
|
||||
target_link_libraries(${PROJECT} PRIVATE ${FW_NETWORK})
|
||||
target_link_libraries(${PROJECT} PRIVATE ${FW_USER_NOTIFICATIONS})
|
||||
|
||||
# Get SDK path
|
||||
execute_process(
|
||||
COMMAND sh -c "xcrun --sdk macosx --show-sdk-path"
|
||||
OUTPUT_VARIABLE OSX_SDK_PATH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
message("OSX_SDK_PATH is: ${OSX_SDK_PATH}")
|
||||
|
||||
# Build the Wireguard Go tunnel
|
||||
file(GLOB_RECURSE WIREGUARD_GO_DEPS ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-go/*.go)
|
||||
set(WIREGUARD_GO_ENV
|
||||
GOCACHE=${CMAKE_BINARY_DIR}/go-cache
|
||||
CC=${CMAKE_C_COMPILER}
|
||||
CXX=${CMAKE_CXX_COMPILER}
|
||||
GOOS=darwin
|
||||
CGO_ENABLED=1
|
||||
GO111MODULE=on
|
||||
CGO_CFLAGS='-g -O3 -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET} -isysroot ${OSX_SDK_PATH}'
|
||||
CGO_LDFLAGS='-g -O3 -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET} -isysroot ${OSX_SDK_PATH}'
|
||||
)
|
||||
|
||||
if(NOT CMAKE_OSX_ARCHITECTURES)
|
||||
foreach(OSXARCH ${CMAKE_OSX_ARCHITECTURES})
|
||||
message("Build wg for OSXARCH: ${OSXARCH}")
|
||||
|
||||
string(REPLACE "x86_64" "amd64" GOARCH ${OSXARCH})
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-${OSXARCH}
|
||||
COMMENT "Building wireguard-go for ${OSXARCH}"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-go
|
||||
DEPENDS
|
||||
${WIREGUARD_GO_DEPS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-go/go.mod
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-go/go.sum
|
||||
COMMAND ${CMAKE_COMMAND} -E env ${WIREGUARD_GO_ENV} GOARCH=${GOARCH}
|
||||
${GOLANG_BUILD_TOOL} build -buildmode exe -buildvcs=false -trimpath -v
|
||||
-o ${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-${OSXARCH}
|
||||
)
|
||||
list(APPEND WG_GO_ARCH_BUILDS ${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-${OSXARCH}/wireguard)
|
||||
endforeach()
|
||||
|
||||
add_custom_target(build_wireguard_go
|
||||
COMMENT "Building wireguard-go"
|
||||
DEPENDS ${WG_GO_ARCH_BUILDS}
|
||||
COMMAND lipo -create -output ${CMAKE_CURRENT_BINARY_DIR}/wireguard-go ${WG_GO_ARCH_BUILDS}
|
||||
)
|
||||
else()
|
||||
# This only builds for the host architecture.
|
||||
add_custom_target(build_wireguard_go
|
||||
COMMENT "Building wireguard-go"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-go
|
||||
DEPENDS
|
||||
${WIREGUARD_GO_DEPS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-go/go.mod
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-go/go.sum
|
||||
COMMAND ${CMAKE_COMMAND} -E env ${WIREGUARD_GO_ENV}
|
||||
${GOLANG_BUILD_TOOL} build -buildmode exe -buildvcs=false -trimpath -v
|
||||
-o ${CMAKE_CURRENT_BINARY_DIR}/wireguard-go
|
||||
)
|
||||
endif()
|
||||
add_dependencies(${PROJECT} build_wireguard_go)
|
||||
osx_bundle_files(${PROJECT}
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/wireguard-go
|
||||
DESTINATION MacOS/
|
||||
)
|
||||
@@ -42,8 +42,11 @@ QString CloakConfigurator::genCloakConfig(const ServerCredentials &credentials,
|
||||
config.insert("NumConn", 1);
|
||||
config.insert("BrowserSig", "chrome");
|
||||
config.insert("StreamTimeout", 300);
|
||||
config.insert("RemoteHost", credentials.hostName);
|
||||
config.insert("RemotePort", "$CLOAK_SERVER_PORT");
|
||||
|
||||
// transfer params to protocol runner
|
||||
config.insert(config_key::transport_proto, "tcp");
|
||||
config.insert(config_key::remote, credentials.hostName);
|
||||
config.insert(config_key::port, "$CLOAK_SERVER_PORT");
|
||||
|
||||
QString textCfg = serverController.replaceVars(QJsonDocument(config).toJson(),
|
||||
serverController.genVarsForScript(credentials, container, containerConfig));
|
||||
|
||||
@@ -95,7 +95,7 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia
|
||||
config.replace("</tls-auth>", "");
|
||||
}
|
||||
|
||||
#ifndef MZ_WINDOWS
|
||||
#if defined Q_OS_MAC || defined(Q_OS_LINUX)
|
||||
config.replace("block-outside-dns", "");
|
||||
#endif
|
||||
|
||||
@@ -123,11 +123,8 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig)
|
||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||
config.append("redirect-gateway ipv6\n");
|
||||
|
||||
#ifndef MZ_WINDOWS
|
||||
#if (defined Q_OS_MAC || defined(Q_OS_LINUX)) && !defined(Q_OS_ANDROID)
|
||||
config.replace("block-outside-dns", "");
|
||||
#endif
|
||||
|
||||
#if (defined (MZ_MACOS) || defined(MZ_LINUX))
|
||||
QString dnsConf = QString(
|
||||
"\nscript-security 2\n"
|
||||
"up %1/update-resolv-conf.sh\n"
|
||||
@@ -150,8 +147,9 @@ QString OpenVpnConfigurator::processConfigWithExportSettings(QString jsonConfig)
|
||||
config.append("redirect-gateway def1 bypass-dhcp\n");
|
||||
}
|
||||
|
||||
// remove block-outside-dns for all exported configs
|
||||
#if (defined Q_OS_MAC || defined(Q_OS_LINUX)) && !defined(Q_OS_ANDROID)
|
||||
config.replace("block-outside-dns", "");
|
||||
#endif
|
||||
|
||||
json[config_key::config] = config;
|
||||
return QJsonDocument(json).toJson();
|
||||
|
||||
@@ -63,7 +63,6 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
||||
{
|
||||
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
|
||||
connData.host = credentials.hostName;
|
||||
connData.port = containerConfig.value(config_key::port).toString(protocols::wireguard::defaultPort);
|
||||
|
||||
if (connData.clientPrivKey.isEmpty() || connData.clientPubKey.isEmpty()) {
|
||||
if (errorCode) *errorCode = ErrorCode::InternalError;
|
||||
@@ -182,7 +181,6 @@ QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &crede
|
||||
jConfig[config_key::config] = config;
|
||||
|
||||
jConfig[config_key::hostName] = connData.host;
|
||||
jConfig[config_key::port] = connData.port.toInt();
|
||||
jConfig[config_key::client_priv_key] = connData.clientPrivKey;
|
||||
jConfig[config_key::client_ip] = connData.clientIP;
|
||||
jConfig[config_key::client_pub_key] = connData.clientPubKey;
|
||||
|
||||
@@ -20,7 +20,6 @@ public:
|
||||
QString serverPubKey; // tls-auth key
|
||||
QString pskKey; // preshared key
|
||||
QString host; // host ip
|
||||
QString port;
|
||||
};
|
||||
|
||||
QString genWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
|
||||
@@ -5,31 +5,107 @@
|
||||
#ifndef CONSTANTS_H
|
||||
#define CONSTANTS_H
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <QObject>
|
||||
|
||||
namespace Constants {
|
||||
|
||||
inline QString versionString() { return QStringLiteral(APP_VERSION); }
|
||||
// Returns true if we are in a production environment.
|
||||
bool inProduction();
|
||||
void setStaging();
|
||||
|
||||
// Number of msecs for the captive-portal block alert.
|
||||
constexpr uint32_t CAPTIVE_PORTAL_ALERT_MSEC = 4000;
|
||||
|
||||
// Number of msecs for the unsecured network alert.
|
||||
constexpr uint32_t UNSECURED_NETWORK_ALERT_MSEC = 4000;
|
||||
|
||||
// Number of recent connections to retain.
|
||||
constexpr int RECENT_CONNECTIONS_MAX_COUNT = 5;
|
||||
|
||||
#if defined(UNIT_TEST)
|
||||
# define CONSTEXPR(type, functionName, releaseValue, debugValue, \
|
||||
testingValue) \
|
||||
inline type functionName() { return testingValue; }
|
||||
#else
|
||||
# define CONSTEXPR(type, functionName, releaseValue, debugValue, \
|
||||
testingValue) \
|
||||
inline type functionName() { \
|
||||
return inProduction() ? releaseValue : debugValue; \
|
||||
}
|
||||
#endif
|
||||
|
||||
// Let's refresh the IP address any 10 minutes (in milliseconds).
|
||||
CONSTEXPR(uint32_t, ipAddressTimerMsec, 600000, 10000, 0)
|
||||
|
||||
// Let's check the connection status any second.
|
||||
CONSTEXPR(uint32_t, checkStatusTimerMsec, 1000, 1000, 0)
|
||||
|
||||
// Number of points for the charts.
|
||||
CONSTEXPR(int, chartsMaxPoints, 30, 30, 30);
|
||||
|
||||
// Any 6 hours, a new check
|
||||
CONSTEXPR(uint32_t, releaseMonitorMsec, 21600000, 4000, 0)
|
||||
|
||||
// in milliseconds, how often we should fetch the server list and the account.
|
||||
CONSTEXPR(uint32_t, scheduleAccountAndServersTimerMsec, 3600000, 4000, 0)
|
||||
|
||||
// how often we check the captive portal when the VPN is on.
|
||||
CONSTEXPR(uint32_t, captivePortalRequestTimeoutMsec, 10000, 4000, 0)
|
||||
|
||||
// How fast the animated icon should move
|
||||
CONSTEXPR(uint32_t, statusIconAnimationMsec, 200, 200, 0)
|
||||
|
||||
// How often glean pings are sent
|
||||
CONSTEXPR(uint32_t, gleanTimeoutMsec, 1200000, 1000, 0)
|
||||
|
||||
// How often we check the surveys to be executed (no network requests are done
|
||||
// for this check)
|
||||
CONSTEXPR(uint32_t, surveyTimerMsec, 300000, 4000, 0)
|
||||
|
||||
#undef CONSTEXPR
|
||||
|
||||
#define PRODBETAEXPR(type, functionName, prod, beta) \
|
||||
inline type functionName() { return inProduction() ? prod : beta; }
|
||||
|
||||
constexpr const char* API_PRODUCTION_URL = "https://vpn.mozilla.org";
|
||||
constexpr const char* API_STAGING_URL =
|
||||
"https://stage-vpn.guardian.nonprod.cloudops.mozgcp.net";
|
||||
|
||||
constexpr const char* LOGO_URL = ":/ui/resources/logo-dock.png";
|
||||
|
||||
PRODBETAEXPR(const char*, fxaUrl, "https://api.accounts.firefox.com",
|
||||
"https://api-accounts.stage.mozaws.net")
|
||||
PRODBETAEXPR(
|
||||
const char*, balrogUrl,
|
||||
"https://aus5.mozilla.org/json/1/FirefoxVPN/%1/%2/release/update.json",
|
||||
"https://stage.balrog.nonprod.cloudops.mozgcp.net/json/1/FirefoxVPN/%1/%2/"
|
||||
"release-cdntest/update.json");
|
||||
PRODBETAEXPR(
|
||||
const char*, balrogRootCertFingerprint,
|
||||
"97e8ba9cf12fb3de53cc42a4e6577ed64df493c247b414fea036818d3823560e",
|
||||
"3c01446abe9036cea9a09acaa3a520ac628f20a7ae32ce861cb2efb70fa0c745");
|
||||
|
||||
#undef PRODBETAEXPR
|
||||
|
||||
constexpr const char* PLATFORM_NAME =
|
||||
#if defined(MZ_IOS)
|
||||
#if defined(MVPN_IOS)
|
||||
"ios"
|
||||
#elif defined(MZ_MACOS)
|
||||
#elif defined(MVPN_MACOS)
|
||||
"macos"
|
||||
#elif defined(MZ_LINUX)
|
||||
#elif defined(MVPN_LINUX)
|
||||
"linux"
|
||||
#elif defined(MZ_ANDROID)
|
||||
#elif defined(MVPN_ANDROID)
|
||||
"android"
|
||||
#elif defined(MZ_WINDOWS)
|
||||
#elif defined(MVPN_WINDOWS)
|
||||
"windows"
|
||||
#elif defined(UNIT_TEST) || defined(MZ_DUMMY)
|
||||
#elif defined(UNIT_TEST) || defined(MVPN_DUMMY)
|
||||
"dummy"
|
||||
#else
|
||||
# error "Unsupported platform"
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
constexpr const char* PLACEHOLDER_USER_DNS = "127.0.0.1";
|
||||
|
||||
};
|
||||
#endif // CONSTANTS_H
|
||||
|
||||
@@ -161,7 +161,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
||||
}
|
||||
#elif defined (Q_OS_MAC)
|
||||
switch (c) {
|
||||
case DockerContainer::WireGuard: return true;
|
||||
case DockerContainer::WireGuard: return false;
|
||||
case DockerContainer::Ipsec: return false;
|
||||
default: return true;
|
||||
}
|
||||
@@ -176,11 +176,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
||||
}
|
||||
|
||||
#elif defined (Q_OS_LINUX)
|
||||
switch (c) {
|
||||
case DockerContainer::WireGuard: return true;
|
||||
case DockerContainer::Ipsec: return false;
|
||||
default: return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
#else
|
||||
return false;
|
||||
|
||||
@@ -54,7 +54,7 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
||||
|
||||
script.replace("\r", "");
|
||||
|
||||
qDebug() << "ServerController::Run script";
|
||||
qDebug() << "Run script";
|
||||
|
||||
QString totalLine;
|
||||
const QStringList &lines = script.split("\n", Qt::SkipEmptyParts);
|
||||
@@ -79,16 +79,17 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
||||
continue;
|
||||
}
|
||||
|
||||
qDebug().noquote() << lineToExec;
|
||||
qDebug().noquote() << "EXEC" << lineToExec;
|
||||
Logger::appendSshLog("Run command:" + lineToExec);
|
||||
|
||||
|
||||
error = m_sshClient.executeCommand(lineToExec, cbReadStdOut, cbReadStdErr);
|
||||
if (error != ErrorCode::NoError) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug().noquote() << "ServerController::runScript finished\n";
|
||||
qDebug() << "ServerController::runScript finished\n";
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
@@ -193,6 +194,12 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
|
||||
};
|
||||
|
||||
*errorCode = runScript(credentials, script, cbReadStdOut);
|
||||
|
||||
qDebug().noquote() << "Copy file from container stdout : \n" << stdOut;
|
||||
|
||||
|
||||
qDebug().noquote() << "Copy file from container END : \n" ;
|
||||
|
||||
return QByteArray::fromHex(stdOut.toUtf8());
|
||||
}
|
||||
|
||||
@@ -209,6 +216,8 @@ ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credential
|
||||
localFile.write(data);
|
||||
localFile.close();
|
||||
|
||||
qDebug() << "remotePath" << remotePath;
|
||||
|
||||
error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName().toStdString(), remotePath.toStdString(), "non_desc");
|
||||
if (error != ErrorCode::NoError) {
|
||||
return error;
|
||||
@@ -233,6 +242,7 @@ ErrorCode ServerController::setupContainer(const ServerCredentials &credentials,
|
||||
QJsonObject &config, bool isUpdate)
|
||||
{
|
||||
qDebug().noquote() << "ServerController::setupContainer" << ContainerProps::containerToString(container);
|
||||
//qDebug().noquote() << QJsonDocument(config).toJson();
|
||||
ErrorCode e = ErrorCode::NoError;
|
||||
|
||||
e = isUserInSudo(credentials, container);
|
||||
@@ -420,6 +430,8 @@ ErrorCode ServerController::runContainerWorker(const ServerCredentials &credenti
|
||||
replaceVars(amnezia::scriptData(ProtocolScriptType::run_container, container),
|
||||
genVarsForScript(credentials, container, config)), cbReadStdOut);
|
||||
|
||||
qDebug() << "cbReadStdOut: " << stdOut;
|
||||
|
||||
|
||||
if (stdOut.contains("docker: Error response from daemon")) return ErrorCode::ServerDockerFailedError;
|
||||
|
||||
@@ -479,6 +491,7 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
||||
const QJsonObject &ssConfig = config.value(ProtocolProps::protoToString(Proto::ShadowSocks)).toObject();
|
||||
const QJsonObject &wireguarConfig = config.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject();
|
||||
const QJsonObject &sftpConfig = config.value(ProtocolProps::protoToString(Proto::Sftp)).toObject();
|
||||
//
|
||||
|
||||
Vars vars;
|
||||
|
||||
@@ -603,8 +616,10 @@ QString ServerController::replaceVars(const QString &script, const Vars &vars)
|
||||
{
|
||||
QString s = script;
|
||||
for (const QPair<QString, QString> &var : vars) {
|
||||
//qDebug() << "Replacing" << var.first << var.second;
|
||||
s.replace(var.first, var.second);
|
||||
}
|
||||
//qDebug().noquote() << script;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@@ -143,6 +143,8 @@ namespace libssh {
|
||||
{
|
||||
output = std::string(buffer, bytesRead);
|
||||
if (!output.empty()) {
|
||||
qDebug().noquote() << (isStdErr ? "stdErr" : "stdOut") << QString(output.c_str());
|
||||
|
||||
if (cbReadStdOut && !isStdErr){
|
||||
auto error = cbReadStdOut(output.c_str(), *this);
|
||||
if (error != ErrorCode::NoError) {
|
||||
@@ -255,6 +257,7 @@ namespace libssh {
|
||||
int bytesWritten = sftp_write(file, buffer, bufferSize);
|
||||
|
||||
std::string chunk(buffer, bufferSize);
|
||||
qDebug() << "sftp write: " << QString(chunk.c_str());
|
||||
|
||||
if (bytesWritten != bufferSize) {
|
||||
fin.close();
|
||||
@@ -269,6 +272,7 @@ namespace libssh {
|
||||
fin.read(buffer, lastChunkSize);
|
||||
|
||||
std::string chunk(buffer, lastChunkSize);
|
||||
qDebug() << "sftp write: " << QString(chunk.c_str());
|
||||
|
||||
int bytesWritten = sftp_write(file, buffer, lastChunkSize);
|
||||
|
||||
|
||||
@@ -1,519 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "daemon.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QTimer>
|
||||
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
constexpr const char* JSON_ALLOWEDIPADDRESSRANGES = "allowedIPAddressRanges";
|
||||
constexpr int HANDSHAKE_POLL_MSEC = 250;
|
||||
|
||||
namespace {
|
||||
|
||||
Logger logger("Daemon");
|
||||
|
||||
Daemon* s_daemon = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
Daemon::Daemon(QObject* parent) : QObject(parent) {
|
||||
MZ_COUNT_CTOR(Daemon);
|
||||
|
||||
logger.debug() << "Daemon created";
|
||||
|
||||
Q_ASSERT(s_daemon == nullptr);
|
||||
s_daemon = this;
|
||||
|
||||
m_handshakeTimer.setSingleShot(true);
|
||||
connect(&m_handshakeTimer, &QTimer::timeout, this, &Daemon::checkHandshake);
|
||||
}
|
||||
|
||||
Daemon::~Daemon() {
|
||||
MZ_COUNT_DTOR(Daemon);
|
||||
|
||||
logger.debug() << "Daemon released";
|
||||
|
||||
Q_ASSERT(s_daemon == this);
|
||||
s_daemon = nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
Daemon* Daemon::instance() {
|
||||
Q_ASSERT(s_daemon);
|
||||
return s_daemon;
|
||||
}
|
||||
|
||||
bool Daemon::activate(const InterfaceConfig& config) {
|
||||
Q_ASSERT(wgutils() != nullptr);
|
||||
|
||||
// There are 3 possible scenarios in which this method is called:
|
||||
//
|
||||
// 1. the VPN is off: the method tries to enable the VPN.
|
||||
// 2. the VPN is on and the platform doesn't support the server-switching:
|
||||
// this method calls deactivate() and then it continues as 1.
|
||||
// 3. the VPN is on and the platform supports the server-switching: this
|
||||
// method calls switchServer().
|
||||
//
|
||||
// At the end, if the activation succeds, the `connected` signal is emitted.
|
||||
logger.debug() << "Activating interface";
|
||||
|
||||
if (m_connections.contains(config.m_hopindex)) {
|
||||
if (supportServerSwitching(config)) {
|
||||
logger.debug() << "Already connected. Server switching supported.";
|
||||
|
||||
if (!switchServer(config)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (supportDnsUtils() && !dnsutils()->restoreResolvers()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!maybeUpdateResolvers(config)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool status = run(Switch, config);
|
||||
logger.debug() << "Connection status:" << status;
|
||||
if (status) {
|
||||
m_connections[config.m_hopindex] = ConnectionState(config);
|
||||
m_handshakeTimer.start(HANDSHAKE_POLL_MSEC);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
logger.warning() << "Already connected. Server switching not supported.";
|
||||
if (!deactivate(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_ASSERT(!m_connections.contains(config.m_hopindex));
|
||||
return activate(config);
|
||||
}
|
||||
|
||||
prepareActivation(config);
|
||||
|
||||
// Bring up the wireguard interface if not already done.
|
||||
if (!wgutils()->interfaceExists()) {
|
||||
if (!wgutils()->addInterface(config)) {
|
||||
logger.error() << "Interface creation failed.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Configure routing for excluded addresses.
|
||||
for (const QString& i : config.m_excludedAddresses) {
|
||||
QHostAddress address(i);
|
||||
if (m_excludedAddrSet.contains(address)) {
|
||||
m_excludedAddrSet[address]++;
|
||||
continue;
|
||||
}
|
||||
wgutils()->addExclusionRoute(address);
|
||||
m_excludedAddrSet[address] = 1;
|
||||
}
|
||||
|
||||
// Add the peer to this interface.
|
||||
if (!wgutils()->updatePeer(config)) {
|
||||
logger.error() << "Peer creation failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!maybeUpdateResolvers(config)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (supportIPUtils()) {
|
||||
if (!iputils()->addInterfaceIPs(config)) {
|
||||
return false;
|
||||
}
|
||||
if (!iputils()->setMTUAndUp(config)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// set routing
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
if (!wgutils()->updateRoutePrefix(ip, config.m_hopindex)) {
|
||||
logger.debug() << "Routing configuration failed for"
|
||||
<< logger.sensitive(ip.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool status = run(Up, config);
|
||||
logger.debug() << "Connection status:" << status;
|
||||
if (status) {
|
||||
m_connections[config.m_hopindex] = ConnectionState(config);
|
||||
m_handshakeTimer.start(HANDSHAKE_POLL_MSEC);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) {
|
||||
if ((config.m_hopindex == 0) && supportDnsUtils()) {
|
||||
QList<QHostAddress> resolvers;
|
||||
resolvers.append(QHostAddress(config.m_dnsServer));
|
||||
|
||||
// If the DNS is not the Gateway, it's a user defined DNS
|
||||
// thus, not add any other :)
|
||||
if (config.m_dnsServer == config.m_serverIpv4Gateway) {
|
||||
resolvers.append(QHostAddress(config.m_serverIpv6Gateway));
|
||||
}
|
||||
|
||||
if (!dnsutils()->updateResolvers(wgutils()->interfaceName(), resolvers)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool Daemon::parseStringList(const QJsonObject& obj, const QString& name,
|
||||
QStringList& list) {
|
||||
if (obj.contains(name)) {
|
||||
QJsonValue value = obj.value(name);
|
||||
if (!value.isArray()) {
|
||||
logger.error() << name << "is not an array";
|
||||
return false;
|
||||
}
|
||||
QJsonArray array = value.toArray();
|
||||
for (const QJsonValue& i : array) {
|
||||
if (!i.isString()) {
|
||||
logger.error() << name << "must contain only strings";
|
||||
return false;
|
||||
}
|
||||
list.append(i.toString());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||
#define GETVALUE(name, where, jsontype) \
|
||||
if (!obj.contains(name)) { \
|
||||
logger.debug() << name << " missing in the jsonConfig input"; \
|
||||
return false; \
|
||||
} else { \
|
||||
QJsonValue value = obj.value(name); \
|
||||
if (value.type() != QJsonValue::jsontype) { \
|
||||
logger.error() << name << " is not a " #jsontype; \
|
||||
return false; \
|
||||
} \
|
||||
where = value.to##jsontype(); \
|
||||
}
|
||||
|
||||
GETVALUE("privateKey", config.m_privateKey, String);
|
||||
GETVALUE("serverPublicKey", config.m_serverPublicKey, String);
|
||||
GETVALUE("serverPort", config.m_serverPort, Double);
|
||||
GETVALUE("serverPskKey", config.m_serverPskKey, String);
|
||||
|
||||
config.m_deviceIpv4Address = obj.value("deviceIpv4Address").toString();
|
||||
config.m_deviceIpv6Address = obj.value("deviceIpv6Address").toString();
|
||||
if (config.m_deviceIpv4Address.isNull() &&
|
||||
config.m_deviceIpv6Address.isNull()) {
|
||||
logger.warning() << "no device addresses found in jsonConfig input";
|
||||
return false;
|
||||
}
|
||||
config.m_serverIpv4AddrIn = obj.value("serverIpv4AddrIn").toString();
|
||||
config.m_serverIpv6AddrIn = obj.value("serverIpv6AddrIn").toString();
|
||||
if (config.m_serverIpv4AddrIn.isNull() &&
|
||||
config.m_serverIpv6AddrIn.isNull()) {
|
||||
logger.error() << "no server addresses found in jsonConfig input";
|
||||
return false;
|
||||
}
|
||||
config.m_serverIpv4Gateway = obj.value("serverIpv4Gateway").toString();
|
||||
config.m_serverIpv6Gateway = obj.value("serverIpv6Gateway").toString();
|
||||
|
||||
if (!obj.contains("dnsServer")) {
|
||||
config.m_dnsServer = QString();
|
||||
} else {
|
||||
QJsonValue value = obj.value("dnsServer");
|
||||
if (!value.isString()) {
|
||||
logger.error() << "dnsServer is not a string";
|
||||
return false;
|
||||
}
|
||||
config.m_dnsServer = value.toString();
|
||||
}
|
||||
|
||||
if (!obj.contains("hopindex")) {
|
||||
config.m_hopindex = 0;
|
||||
} else {
|
||||
QJsonValue value = obj.value("hopindex");
|
||||
if (!value.isDouble()) {
|
||||
logger.error() << "hopindex is not a number";
|
||||
return false;
|
||||
}
|
||||
config.m_hopindex = value.toInt();
|
||||
}
|
||||
|
||||
if (!obj.contains(JSON_ALLOWEDIPADDRESSRANGES)) {
|
||||
logger.error() << JSON_ALLOWEDIPADDRESSRANGES
|
||||
<< "missing in the jsonconfig input";
|
||||
return false;
|
||||
} else {
|
||||
QJsonValue value = obj.value(JSON_ALLOWEDIPADDRESSRANGES);
|
||||
if (!value.isArray()) {
|
||||
logger.error() << JSON_ALLOWEDIPADDRESSRANGES << "is not an array";
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonArray array = value.toArray();
|
||||
for (const QJsonValue& i : array) {
|
||||
if (!i.isObject()) {
|
||||
logger.error() << JSON_ALLOWEDIPADDRESSRANGES
|
||||
<< "must contain only objects";
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonObject ipObj = i.toObject();
|
||||
|
||||
QJsonValue address = ipObj.value("address");
|
||||
if (!address.isString()) {
|
||||
logger.error() << JSON_ALLOWEDIPADDRESSRANGES
|
||||
<< "objects must have a string address";
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue range = ipObj.value("range");
|
||||
if (!range.isDouble()) {
|
||||
logger.error() << JSON_ALLOWEDIPADDRESSRANGES
|
||||
<< "object must have a numberic range";
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue isIpv6 = ipObj.value("isIpv6");
|
||||
if (!isIpv6.isBool()) {
|
||||
logger.error() << JSON_ALLOWEDIPADDRESSRANGES
|
||||
<< "object must have a boolean isIpv6";
|
||||
return false;
|
||||
}
|
||||
|
||||
config.m_allowedIPAddressRanges.append(
|
||||
IPAddress(QHostAddress(address.toString()), range.toInt()));
|
||||
}
|
||||
|
||||
// Sort allowed IPs by decreasing prefix length.
|
||||
std::sort(config.m_allowedIPAddressRanges.begin(),
|
||||
config.m_allowedIPAddressRanges.end(),
|
||||
[&](const IPAddress& a, const IPAddress& b) -> bool {
|
||||
return a.prefixLength() > b.prefixLength();
|
||||
});
|
||||
}
|
||||
|
||||
if (!parseStringList(obj, "excludedAddresses", config.m_excludedAddresses)) {
|
||||
return false;
|
||||
}
|
||||
if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Daemon::deactivate(bool emitSignals) {
|
||||
Q_ASSERT(wgutils() != nullptr);
|
||||
|
||||
// Deactivate the main interface.
|
||||
if (m_connections.contains(0)) {
|
||||
const ConnectionState& state = m_connections.value(0);
|
||||
if (!run(Down, state.m_config)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (emitSignals) {
|
||||
emit disconnected();
|
||||
}
|
||||
|
||||
// Cleanup DNS
|
||||
if (supportDnsUtils() && !dnsutils()->restoreResolvers()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!wgutils()->interfaceExists()) {
|
||||
logger.warning() << "Wireguard interface does not exist.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cleanup peers and routing
|
||||
for (const ConnectionState& state : m_connections) {
|
||||
const InterfaceConfig& config = state.m_config;
|
||||
logger.debug() << "Deleting routes for hop" << config.m_hopindex;
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
wgutils()->deleteRoutePrefix(ip, config.m_hopindex);
|
||||
}
|
||||
wgutils()->deletePeer(config);
|
||||
}
|
||||
|
||||
// Cleanup routing for excluded addresses.
|
||||
for (auto iterator = m_excludedAddrSet.constBegin();
|
||||
iterator != m_excludedAddrSet.constEnd(); ++iterator) {
|
||||
wgutils()->deleteExclusionRoute(iterator.key());
|
||||
}
|
||||
m_excludedAddrSet.clear();
|
||||
|
||||
// Delete the interface
|
||||
if (!wgutils()->deleteInterface()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_connections.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
QString Daemon::logs() {
|
||||
return {};
|
||||
}
|
||||
|
||||
void Daemon::cleanLogs() { }
|
||||
|
||||
bool Daemon::supportServerSwitching(const InterfaceConfig& config) const {
|
||||
if (!m_connections.contains(config.m_hopindex)) {
|
||||
return false;
|
||||
}
|
||||
const InterfaceConfig& current =
|
||||
m_connections.value(config.m_hopindex).m_config;
|
||||
|
||||
return current.m_privateKey == config.m_privateKey &&
|
||||
current.m_deviceIpv4Address == config.m_deviceIpv4Address &&
|
||||
current.m_deviceIpv6Address == config.m_deviceIpv6Address &&
|
||||
current.m_serverIpv4Gateway == config.m_serverIpv4Gateway &&
|
||||
current.m_serverIpv6Gateway == config.m_serverIpv6Gateway;
|
||||
}
|
||||
|
||||
bool Daemon::switchServer(const InterfaceConfig& config) {
|
||||
Q_ASSERT(wgutils() != nullptr);
|
||||
|
||||
logger.debug() << "Switching server for hop" << config.m_hopindex;
|
||||
|
||||
Q_ASSERT(m_connections.contains(config.m_hopindex));
|
||||
const InterfaceConfig& lastConfig =
|
||||
m_connections.value(config.m_hopindex).m_config;
|
||||
|
||||
// Configure routing for new excluded addresses.
|
||||
for (const QString& i : config.m_excludedAddresses) {
|
||||
QHostAddress address(i);
|
||||
if (m_excludedAddrSet.contains(address)) {
|
||||
m_excludedAddrSet[address]++;
|
||||
continue;
|
||||
}
|
||||
wgutils()->addExclusionRoute(address);
|
||||
m_excludedAddrSet[address] = 1;
|
||||
}
|
||||
|
||||
// Activate the new peer and its routes.
|
||||
if (!wgutils()->updatePeer(config)) {
|
||||
logger.error() << "Server switch failed to update the wireguard interface";
|
||||
return false;
|
||||
}
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
if (!wgutils()->updateRoutePrefix(ip, config.m_hopindex)) {
|
||||
logger.error() << "Server switch failed to update the routing table";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove routing entries for the old peer.
|
||||
for (const QString& i : lastConfig.m_excludedAddresses) {
|
||||
QHostAddress address(i);
|
||||
Q_ASSERT(m_excludedAddrSet.contains(address));
|
||||
if (m_excludedAddrSet[address] > 1) {
|
||||
m_excludedAddrSet[address]--;
|
||||
continue;
|
||||
}
|
||||
wgutils()->deleteExclusionRoute(address);
|
||||
m_excludedAddrSet.remove(address);
|
||||
}
|
||||
for (const IPAddress& ip : lastConfig.m_allowedIPAddressRanges) {
|
||||
if (!config.m_allowedIPAddressRanges.contains(ip)) {
|
||||
wgutils()->deleteRoutePrefix(ip, config.m_hopindex);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the old peer if it is no longer necessary.
|
||||
if (config.m_serverPublicKey != lastConfig.m_serverPublicKey) {
|
||||
if (!wgutils()->deletePeer(lastConfig)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_connections[config.m_hopindex] = ConnectionState(config);
|
||||
return true;
|
||||
}
|
||||
|
||||
QJsonObject Daemon::getStatus() {
|
||||
Q_ASSERT(wgutils() != nullptr);
|
||||
QJsonObject json;
|
||||
logger.debug() << "Status request";
|
||||
|
||||
if (!m_connections.contains(0) || !wgutils()->interfaceExists()) {
|
||||
json.insert("connected", QJsonValue(false));
|
||||
return json;
|
||||
}
|
||||
|
||||
const ConnectionState& connection = m_connections.value(0);
|
||||
QList<WireguardUtils::PeerStatus> peers = wgutils()->getPeerStatus();
|
||||
for (const WireguardUtils::PeerStatus& status : peers) {
|
||||
if (status.m_pubkey != connection.m_config.m_serverPublicKey) {
|
||||
continue;
|
||||
}
|
||||
json.insert("connected", QJsonValue(true));
|
||||
json.insert("serverIpv4Gateway",
|
||||
QJsonValue(connection.m_config.m_serverIpv4Gateway));
|
||||
json.insert("deviceIpv4Address",
|
||||
QJsonValue(connection.m_config.m_deviceIpv4Address));
|
||||
json.insert("date", connection.m_date.toString());
|
||||
json.insert("txBytes", QJsonValue(status.m_txBytes));
|
||||
json.insert("rxBytes", QJsonValue(status.m_rxBytes));
|
||||
return json;
|
||||
}
|
||||
|
||||
json.insert("connected", QJsonValue(false));
|
||||
return json;
|
||||
}
|
||||
|
||||
void Daemon::checkHandshake() {
|
||||
Q_ASSERT(wgutils() != nullptr);
|
||||
|
||||
logger.debug() << "Checking for handshake...";
|
||||
|
||||
int pendingHandshakes = 0;
|
||||
QList<WireguardUtils::PeerStatus> peers = wgutils()->getPeerStatus();
|
||||
for (ConnectionState& connection : m_connections) {
|
||||
const InterfaceConfig& config = connection.m_config;
|
||||
if (connection.m_date.isValid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the handshake has completed.
|
||||
for (const WireguardUtils::PeerStatus& status : peers) {
|
||||
if (config.m_serverPublicKey != status.m_pubkey) {
|
||||
continue;
|
||||
}
|
||||
if (status.m_handshake != 0) {
|
||||
connection.m_date.setMSecsSinceEpoch(status.m_handshake);
|
||||
emit connected(status.m_pubkey);
|
||||
}
|
||||
}
|
||||
|
||||
if (!connection.m_date.isValid()) {
|
||||
pendingHandshakes++;
|
||||
}
|
||||
}
|
||||
|
||||
// Check again if there were connections that haven't completed a handshake.
|
||||
if (pendingHandshakes > 0) {
|
||||
m_handshakeTimer.start(HANDSHAKE_POLL_MSEC);
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DAEMON_H
|
||||
#define DAEMON_H
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
|
||||
#include "dnsutils.h"
|
||||
#include "interfaceconfig.h"
|
||||
#include "iputils.h"
|
||||
#include "wireguardutils.h"
|
||||
|
||||
class Daemon : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Op {
|
||||
Up,
|
||||
Down,
|
||||
Switch,
|
||||
};
|
||||
|
||||
explicit Daemon(QObject* parent);
|
||||
~Daemon();
|
||||
|
||||
static Daemon* instance();
|
||||
|
||||
static bool parseConfig(const QJsonObject& obj, InterfaceConfig& config);
|
||||
|
||||
virtual bool activate(const InterfaceConfig& config);
|
||||
virtual bool deactivate(bool emitSignals = true);
|
||||
virtual QJsonObject getStatus();
|
||||
|
||||
// Callback before any Activating measure is done
|
||||
virtual void prepareActivation(const InterfaceConfig& config){
|
||||
Q_UNUSED(config)};
|
||||
|
||||
QString logs();
|
||||
void cleanLogs();
|
||||
|
||||
signals:
|
||||
void connected(const QString& pubkey);
|
||||
void disconnected();
|
||||
void backendFailure();
|
||||
|
||||
private:
|
||||
bool maybeUpdateResolvers(const InterfaceConfig& config);
|
||||
|
||||
protected:
|
||||
virtual bool run(Op op, const InterfaceConfig& config) {
|
||||
Q_UNUSED(op);
|
||||
Q_UNUSED(config);
|
||||
return true;
|
||||
}
|
||||
virtual bool supportServerSwitching(const InterfaceConfig& config) const;
|
||||
virtual bool switchServer(const InterfaceConfig& config);
|
||||
virtual WireguardUtils* wgutils() const = 0;
|
||||
virtual bool supportIPUtils() const { return false; }
|
||||
virtual IPUtils* iputils() { return nullptr; }
|
||||
virtual bool supportDnsUtils() const { return false; }
|
||||
virtual DnsUtils* dnsutils() { return nullptr; }
|
||||
|
||||
static bool parseStringList(const QJsonObject& obj, const QString& name,
|
||||
QStringList& list);
|
||||
|
||||
void checkHandshake();
|
||||
|
||||
class ConnectionState {
|
||||
public:
|
||||
ConnectionState(){};
|
||||
ConnectionState(const InterfaceConfig& config) { m_config = config; }
|
||||
QDateTime m_date;
|
||||
InterfaceConfig m_config;
|
||||
};
|
||||
QMap<int, ConnectionState> m_connections;
|
||||
QHash<QHostAddress, int> m_excludedAddrSet;
|
||||
QTimer m_handshakeTimer;
|
||||
};
|
||||
|
||||
#endif // DAEMON_H
|
||||
@@ -1,98 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "daemonlocalserver.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QLocalSocket>
|
||||
|
||||
#include "daemonlocalserverconnection.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
#ifdef MZ_MACOS
|
||||
# include <sys/stat.h>
|
||||
# include <sys/types.h>
|
||||
# include <unistd.h>
|
||||
|
||||
constexpr const char* TMP_PATH = "/tmp/amneziavpn.socket";
|
||||
constexpr const char* VAR_PATH = "/var/run/amneziavpn/daemon.socket";
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
Logger logger("DaemonLocalServer");
|
||||
} // namespace
|
||||
|
||||
DaemonLocalServer::DaemonLocalServer(QObject* parent) : QObject(parent) {
|
||||
MZ_COUNT_CTOR(DaemonLocalServer);
|
||||
}
|
||||
|
||||
DaemonLocalServer::~DaemonLocalServer() { MZ_COUNT_DTOR(DaemonLocalServer); }
|
||||
|
||||
bool DaemonLocalServer::initialize() {
|
||||
m_server.setSocketOptions(QLocalServer::WorldAccessOption);
|
||||
|
||||
QString path = daemonPath();
|
||||
logger.debug() << "Server path:" << path;
|
||||
|
||||
if (QFileInfo::exists(path)) {
|
||||
QFile::remove(path);
|
||||
}
|
||||
|
||||
if (!m_server.listen(path)) {
|
||||
logger.error() << "Failed to listen the daemon path";
|
||||
return false;
|
||||
}
|
||||
|
||||
connect(&m_server, &QLocalServer::newConnection, [&] {
|
||||
logger.debug() << "New connection received";
|
||||
|
||||
if (!m_server.hasPendingConnections()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QLocalSocket* socket = m_server.nextPendingConnection();
|
||||
Q_ASSERT(socket);
|
||||
|
||||
DaemonLocalServerConnection* connection =
|
||||
new DaemonLocalServerConnection(&m_server, socket);
|
||||
connect(socket, &QLocalSocket::disconnected, connection,
|
||||
&DaemonLocalServerConnection::deleteLater);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QString DaemonLocalServer::daemonPath() const {
|
||||
#if defined(MZ_WINDOWS)
|
||||
return "\\\\.\\pipe\\amneziavpn";
|
||||
#elif defined(MZ_MACOS)
|
||||
QDir dir("/var/run");
|
||||
if (!dir.exists()) {
|
||||
logger.warning() << "/var/run doesn't exist. Fallback /tmp.";
|
||||
return TMP_PATH;
|
||||
}
|
||||
|
||||
if (dir.exists("amneziavpn")) {
|
||||
logger.debug() << "/var/run/amneziavpn seems to be usable";
|
||||
return VAR_PATH;
|
||||
}
|
||||
|
||||
if (!dir.mkdir("amneziavpn")) {
|
||||
logger.warning() << "Failed to create /var/run/amneziavpn";
|
||||
return TMP_PATH;
|
||||
}
|
||||
|
||||
if (chmod("/var/run/amneziavpn", S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
|
||||
logger.warning()
|
||||
<< "Failed to set the right permissions to /var/run/amneziavpn";
|
||||
return TMP_PATH;
|
||||
}
|
||||
|
||||
return VAR_PATH;
|
||||
#else
|
||||
# error Unsupported platform
|
||||
#endif
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DAEMONLOCALSERVER_H
|
||||
#define DAEMONLOCALSERVER_H
|
||||
|
||||
#include <QLocalServer>
|
||||
|
||||
class DaemonLocalServer final : public QObject {
|
||||
Q_DISABLE_COPY_MOVE(DaemonLocalServer)
|
||||
|
||||
public:
|
||||
explicit DaemonLocalServer(QObject* parent);
|
||||
~DaemonLocalServer();
|
||||
|
||||
bool initialize();
|
||||
|
||||
private:
|
||||
QString daemonPath() const;
|
||||
|
||||
private:
|
||||
QLocalServer m_server;
|
||||
};
|
||||
|
||||
#endif // DAEMONLOCALSERVER_H
|
||||
@@ -1,162 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "daemonlocalserverconnection.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QLocalSocket>
|
||||
|
||||
#include "daemon.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace {
|
||||
Logger logger("DaemonLocalServerConnection");
|
||||
}
|
||||
|
||||
DaemonLocalServerConnection::DaemonLocalServerConnection(QObject* parent,
|
||||
QLocalSocket* socket)
|
||||
: QObject(parent) {
|
||||
MZ_COUNT_CTOR(DaemonLocalServerConnection);
|
||||
|
||||
logger.debug() << "Connection created";
|
||||
|
||||
Q_ASSERT(socket);
|
||||
m_socket = socket;
|
||||
|
||||
connect(m_socket, &QLocalSocket::readyRead, this,
|
||||
&DaemonLocalServerConnection::readData);
|
||||
|
||||
Daemon* daemon = Daemon::instance();
|
||||
connect(daemon, &Daemon::connected, this,
|
||||
&DaemonLocalServerConnection::connected);
|
||||
connect(daemon, &Daemon::disconnected, this,
|
||||
&DaemonLocalServerConnection::disconnected);
|
||||
connect(daemon, &Daemon::backendFailure, this,
|
||||
&DaemonLocalServerConnection::backendFailure);
|
||||
}
|
||||
|
||||
DaemonLocalServerConnection::~DaemonLocalServerConnection() {
|
||||
MZ_COUNT_DTOR(DaemonLocalServerConnection);
|
||||
|
||||
logger.debug() << "Connection released";
|
||||
}
|
||||
|
||||
void DaemonLocalServerConnection::readData() {
|
||||
logger.debug() << "Read Data";
|
||||
|
||||
Q_ASSERT(m_socket);
|
||||
|
||||
while (true) {
|
||||
int pos = m_buffer.indexOf("\n");
|
||||
if (pos == -1) {
|
||||
QByteArray input = m_socket->readAll();
|
||||
if (input.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
m_buffer.append(input);
|
||||
continue;
|
||||
}
|
||||
|
||||
QByteArray line = m_buffer.left(pos);
|
||||
m_buffer.remove(0, pos + 1);
|
||||
|
||||
QByteArray command(line);
|
||||
command = command.trimmed();
|
||||
|
||||
if (command.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
parseCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
|
||||
QJsonDocument json = QJsonDocument::fromJson(data);
|
||||
if (!json.isObject()) {
|
||||
logger.error() << "Invalid input";
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject obj = json.object();
|
||||
QJsonValue typeValue = obj.value("type");
|
||||
if (!typeValue.isString()) {
|
||||
logger.warning() << "No type command. Ignoring request.";
|
||||
return;
|
||||
}
|
||||
QString type = typeValue.toString();
|
||||
|
||||
logger.debug() << "Command received:" << type;
|
||||
|
||||
if (type == "activate") {
|
||||
InterfaceConfig config;
|
||||
if (!Daemon::parseConfig(obj, config)) {
|
||||
logger.error() << "Invalid configuration";
|
||||
emit disconnected();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Daemon::instance()->activate(config)) {
|
||||
logger.error() << "Failed to activate the interface";
|
||||
emit disconnected();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "deactivate") {
|
||||
Daemon::instance()->deactivate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "status") {
|
||||
QJsonObject obj = Daemon::instance()->getStatus();
|
||||
obj.insert("type", "status");
|
||||
m_socket->write(QJsonDocument(obj).toJson(QJsonDocument::Compact));
|
||||
m_socket->write("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "logs") {
|
||||
QJsonObject obj;
|
||||
obj.insert("type", "logs");
|
||||
obj.insert("logs", Daemon::instance()->logs().replace("\n", "|"));
|
||||
m_socket->write(QJsonDocument(obj).toJson(QJsonDocument::Compact));
|
||||
m_socket->write("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "cleanlogs") {
|
||||
Daemon::instance()->cleanLogs();
|
||||
return;
|
||||
}
|
||||
|
||||
logger.warning() << "Invalid command:" << type;
|
||||
}
|
||||
|
||||
void DaemonLocalServerConnection::connected(const QString& pubkey) {
|
||||
QJsonObject obj;
|
||||
obj.insert("type", "connected");
|
||||
obj.insert("pubkey", QJsonValue(pubkey));
|
||||
write(obj);
|
||||
}
|
||||
|
||||
void DaemonLocalServerConnection::disconnected() {
|
||||
QJsonObject obj;
|
||||
obj.insert("type", "disconnected");
|
||||
write(obj);
|
||||
}
|
||||
|
||||
void DaemonLocalServerConnection::backendFailure() {
|
||||
QJsonObject obj;
|
||||
obj.insert("type", "backendFailure");
|
||||
write(obj);
|
||||
}
|
||||
|
||||
void DaemonLocalServerConnection::write(const QJsonObject& obj) {
|
||||
m_socket->write(QJsonDocument(obj).toJson(QJsonDocument::Compact));
|
||||
m_socket->write("\n");
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DAEMONLOCALSERVERCONNECTION_H
|
||||
#define DAEMONLOCALSERVERCONNECTION_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QLocalSocket;
|
||||
|
||||
class DaemonLocalServerConnection final : public QObject {
|
||||
Q_DISABLE_COPY_MOVE(DaemonLocalServerConnection)
|
||||
|
||||
public:
|
||||
DaemonLocalServerConnection(QObject* parent, QLocalSocket* socket);
|
||||
~DaemonLocalServerConnection();
|
||||
|
||||
private:
|
||||
void readData();
|
||||
|
||||
void parseCommand(const QByteArray& json);
|
||||
|
||||
void connected(const QString& pubkey);
|
||||
void disconnected();
|
||||
void backendFailure();
|
||||
|
||||
void write(const QJsonObject& obj);
|
||||
|
||||
private:
|
||||
QLocalSocket* m_socket = nullptr;
|
||||
|
||||
QByteArray m_buffer;
|
||||
};
|
||||
|
||||
#endif // DAEMONLOCALSERVERCONNECTION_H
|
||||
@@ -1,34 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DNSUTILS_H
|
||||
#define DNSUTILS_H
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QString>
|
||||
|
||||
#include "dnsutils.h"
|
||||
|
||||
class DnsUtils : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DnsUtils(QObject* parent) : QObject(parent){};
|
||||
virtual ~DnsUtils() = default;
|
||||
|
||||
virtual bool updateResolvers(const QString& ifname,
|
||||
const QList<QHostAddress>& resolvers) {
|
||||
Q_UNUSED(ifname);
|
||||
Q_UNUSED(resolvers);
|
||||
qFatal("Have you forgotten to implement DnsUtils::updateResolvers?");
|
||||
return false;
|
||||
};
|
||||
|
||||
virtual bool restoreResolvers() {
|
||||
qFatal("Have you forgotten to implement DnsUtils::restoreResolvers?");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // DNSUTILS_H
|
||||
@@ -1,31 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef INTERFACECONFIG_H
|
||||
#define INTERFACECONFIG_H
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
#include "ipaddress.h"
|
||||
|
||||
struct InterfaceConfig {
|
||||
int m_hopindex = 0;
|
||||
QString m_privateKey;
|
||||
QString m_deviceIpv4Address;
|
||||
QString m_deviceIpv6Address;
|
||||
QString m_serverIpv4Gateway;
|
||||
QString m_serverIpv6Gateway;
|
||||
QString m_serverPublicKey;
|
||||
QString m_serverPskKey;
|
||||
QString m_serverIpv4AddrIn;
|
||||
QString m_serverIpv6AddrIn;
|
||||
QString m_dnsServer;
|
||||
int m_serverPort = 0;
|
||||
QList<IPAddress> m_allowedIPAddressRanges;
|
||||
QStringList m_excludedAddresses;
|
||||
QStringList m_vpnDisabledApps;
|
||||
};
|
||||
|
||||
#endif // INTERFACECONFIG_H
|
||||
@@ -1,31 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef IPUTILS_H
|
||||
#define IPUTILS_H
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QObject>
|
||||
|
||||
#include "interfaceconfig.h"
|
||||
|
||||
class IPUtils : public QObject {
|
||||
public:
|
||||
explicit IPUtils(QObject* parent) : QObject(parent){};
|
||||
virtual ~IPUtils() = default;
|
||||
|
||||
virtual bool addInterfaceIPs(const InterfaceConfig& config) {
|
||||
Q_UNUSED(config);
|
||||
qFatal("Have you forgotten to implement IPUtils::addInterfaceIPs?");
|
||||
return false;
|
||||
};
|
||||
|
||||
virtual bool setMTUAndUp(const InterfaceConfig& config) {
|
||||
Q_UNUSED(config);
|
||||
qFatal("Have you forgotten to implement IPUtils::setMTUAndUp?");
|
||||
return false;
|
||||
};
|
||||
};
|
||||
|
||||
#endif // IPUTILS_H
|
||||
@@ -1,51 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef WIREGUARDUTILS_H
|
||||
#define WIREGUARDUTILS_H
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QHostAddress>
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
#include "interfaceconfig.h"
|
||||
|
||||
constexpr const char* WG_INTERFACE = "moz0";
|
||||
|
||||
constexpr uint16_t WG_KEEPALIVE_PERIOD = 60;
|
||||
|
||||
class WireguardUtils : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
class PeerStatus {
|
||||
public:
|
||||
PeerStatus(const QString& pubkey = QString()) { m_pubkey = pubkey; }
|
||||
QString m_pubkey;
|
||||
qint64 m_handshake = 0;
|
||||
qint64 m_rxBytes = 0;
|
||||
qint64 m_txBytes = 0;
|
||||
};
|
||||
|
||||
explicit WireguardUtils(QObject* parent) : QObject(parent){};
|
||||
virtual ~WireguardUtils() = default;
|
||||
|
||||
virtual bool interfaceExists() = 0;
|
||||
virtual QString interfaceName() { return WG_INTERFACE; }
|
||||
virtual bool addInterface(const InterfaceConfig& config) = 0;
|
||||
virtual bool deleteInterface() = 0;
|
||||
|
||||
virtual bool updatePeer(const InterfaceConfig& config) = 0;
|
||||
virtual bool deletePeer(const InterfaceConfig& config) = 0;
|
||||
virtual QList<PeerStatus> getPeerStatus() = 0;
|
||||
|
||||
virtual bool updateRoutePrefix(const IPAddress& prefix, int hopindex) = 0;
|
||||
virtual bool deleteRoutePrefix(const IPAddress& prefix, int hopindex) = 0;
|
||||
|
||||
virtual bool addExclusionRoute(const QHostAddress& address) = 0;
|
||||
virtual bool deleteExclusionRoute(const QHostAddress& address) = 0;
|
||||
};
|
||||
|
||||
#endif // WIREGUARDUTILS_H
|
||||
|
Before Width: | Height: | Size: 151 KiB |
@@ -1,2 +0,0 @@
|
||||
File AmneziaVPN_Full_logo.svg licensed by CC-BY-SA 4.0
|
||||
Full license text - https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt
|
||||
|
After Width: | Height: | Size: 499 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "icon_40x40.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_60x60.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_58x58.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_87x87.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_80x80.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_120x120.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_120x120-1.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_180x180.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_1024x1024.png",
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 499 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1,116 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "icon-ios-20@2x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-ios-20@3x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-ios-29@2x-1.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-ios-29@3x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-ios-40@2x-1.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-ios-40@3x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-ios-60@2x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-ios-60@3x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-ios-20@1x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-ios-20@2x-1.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-ios-29@1x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-ios-29@2x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-ios-40@1x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-ios-40@2x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-ios-76@1x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-ios-76@2x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-ios-83.5@2x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "83.5x83.5"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-ios-1024@1x.png",
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 407 B |
|
After Width: | Height: | Size: 721 B |
|
After Width: | Height: | Size: 721 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 596 B |
|
After Width: | Height: | Size: 975 B |
|
After Width: | Height: | Size: 975 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 744 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -80,92 +80,6 @@
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>WireGuard config</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>org.amnezia.AmneziaVPN.wireguard-config</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>conf</string>
|
||||
<string>cfg</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
<string>text/plain</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>OpenVPN config</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>org.amnezia.AmneziaVPN.openvpn-config</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>ovpn</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
<string>text/plain</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>AmneziaVPN backup file</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>org.amnezia.AmneziaVPN.backup-config</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>backup</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
<string>text/plain</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Amnezia VPN config</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>org.amnezia.AmneziaVPN.amnezia-config</string>
|
||||
<string>org.amnezia.AmneziaVPN.wireguard-config</string>
|
||||
<string>org.amnezia.AmneziaVPN.openvpn-config</string>
|
||||
<string>org.amnezia.AmneziaVPN.backup-config</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
</plist>
|
||||
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 767 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |