Compare commits

..

1 Commits

Author SHA1 Message Date
vladimir.kuznetsov f41dc338bc bugfix: temporarily removed vless for api native configs 2025-07-16 10:31:10 +08:00
234 changed files with 8389 additions and 11363 deletions
+3 -64
View File
@@ -358,7 +358,7 @@ jobs:
- name: 'Setup xcode'
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '16.2.0'
xcode-version: '15.4.0'
- name: 'Install Qt'
uses: jurplel/install-qt-action@v3
@@ -402,67 +402,6 @@ jobs:
path: deploy/build/client/AmneziaVPN.app
retention-days: 7
Build-MacOS-NE:
runs-on: macos-latest
env:
QT_VERSION: 6.8.3
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
MAC_APP_CERT_CERT: ${{ secrets.MAC_APP_CERT_CERT }}
MAC_SIGNER_ID: ${{ secrets.MAC_SIGNER_ID }}
MAC_APP_CERT_PW: ${{ secrets.MAC_APP_CERT_PW }}
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
steps:
- name: 'Setup xcode'
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '16.2.0'
- name: 'Install Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'mac'
target: 'desktop'
arch: 'clang_64'
modules: 'qtremoteobjects qt5compat qtshadertools'
dir: ${{ runner.temp }}
setup-python: 'true'
set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Get sources'
uses: actions/checkout@v4
with:
submodules: 'true'
fetch-depth: 10
- name: 'Setup ccache'
uses: hendrikmuhs/ccache-action@v1.2
- name: 'Build project'
run: |
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
bash deploy/build_macos_ne.sh
- name: 'Upload unpacked artifact'
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN_MacOS_unpacked
path: deploy/build/client/AmneziaVPN.app
retention-days: 7
# ------------------------------------------------------
Build-Android:
@@ -470,7 +409,7 @@ jobs:
env:
ANDROID_BUILD_PLATFORM: android-34
QT_VERSION: 6.6.3
QT_VERSION: 6.7.3
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
@@ -487,7 +426,7 @@ jobs:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'desktop'
arch: 'gcc_64'
arch: 'linux_gcc_64'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
py7zrversion: '==0.22.*'
-1
View File
@@ -9,7 +9,6 @@ deploy/build_32/*
deploy/build_64/*
winbuild*.bat
.cache/
.vscode/
# Qt-es
+5 -12
View File
@@ -1,9 +1,8 @@
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
set(AMNEZIAVPN_VERSION 4.8.11.0)
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
project(${PROJECT} VERSION 4.8.8.3
DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/"
)
@@ -12,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 1095)
set(APP_ANDROID_VERSION_CODE 2089)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")
@@ -32,19 +31,13 @@ set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(APPLE)
if(IOS)
set(CMAKE_OSX_ARCHITECTURES "arm64")
elseif(MACOS_NE)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64")
else()
set(CMAKE_OSX_ARCHITECTURES "x86_64")
endif()
if(APPLE AND NOT IOS)
set(CMAKE_OSX_ARCHITECTURES "x86_64")
endif()
add_subdirectory(client)
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
if(NOT IOS AND NOT ANDROID)
add_subdirectory(service)
include(${CMAKE_SOURCE_DIR}/deploy/installer/config.cmake)
+5 -5
View File
@@ -9,17 +9,17 @@
### [English]([https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md](https://github.com/amnezia-vpn/amnezia-client/tree/dev?tab=readme-ov-file#)) | [Русский](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md)
[Amnezia](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
[Amnezia](https://amnezia.org) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org)
### [Website](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) | [Alt website link](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en-mirror) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting)
### [Website](https://amnezia.org) | [Alt website link](https://storage.googleapis.com/amnezia/amnezia.org) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting)
> [!TIP]
> If the [Amnezia website](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en-mirror).
> If the [Amnezia website](https://amnezia.org) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/amnezia/amnezia.org ).
<a href="https://amnezia.org/en/downloads?utm_source=github&utm_campaign=amnezia_button-readme-en"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
<a href="https://storage.googleapis.com/amnezia/amnezia.org?m-path=/en/downloads&utm_source=github&utm_campaign=amnezia_button-readme-en-mirrow"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a>
<a href="https://amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
<a href="https://storage.googleapis.com/amnezia/q9p19109"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a>
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
+4 -4
View File
@@ -6,16 +6,16 @@
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
[AmneziaVPN](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
[AmneziaVPN](https://amnezia.org) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org)
### [Сайт](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru-mirror) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
### [Сайт](https://amnezia.org) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
> [!TIP]
> Если [сайт Amnezia](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru-mirror).
> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org).
<a href="https://storage.googleapis.com/amnezia/amnezia.org?m-path=/ru/downloads&utm_source=github&utm_campaign=amnezia_button-readme-ru-mirror"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website-ru.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
<a href="https://storage.googleapis.com/amnezia/q9p19109"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website-ru.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
[Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases)
+8 -18
View File
@@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
project(${PROJECT})
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER "Autogen")
set_property(GLOBAL PROPERTY AUTOMOC_TARGETS_FOLDER "Autogen")
@@ -52,9 +53,6 @@ endif()
qt_standard_project_setup()
qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
target_include_directories(${PROJECT} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
)
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
@@ -112,15 +110,6 @@ include_directories(
${CMAKE_CURRENT_BINARY_DIR}
)
if(MACOS_NE)
message("MACOS_NE is ON")
add_definitions(-DQ_OS_MAC)
add_definitions(-DMACOS_NE)
message("Add macros for MacOS Network Extension")
else()
message("MACOS_NE is OFF")
endif()
include_directories(mozilla)
include_directories(mozilla/shared)
include_directories(mozilla/models)
@@ -150,7 +139,7 @@ if(WIN32)
endif()
if(APPLE)
cmake_policy(SET CMP0099 NEW)
cmake_policy(SET CMP0099 OLD)
cmake_policy(SET CMP0114 NEW)
if(NOT BUILD_OSX_APP_IDENTIFIER)
@@ -169,6 +158,7 @@ if(APPLE)
set(CMAKE_XCODE_GENERATE_SCHEME FALSE)
set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${BUILD_VPN_DEVELOPMENT_TEAM})
set(CMAKE_XCODE_ATTRIBUTE_GROUP_ID_IOS ${BUILD_IOS_GROUP_IDENTIFIER})
endif()
if(LINUX AND NOT ANDROID)
@@ -176,7 +166,8 @@ if(LINUX AND NOT ANDROID)
link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/linux)
endif()
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
message("Client desktop build")
add_compile_definitions(AMNEZIA_DESKTOP)
endif()
@@ -187,9 +178,7 @@ endif()
if(IOS)
include(cmake/ios.cmake)
include(cmake/ios-arch-fixup.cmake)
elseif(APPLE AND MACOS_NE)
include(cmake/macos_ne.cmake)
elseif(APPLE)
elseif(APPLE AND NOT IOS)
include(cmake/osxtools.cmake)
include(cmake/macos.cmake)
endif()
@@ -210,7 +199,7 @@ elseif(APPLE AND NOT IOS)
set(DEPLOY_PLATFORM_PATH "macos")
endif()
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
if(NOT IOS AND NOT ANDROID)
add_custom_command(
TARGET ${PROJECT} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E $<IF:$<CONFIG:Debug>,copy_directory,true>
@@ -225,6 +214,7 @@ if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
$<TARGET_FILE_DIR:${PROJECT}>
COMMAND_EXPAND_LISTS
)
endif()
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
+19 -48
View File
@@ -12,7 +12,6 @@
#include <QTextDocument>
#include <QTimer>
#include <QTranslator>
#include <QEvent>
#include "logger.h"
#include "ui/controllers/pageController.h"
@@ -22,12 +21,8 @@
#include "platforms/ios/QRCodeReaderBase.h"
#include "protocols/qml_register_protocols.h"
#include <QtQuick/QQuickWindow> // for QQuickWindow
#include <QWindow> // for qobject_cast<QWindow*>
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv),
m_optAutostart({QStringLiteral("a"), QStringLiteral("autostart")}, QStringLiteral("System autostart")),
m_optCleanup ({QStringLiteral("c"), QStringLiteral("cleanup")}, QStringLiteral("Cleanup logs"))
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
{
setQuitOnLastWindowClosed(false);
@@ -54,6 +49,7 @@ AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_C
AmneziaApplication::~AmneziaApplication()
{
m_vpnConnectionThread.quit();
m_vpnConnectionThread.wait(3000);
if (m_engine) {
QObject::disconnect(m_engine, 0, 0, 0);
@@ -67,28 +63,15 @@ void AmneziaApplication::init()
const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml"));
QObject::connect(
m_engine, &QQmlApplicationEngine::objectCreated, this,
[this, url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl) {
QCoreApplication::exit(-1);
return;
}
// install filter on main window
if (auto win = qobject_cast<QQuickWindow*>(obj)) {
win->installEventFilter(this);
win->show();
}
},
Qt::QueuedConnection);
m_engine, &QQmlApplicationEngine::objectCreated, this,
[url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
},
Qt::QueuedConnection);
m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance());
#ifdef MACOS_NE
m_engine->rootContext()->setContextProperty("IsMacOsNeBuild", true);
#else
m_engine->rootContext()->setContextProperty("IsMacOsNeBuild", false);
#endif
m_vpnConnection.reset(new VpnConnection(m_settings));
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
m_vpnConnectionThread.start();
@@ -111,7 +94,7 @@ void AmneziaApplication::init()
Logger::setServiceLogsEnabled(enabled);
#ifdef Q_OS_WIN //TODO
if (m_parser.isSet(m_optAutostart))
if (m_parser.isSet("a"))
m_coreController->pageController()->showOnStartup();
else
emit m_coreController->pageController()->raiseMainWindow();
@@ -179,12 +162,15 @@ bool AmneziaApplication::parseCommands()
m_parser.addHelpOption();
m_parser.addVersionOption();
m_parser.addOption(m_optAutostart);
m_parser.addOption(m_optCleanup);
QCommandLineOption c_autostart { { "a", "autostart" }, "System autostart" };
m_parser.addOption(c_autostart);
QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" };
m_parser.addOption(c_cleanup);
m_parser.process(*this);
if (m_parser.isSet(m_optCleanup)) {
if (m_parser.isSet(c_cleanup)) {
Logger::cleanUp();
QTimer::singleShot(100, this, [this] { quit(); });
exec();
@@ -193,8 +179,9 @@ bool AmneziaApplication::parseCommands()
return true;
}
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
void AmneziaApplication::startLocalServer() {
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
void AmneziaApplication::startLocalServer()
{
const QString serverName("AmneziaVPNInstance");
QLocalServer::removeServer(serverName);
@@ -211,22 +198,6 @@ void AmneziaApplication::startLocalServer() {
}
#endif
bool AmneziaApplication::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::Close) {
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
quit();
#else
if (m_coreController && m_coreController->pageController()) {
m_coreController->pageController()->hideMainWindow();
}
#endif
return true; // eat the close
}
// call base QObject::eventFilter
return QObject::eventFilter(watched, event);
}
QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
{
return m_engine;
+5 -10
View File
@@ -7,9 +7,9 @@
#include <QQmlContext>
#include <QThread>
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
#include <QGuiApplication>
#include <QGuiApplication>
#else
#include <QApplication>
#include <QApplication>
#endif
#include <QClipboard>
@@ -20,9 +20,9 @@
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
#define AMNEZIA_BASE_CLASS QGuiApplication
#define AMNEZIA_BASE_CLASS QGuiApplication
#else
#define AMNEZIA_BASE_CLASS QApplication
#define AMNEZIA_BASE_CLASS QApplication
#endif
class AmneziaApplication : public AMNEZIA_BASE_CLASS
@@ -37,7 +37,7 @@ public:
void loadFonts();
bool parseCommands();
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
void startLocalServer();
#endif
@@ -56,15 +56,10 @@ private:
QCommandLineParser m_parser;
QCommandLineOption m_optAutostart;
QCommandLineOption m_optCleanup;
QSharedPointer<VpnConnection> m_vpnConnection;
QThread m_vpnConnectionThread;
QNetworkAccessManager *m_nam;
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
};
#endif // AMNEZIA_APPLICATION_H
-6
View File
@@ -3,13 +3,10 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.amnezia.vpn"
android:versionName="-- %%INSERT_VERSION_NAME%% --"
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
android:installLocation="auto">
<uses-sdk android:maxSdkVersion="25" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
@@ -70,9 +67,6 @@
android:name="android.app.lib_name"
android:value="-- %%INSERT_APP_LIB_NAME%% --" />
<meta-data
android:name="android.app.extract_android_style"
android:value="minimal" />
</activity>
<activity
+1 -1
View File
@@ -33,7 +33,7 @@ android.library.defaults.buildfeatures.androidresources=false
# For development copy and set local values for these parameters in local.properties
#androidCompileSdkVersion=android-34
#androidBuildToolsVersion=34.0.0
#qtMinSdkVersion=24
#qtMinSdkVersion=26
#qtTargetSdkVersion=34
#androidNdkVersion=26.1.10909125
#qtTargetAbiList=x86_64
@@ -183,6 +183,14 @@ class OpenVpnClient(
// Never called more than once per tun_builder session.
override fun tun_builder_set_proxy_http(host: String, port: Int): Boolean {
Log.d(TAG, "tun_builder_set_proxy_http: $host, $port")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
try {
configBuilder.setHttpProxy(ProxyInfo.buildDirectProxy(host, port))
} catch (e: Exception) {
Log.e(TAG, "Could not set proxy: ${e.message}")
return false
}
}
return true
}
@@ -113,7 +113,12 @@ abstract class Protocol {
Log.d(TAG, "addRoute: $inetNetwork")
vpnBuilder.addRoute(inetNetwork)
} else {
Log.e(TAG, "Trying to exclude route $inetNetwork on old Android")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Log.d(TAG, "excludeRoute: $inetNetwork")
vpnBuilder.excludeRoute(inetNetwork)
} else {
Log.e(TAG, "Trying to exclude route $inetNetwork on old Android")
}
}
}
@@ -130,6 +135,13 @@ abstract class Protocol {
Log.d(TAG, "setMtu: ${config.mtu}")
vpnBuilder.setMtu(config.mtu)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
config.httpProxy?.let {
Log.d(TAG, "setHttpProxy: $it")
vpnBuilder.setHttpProxy(it)
}
}
if (config.allowAllAF) {
Log.d(TAG, "allowFamily")
vpnBuilder.allowFamily(OsConstants.AF_INET)
@@ -139,6 +151,8 @@ abstract class Protocol {
Log.d(TAG, "setBlocking: ${config.blockingMode}")
vpnBuilder.setBlocking(config.blockingMode)
vpnBuilder.setUnderlyingNetworks(null)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
vpnBuilder.setMetered(false)
}
}
@@ -145,7 +145,7 @@ open class ProtocolConfig protected constructor(
}
// for older versions of Android, build a list of subnets without excluded routes
// and add them to routes
if (routes.any { !it.include }) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU && routes.any { !it.include }) {
val ipRangeSet = IpRangeSet()
routes.forEach {
if (it.include) ipRangeSet.add(IpRange(it.inetNetwork))
+1 -1
View File
@@ -21,5 +21,5 @@ android {
}
dependencies {
implementation(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar"))))
api(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar"))))
}
@@ -3,7 +3,9 @@ package org.amnezia.vpn
import android.Manifest
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.app.NotificationManager
import android.content.ActivityNotFoundException
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Intent
import android.content.Intent.EXTRA_MIME_TYPES
@@ -75,6 +77,7 @@ class AmneziaActivity : QtActivity() {
private var isWaitingStatus = true
private var isServiceConnected = false
private var isInBoundState = false
private var notificationStateReceiver: BroadcastReceiver? = null
private lateinit var vpnServiceMessenger: IpcMessenger
private var pfd: ParcelFileDescriptor? = null
@@ -183,6 +186,7 @@ class AmneziaActivity : QtActivity() {
doBindService()
}
)
registerBroadcastReceivers()
intent?.let(::processIntent)
runBlocking { vpnProto = proto.await() }
}
@@ -198,6 +202,26 @@ class AmneziaActivity : QtActivity() {
}
}
private fun registerBroadcastReceivers() {
notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
registerBroadcastReceiver(
arrayOf(
NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED,
NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED
)
) {
Log.v(
TAG, "Notification state changed: ${it?.action}, blocked = " +
"${it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)}"
)
mainScope.launch {
qtInitialized.await()
QtAndroidController.onNotificationStateChanged()
}
}
} else null
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
Log.v(TAG, "onNewIntent: $intent")
@@ -243,6 +267,8 @@ class AmneziaActivity : QtActivity() {
override fun onDestroy() {
Log.d(TAG, "Destroy Amnezia activity")
unregisterBroadcastReceiver(notificationStateReceiver)
notificationStateReceiver = null
mainScope.cancel()
super.onDestroy()
}
@@ -721,7 +747,7 @@ class AmneziaActivity : QtActivity() {
}
@Suppress("unused")
fun isNotificationPermissionGranted(): Boolean = true
fun isNotificationPermissionGranted(): Boolean = applicationContext.isNotificationPermissionGranted()
@Suppress("unused")
fun requestNotificationPermission() {
@@ -821,6 +847,67 @@ class AmneziaActivity : QtActivity() {
0, 0, 1.0f, 1.0f, 0, 0, 0,0
)
// workaround for a bug in Qt that causes the mouse click event not to be handled
// also disable right-click, as it causes the application to crash
private var lastButtonState = 0
private fun MotionEvent.fixCopy(): MotionEvent = MotionEvent.obtain(
downTime,
eventTime,
action,
pointerCount,
(0 until pointerCount).map { i ->
MotionEvent.PointerProperties().apply {
getPointerProperties(i, this)
}
}.toTypedArray(),
(0 until pointerCount).map { i ->
MotionEvent.PointerCoords().apply {
getPointerCoords(i, this)
}
}.toTypedArray(),
metaState,
MotionEvent.BUTTON_PRIMARY,
xPrecision,
yPrecision,
deviceId,
edgeFlags,
source,
flags
)
private fun handleMouseEvent(ev: MotionEvent, superDispatch: (MotionEvent?) -> Boolean): Boolean {
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
lastButtonState = ev.buttonState
if (ev.buttonState == MotionEvent.BUTTON_SECONDARY) return true
}
MotionEvent.ACTION_UP -> {
when (lastButtonState) {
MotionEvent.BUTTON_SECONDARY -> return true
MotionEvent.BUTTON_PRIMARY -> {
val modEvent = ev.fixCopy()
return superDispatch(modEvent).apply { modEvent.recycle() }
}
}
}
}
return superDispatch(ev)
}
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
Log.v(TAG, "dispatchTouch: $ev")
if (ev != null && ev.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
return handleMouseEvent(ev) { super.dispatchTouchEvent(it) }
}
return super.dispatchTouchEvent(ev)
}
override fun dispatchTrackballEvent(ev: MotionEvent?): Boolean {
ev?.let { return handleMouseEvent(ev) { super.dispatchTrackballEvent(it) }}
return super.dispatchTrackballEvent(ev)
}
/**
* Utils methods
*/
@@ -1,9 +1,12 @@
package org.amnezia.vpn
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.net.VpnService
import android.os.Build
import android.os.IBinder
import android.os.Messenger
import android.service.quicksettings.Tile
@@ -145,8 +148,7 @@ class AmneziaTileService : TileService() {
Intent(this, AmneziaActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}.also {
@Suppress("DEPRECATION")
startActivityAndCollapse(it)
startActivityAndCollapseCompat(it)
}
}
}
@@ -190,8 +192,7 @@ class AmneziaTileService : TileService() {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
putExtra(EXTRA_PROTOCOL, vpnProto)
}.also {
@Suppress("DEPRECATION")
startActivityAndCollapse(it)
startActivityAndCollapseCompat(it)
}
false
} else {
@@ -215,6 +216,23 @@ class AmneziaTileService : TileService() {
private fun stopVpn() = vpnServiceMessenger.send(Action.DISCONNECT)
@SuppressLint("StartActivityAndCollapseDeprecated")
private fun startActivityAndCollapseCompat(intent: Intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
startActivityAndCollapse(
PendingIntent.getActivity(
applicationContext,
0,
intent,
PendingIntent.FLAG_IMMUTABLE
)
)
} else {
@Suppress("DEPRECATION")
startActivityAndCollapse(intent)
}
}
private fun updateVpnState(state: ProtocolState) =
scope.launch { VpnStateStore.store { it.copy(protocolState = state) } }
@@ -231,14 +249,17 @@ class AmneziaTileService : TileService() {
when (val protocolState = vpnState.protocolState) {
CONNECTED -> {
state = Tile.STATE_ACTIVE
subtitleCompat = null
}
DISCONNECTED, UNKNOWN -> {
state = Tile.STATE_INACTIVE
subtitleCompat = null
}
CONNECTING, DISCONNECTING, RECONNECTING -> {
state = Tile.STATE_UNAVAILABLE
subtitleCompat = getString(protocolState)
}
}
updateTile()
@@ -246,4 +267,17 @@ class AmneziaTileService : TileService() {
// double update to fix weird visual glitches
tile.updateTile()
}
private var Tile.subtitleCompat: CharSequence?
set(value) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
this.subtitle = value
}
}
get() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return this.subtitle
}
return null
}
}
@@ -3,10 +3,14 @@ package org.amnezia.vpn
import android.annotation.SuppressLint
import android.app.ActivityManager
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED
import android.net.VpnService
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.Looper
@@ -100,6 +104,7 @@ open class AmneziaVpnService : VpnService() {
private lateinit var networkState: NetworkState
private lateinit var trafficStats: TrafficStats
private var controlReceiver: BroadcastReceiver? = null
private var notificationStateReceiver: BroadcastReceiver? = null
private var screenOnReceiver: BroadcastReceiver? = null
private var screenOffReceiver: BroadcastReceiver? = null
private val clientMessengers = ConcurrentHashMap<Messenger, IpcMessenger>()
@@ -184,6 +189,16 @@ open class AmneziaVpnService : VpnService() {
Messenger(actionMessageHandler)
}
/**
* Notification setup
*/
private val foregroundServiceTypeCompat
get() = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE -> FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> FOREGROUND_SERVICE_TYPE_MANIFEST
else -> 0
}
private val serviceNotification: ServiceNotification by lazy(NONE) { ServiceNotification(this) }
/**
@@ -217,7 +232,7 @@ open class AmneziaVpnService : VpnService() {
ServiceCompat.startForeground(
this, NOTIFICATION_ID,
serviceNotification.buildNotification(serverName, vpnProto?.label, protocolState.value),
0
foregroundServiceTypeCompat
)
return START_REDELIVER_INTENT
}
@@ -294,6 +309,23 @@ open class AmneziaVpnService : VpnService() {
}
}
notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
registerBroadcastReceiver(
arrayOf(
NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED,
NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED
)
) {
val state = it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)
Log.v(TAG, "Notification state changed: ${it?.action}, blocked = $state")
if (state == false) {
enableNotification()
} else {
disableNotification()
}
}
} else null
registerScreenStateBroadcastReceivers()
}
@@ -321,8 +353,10 @@ open class AmneziaVpnService : VpnService() {
private fun unregisterBroadcastReceivers() {
Log.d(TAG, "Unregister broadcast receivers")
unregisterBroadcastReceiver(controlReceiver)
unregisterBroadcastReceiver(notificationStateReceiver)
unregisterScreenStateBroadcastReceivers()
controlReceiver = null
notificationStateReceiver = null
}
/**
@@ -1,15 +1,19 @@
package org.amnezia.vpn
import android.Manifest.permission
import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.app.NotificationChannelCompat.Builder
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.Action
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import org.amnezia.vpn.protocol.ProtocolState
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
@@ -81,17 +85,27 @@ class ServiceNotification(private val context: Context) {
.setSubText(getSpeedString(speed))
.build()
fun isNotificationEnabled(): Boolean = notificationManager.areNotificationsEnabled()
fun isNotificationEnabled(): Boolean {
if (!context.isNotificationPermissionGranted()) return false
if (!notificationManager.areNotificationsEnabled()) return false
return notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID)?.let {
it.importance != NotificationManager.IMPORTANCE_NONE
} ?: true
}
@SuppressLint("MissingPermission")
fun updateNotification(serverName: String?, protocol: String?, state: ProtocolState) {
Log.v(TAG, "Update notification: $serverName, $state")
notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, protocol, state))
if (context.isNotificationPermissionGranted()) {
Log.v(TAG, "Update notification: $serverName, $state")
notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, protocol, state))
}
}
@SuppressLint("MissingPermission")
fun updateSpeed(speed: TrafficData) {
notificationManager.notify(NOTIFICATION_ID, buildNotification(speed))
if (context.isNotificationPermissionGranted()) {
notificationManager.notify(NOTIFICATION_ID, buildNotification(speed))
}
}
private fun getSpeedString(traffic: TrafficData) =
@@ -152,3 +166,8 @@ class ServiceNotification(private val context: Context) {
}
}
}
fun Context.isNotificationPermissionGranted(): Boolean =
Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU ||
ContextCompat.checkSelfPermission(this, permission.POST_NOTIFICATIONS) ==
PackageManager.PERMISSION_GRANTED
@@ -7,6 +7,7 @@ import android.content.Intent
import android.content.res.Configuration.UI_MODE_NIGHT_MASK
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.net.VpnService
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.widget.Toast
@@ -30,9 +31,12 @@ class VpnRequestActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "Start request activity")
@Suppress("DEPRECATION")
vpnProto = intent.extras?.getSerializable(EXTRA_PROTOCOL) as VpnProto
vpnProto = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.extras?.getSerializable(EXTRA_PROTOCOL, VpnProto::class.java)
} else {
@Suppress("DEPRECATION")
intent.extras?.getSerializable(EXTRA_PROTOCOL) as VpnProto
}
val requestIntent = VpnService.prepare(applicationContext)
if (requestIntent != null) {
if (getSystemService<KeyguardManager>()!!.isKeyguardLocked) {
+5 -12
View File
@@ -1,9 +1,6 @@
package org.amnezia.vpn.util
import android.content.Context
import android.icu.text.DateFormat
import android.icu.text.SimpleDateFormat
import android.icu.util.TimeZone
import android.os.Build
import android.os.Process
import java.io.File
@@ -11,8 +8,8 @@ import java.io.IOException
import java.io.RandomAccessFile
import java.nio.channels.FileChannel
import java.nio.channels.FileLock
import java.util.Date
import java.util.Locale
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.concurrent.locks.ReentrantLock
import org.amnezia.vpn.util.Log.Priority.D
import org.amnezia.vpn.util.Log.Priority.E
@@ -40,11 +37,7 @@ private const val LOG_MAX_FILE_SIZE = 1024 * 1024
* | | | create a report and/or terminate the process |
*/
object Log {
private val dateTimeFormat = object : ThreadLocal<DateFormat>() {
override fun initialValue(): DateFormat = SimpleDateFormat(DATE_TIME_PATTERN, Locale.US).apply {
timeZone = TimeZone.getTimeZone("UTC")
}
}
private val dateTimeFormat: DateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)
private lateinit var logDir: File
private val logFile: File by lazy { File(logDir, LOG_FILE_NAME) }
@@ -142,8 +135,8 @@ object Log {
}
private fun formatLogMsg(tag: String, msg: String, priority: Priority): String {
val utcDate = dateTimeFormat.get()?.format(Date())
return "${utcDate}Z ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
val date = LocalDateTime.now().format(dateTimeFormat)
return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
"$tag: $msg\n"
}
@@ -8,9 +8,11 @@ import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
import android.net.NetworkRequest
import android.os.Build
import android.os.Handler
import androidx.core.content.getSystemService
import kotlin.LazyThreadSafetyMode.NONE
import kotlinx.coroutines.delay
import org.amnezia.vpn.util.Log
private const val TAG = "NetworkState"
@@ -45,9 +47,7 @@ class NetworkState(
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
Log.v(TAG, "onCapabilitiesChanged: $network, $networkCapabilities")
handler.post {
checkNetworkState(network, networkCapabilities)
}
checkNetworkState(network, networkCapabilities)
}
private fun checkNetworkState(network: Network, networkCapabilities: NetworkCapabilities) {
@@ -76,10 +76,33 @@ class NetworkState(
}
}
fun bindNetworkListener() {
suspend fun bindNetworkListener() {
if (isListenerBound) return
Log.d(TAG, "Bind network listener")
connectivityManager.requestNetwork(networkRequest, networkCallback)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
connectivityManager.registerBestMatchingNetworkCallback(networkRequest, networkCallback, handler)
} else {
val numberAttempts = 300
var attemptCount = 0
while(true) {
try {
connectivityManager.requestNetwork(networkRequest, networkCallback, handler)
break
} catch (e: SecurityException) {
Log.e(TAG, "Failed to bind network listener: $e")
// Android 11 bug: https://issuetracker.google.com/issues/175055271
if (e.message?.startsWith("Package android does not belong to") == true) {
if (++attemptCount > numberAttempts) {
throw e
}
delay(1000)
continue
} else {
throw e
}
}
}
}
isListenerBound = true
}
@@ -1,6 +1,7 @@
package org.amnezia.vpn.util.net
import android.net.TrafficStats
import android.os.Build
import android.os.Process
import android.os.SystemClock
import kotlin.math.roundToLong
@@ -16,12 +17,18 @@ class TrafficStats {
private var lastTrafficData = TrafficData.ZERO
private var lastTimestamp = 0L
private val getTrafficDataCompat: () -> TrafficData = run {
val uid = Process.myUid()
fun(): TrafficData {
return TrafficData(TrafficStats.getUidRxBytes(uid), TrafficStats.getUidTxBytes(uid))
private val getTrafficDataCompat: () -> TrafficData =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val iface = "tun0"
fun(): TrafficData {
return TrafficData(TrafficStats.getRxBytes(iface), TrafficStats.getTxBytes(iface))
}
} else {
val uid = Process.myUid()
fun(): TrafficData {
return TrafficData(TrafficStats.getUidRxBytes(uid), TrafficStats.getUidTxBytes(uid))
}
}
}
fun reset() {
lastTrafficData = getTrafficDataCompat()
+3 -9
View File
@@ -27,15 +27,9 @@ if(WIN32)
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib")
endif()
elseif(APPLE AND NOT IOS)
if(MACOS_NE)
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/universal2/libssh.a")
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/universal2/libz.a")
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/universal2")
else()
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libssh.a")
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libz.a")
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64")
endif()
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libssh.a")
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libz.a")
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64")
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/macos/include")
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libssl.a")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libcrypto.a")
+1 -1
View File
@@ -1,6 +1,6 @@
message("Client android ${CMAKE_ANDROID_ARCH_ABI} build")
set(APP_ANDROID_MIN_SDK 24)
set(APP_ANDROID_MIN_SDK 26)
set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING
"The minimum API level supported by the application or library" FORCE)
+4 -16
View File
@@ -46,7 +46,6 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/AmneziaSceneDelegateHooks.mm
)
@@ -136,21 +135,10 @@ set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
add_subdirectory(ios/networkextension)
add_dependencies(${PROJECT} networkextension)
set(OPENVPN_FRAMEWORK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios")
set(OPENVPN_EMBEDDED_FRAMEWORKS
"${OPENVPN_FRAMEWORK_DIR}/OpenVPNAdapter.framework"
"${OPENVPN_FRAMEWORK_DIR}/OpenVPNClient.framework"
"${OPENVPN_FRAMEWORK_DIR}/mbedTLS.framework"
"${OPENVPN_FRAMEWORK_DIR}/LZ4.framework"
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
"${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework"
)
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS "${OPENVPN_EMBEDDED_FRAMEWORKS}")
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS "$(inherited) ${OPENVPN_FRAMEWORK_DIR}")
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/)
target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework")
foreach(_framework ${OPENVPN_EMBEDDED_FRAMEWORKS})
target_link_libraries(networkextension PRIVATE "${_framework}")
endforeach()
set_property(TARGET networkextension PROPERTY XCODE_EMBED_FRAMEWORKS "${OPENVPN_EMBEDDED_FRAMEWORKS}")
set_property(TARGET networkextension PROPERTY XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON)
set_property(TARGET networkextension PROPERTY XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS "$(inherited) ${OPENVPN_FRAMEWORK_DIR}")
+3 -8
View File
@@ -14,15 +14,11 @@ set(LIBS ${LIBS}
${FW_SECURITY}
${FW_COREWLAN}
${FW_NETWORK}
${FW_USER_NOTIFICATIONS}
${FW_USERNOTIFICATIONS}
${FW_NETWORK_EXTENSION}
)
set_target_properties(${PROJECT} PROPERTIES
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_SHORT_VERSION_STRING "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}"
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
)
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
@@ -35,8 +31,6 @@ 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)
@@ -55,3 +49,4 @@ execute_process(
)
message("OSX_SDK_PATH is: ${OSX_SDK_PATH}")
-168
View File
@@ -1,168 +0,0 @@
message("Client ==> MacOS NE build")
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
enable_language(OBJC)
enable_language(Swift)
find_package(Qt6 REQUIRED COMPONENTS ShaderTools Widgets)
# Link Qt Widgets for QWidget, QMenu, QAction etc.
set(LIBS ${LIBS} Qt6::ShaderTools Qt6::Widgets)
find_library(FW_AUTHENTICATIONSERVICES AuthenticationServices)
find_library(FW_AVFOUNDATION AVFoundation)
find_library(FW_FOUNDATION Foundation)
find_library(FW_STOREKIT StoreKit)
find_library(FW_SERVICEMGMT ServiceManagement)
find_library(FW_USERNOTIFICATIONS UserNotifications)
find_library(FW_NETWORKEXTENSION NetworkExtension)
set(LIBS ${LIBS}
${FW_AUTHENTICATIONSERVICES}
${FW_AVFOUNDATION}
${FW_FOUNDATION}
${FW_STOREKIT}
${FW_SERVICEMGMT}
${FW_USERNOTIFICATIONS}
${FW_NETWORKEXTENSION}
)
set(HEADERS ${HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate-C-Interface.h
)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h PROPERTIES OBJECTIVE_CPP_HEADER TRUE)
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm
)
set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns)
set(MACOSX_BUNDLE_ICON_FILE app.icns)
set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
set(SOURCES ${SOURCES} ${ICON_FILE})
target_include_directories(${PROJECT} PRIVATE
${Qt6Gui_PRIVATE_INCLUDE_DIRS}
${Qt6Widgets_PRIVATE_INCLUDE_DIRS}
)
set_target_properties(${PROJECT} PROPERTIES
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Info.plist.in
MACOSX_BUNDLE_ICON_FILE "AppIcon"
MACOSX_BUNDLE_INFO_STRING "AmneziaVPN"
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPN"
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
MACOSX_BUNDLE_LONG_VERSION_STRING "${APPLE_PROJECT_VERSION}-${CMAKE_PROJECT_VERSION_TWEAK}"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/macos/app/app.entitlements"
XCODE_ATTRIBUTE_MARKETING_VERSION "${APPLE_PROJECT_VERSION}"
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPN"
XCODE_ATTRIBUTE_BUNDLE_INFO_STRING "AmneziaVPN"
XCODE_GENERATE_SCHEME TRUE
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY "NO"
XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY "YES"
XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "11.0"
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks"
XCODE_EMBED_APP_EXTENSIONS AmneziaVPNNetworkExtension
)
if(DEPLOY)
set_target_properties(${PROJECT} PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr macos.org.amnezia.AmneziaVPN"
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev macos.org.amnezia.AmneziaVPN"
)
else()
set_target_properties(${PROJECT} PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
)
endif()
set_target_properties(${PROJECT} PROPERTIES
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
XCODE_ATTRIBUTE_SWIFT_OBJC_INTERFACE_HEADER_NAME "AmneziaVPN-Swift.h"
XCODE_ATTRIBUTE_SWIFT_OBJC_INTEROP_MODE "objcxx"
)
set_target_properties(${PROJECT} PROPERTIES
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
)
target_include_directories(${PROJECT} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
target_compile_options(${PROJECT} PRIVATE
-DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\"
-DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\"
)
set(WG_APPLE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rd/amneziawg-apple/Sources)
target_sources(${PROJECT} PRIVATE
${WG_APPLE_SOURCE_DIR}/WireGuardKitC/x25519.c
${CLIENT_ROOT_DIR}/platforms/ios/LogController.swift
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
${CLIENT_ROOT_DIR}/platforms/ios/ScreenProtection.swift
${CLIENT_ROOT_DIR}/platforms/ios/VPNCController.swift
)
target_sources(${PROJECT} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Images.xcassets
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
)
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Images.xcassets
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
)
add_subdirectory(macos/networkextension)
add_dependencies(${PROJECT} AmneziaVPNNetworkExtension)
get_target_property(QtCore_location Qt6::Core LOCATION)
message("QtCore_location")
message(${QtCore_location})
get_filename_component(QT_BIN_DIR_DETECTED "${QtCore_location}/../../../../../bin" ABSOLUTE)
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
"${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos/OpenVPNAdapter.framework"
)
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos)
target_link_libraries("AmneziaVPNNetworkExtension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos/OpenVPNAdapter.framework")
add_custom_command(TARGET ${PROJECT} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory
$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks
COMMAND /usr/bin/find "$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework" -name "*.sha256" -delete
COMMAND /usr/bin/codesign --force --sign "Apple Distribution"
"$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework/Versions/Current/OpenVPNAdapter"
COMMAND ${QT_BIN_DIR_DETECTED}/macdeployqt $<TARGET_BUNDLE_DIR:AmneziaVPN> -appstore-compliant -qmldir=${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Signing OpenVPNAdapter framework"
)
+2 -16
View File
@@ -39,7 +39,7 @@ set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h
)
if(NOT IOS AND NOT MACOS_NE)
if(NOT IOS)
set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.h
)
@@ -89,26 +89,12 @@ set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
)
if(NOT IOS AND NOT MACOS_NE)
if(NOT IOS)
set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.cpp
)
endif()
# Include native macOS platform helpers (dock/status-item)
if(APPLE AND NOT IOS)
list(APPEND HEADERS
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.h
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.h
${CLIENT_ROOT_DIR}/ui/macos_util.h
)
list(APPEND SOURCES
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.mm
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.mm
${CLIENT_ROOT_DIR}/ui/macos_util.mm
)
endif()
if(NOT ANDROID)
set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/ui/notificationhandler.cpp
+3 -21
View File
@@ -83,30 +83,12 @@ QString OpenVpnConfigurator::createConfig(const ServerCredentials &credentials,
return "";
}
auto sanitizeStaticKey = [](const QString &key) {
QStringList lines = key.split('\n');
QStringList filtered;
filtered.reserve(lines.size());
for (const QString &line : lines) {
const QString trimmed = line.trimmed();
if (trimmed.startsWith('#')) {
continue;
}
filtered.append(line);
}
QString result = filtered.join('\n');
if (!result.endsWith('\n')) {
result.append('\n');
}
return result;
};
config.replace("$OPENVPN_CA_CERT", connData.caCert);
config.replace("$OPENVPN_CLIENT_CERT", connData.clientCert);
config.replace("$OPENVPN_PRIV_KEY", connData.privKey);
if (config.contains("$OPENVPN_TA_KEY")) {
config.replace("$OPENVPN_TA_KEY", sanitizeStaticKey(connData.taKey));
config.replace("$OPENVPN_TA_KEY", connData.taKey);
} else {
config.replace("<tls-auth>", "");
config.replace("</tls-auth>", "");
@@ -135,7 +117,7 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
if (!isApiConfig) {
QRegularExpression regex("redirect-gateway.*");
config.replace(regex, "");
// We don't use secondary DNS if primary DNS is AmneziaDNS
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
@@ -149,7 +131,7 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
// no redirect-gateway
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
// Prevent ipv6 leak
#endif
+4 -5
View File
@@ -8,7 +8,7 @@
#include <QTemporaryFile>
#include <QThread>
#include <qtimer.h>
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
#include <QGuiApplication>
#else
#include <QApplication>
@@ -24,7 +24,7 @@ SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, const QShar
QString SshConfigurator::convertOpenSShKey(const QString &key)
{
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
#ifndef Q_OS_IOS
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
@@ -67,10 +67,9 @@ QString SshConfigurator::convertOpenSShKey(const QString &key)
#endif
}
// DEAD CODE.
void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
{
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
#ifndef Q_OS_IOS
QProcess *p = new QProcess();
p->setProcessChannelMode(QProcess::SeparateChannels);
@@ -102,7 +101,7 @@ QProcessEnvironment SshConfigurator::prepareEnv()
pathEnvVar.clear();
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;");
#elif defined(Q_OS_MACX) && !defined(MACOS_NE)
#elif defined(Q_OS_MACX)
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
#endif
+1 -18
View File
@@ -261,7 +261,6 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
return true;
#elif defined(Q_OS_IOS)
// Standard iOS build (without Network Extension limitations)
switch (c) {
case DockerContainer::WireGuard: return true;
case DockerContainer::OpenVpn: return true;
@@ -270,23 +269,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
case DockerContainer::Cloak: return true;
case DockerContainer::SSXray: return true;
// case DockerContainer::ShadowSocks: return true;
default:
return false;
}
#elif defined(MACOS_NE)
// macOS build using Network Extension – hide OpenVPN-based containers
switch (c) {
case DockerContainer::WireGuard: return true;
case DockerContainer::Awg: return true;
case DockerContainer::Xray: return true;
case DockerContainer::SSXray: return true;
case DockerContainer::OpenVpn:
case DockerContainer::Cloak:
case DockerContainer::ShadowSocks:
return false;
default:
return false;
default: return false;
}
#elif defined(Q_OS_MAC)
switch (c) {
-4
View File
@@ -64,10 +64,6 @@ namespace apiDefs
constexpr QLatin1String id("id");
constexpr QLatin1String orderId("order_id");
constexpr QLatin1String migrationCode("migration_code");
constexpr QLatin1String transactionId("transaction_id");
constexpr QLatin1String userCountryCode("user_country_code");
}
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
+13 -61
View File
@@ -23,7 +23,7 @@ namespace
bool apiUtils::isSubscriptionExpired(const QString &subscriptionEndDate)
{
QDateTime now = QDateTime::currentDateTimeUtc();
QDateTime now = QDateTime::currentDateTime();
QDateTime endDate = QDateTime::fromString(subscriptionEndDate, Qt::ISODateWithMs);
return endDate < now;
}
@@ -82,9 +82,7 @@ apiDefs::ConfigSource apiUtils::getConfigSource(const QJsonObject &serverConfigO
return static_cast<apiDefs::ConfigSource>(serverConfigObject.value(apiDefs::key::configVersion).toInt());
}
amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &sslErrors, const QString &replyErrorString,
const QNetworkReply::NetworkError &replyError, const int httpStatusCode,
const QByteArray &responseBody)
amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply)
{
const int httpStatusCodeConflict = 409;
const int httpStatusCodeNotFound = 404;
@@ -92,19 +90,21 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
if (!sslErrors.empty()) {
qDebug().noquote() << sslErrors;
return amnezia::ErrorCode::ApiConfigSslError;
} else if (replyError == QNetworkReply::NoError) {
} else if (reply->error() == QNetworkReply::NoError) {
return amnezia::ErrorCode::NoError;
} else if (replyError == QNetworkReply::NetworkError::OperationCanceledError
|| replyError == QNetworkReply::NetworkError::TimeoutError) {
qDebug() << replyError;
} else if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
qDebug() << reply->error();
return amnezia::ErrorCode::ApiConfigTimeoutError;
} else if (replyError == QNetworkReply::NetworkError::OperationNotImplementedError) {
qDebug() << replyError;
} else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) {
qDebug() << reply->error();
return amnezia::ErrorCode::ApiUpdateRequestError;
} else {
qDebug() << QString::fromUtf8(responseBody);
qDebug() << replyError;
qDebug() << replyErrorString;
QString err = reply->errorString();
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << QString::fromUtf8(reply->readAll());
qDebug() << reply->error();
qDebug() << err;
qDebug() << httpStatusCode;
if (httpStatusCode == httpStatusCodeConflict) {
return amnezia::ErrorCode::ApiConfigLimitError;
@@ -162,51 +162,3 @@ QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject)
return QString("vpn://%1").arg(QString(signedData.toBase64(QByteArray::Base64UrlEncoding)));
}
QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject)
{
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV2) {
return {};
}
QString vpnKeyText = "";
auto apiConfig = serverConfigObject.value(apiDefs::key::apiConfig).toObject();
auto authData = serverConfigObject.value(QLatin1String("auth_data")).toObject();
const QString name = serverConfigObject.value(apiDefs::key::name).toString();
const QString description = serverConfigObject.value(apiDefs::key::description).toString();
const double configVersion = serverConfigObject.value(apiDefs::key::configVersion).toDouble();
const QString serviceType = apiConfig.value(apiDefs::key::serviceType).toString();
const QString serviceProtocol = apiConfig.value(QLatin1String("service_protocol")).toString();
const QString userCountryCode = apiConfig.value(QLatin1String("user_country_code")).toString();
const QString apiKey = authData.value(apiDefs::key::apiKey).toString();
QString vpnKeyStr = "{";
vpnKeyStr += "\"" + QString(apiDefs::key::name) + "\": \"" + name + "\", ";
vpnKeyStr += "\"" + QString(apiDefs::key::description) + "\": \"" + description + "\", ";
vpnKeyStr += "\"" + QString(apiDefs::key::configVersion) + "\": " + QString::number(static_cast<int>(configVersion)) + ", ";
vpnKeyStr += "\"" + QString(apiDefs::key::apiConfig) + "\": {";
vpnKeyStr += "\"" + QString(apiDefs::key::serviceType) + "\": \"" + serviceType + "\", ";
vpnKeyStr += "\"service_protocol\": \"" + serviceProtocol + "\", ";
vpnKeyStr += "\"user_country_code\": \"" + userCountryCode + "\"";
vpnKeyStr += "}, ";
vpnKeyStr += "\"auth_data\": {";
vpnKeyStr += "\"" + QString(apiDefs::key::apiKey) + "\": \"" + apiKey + "\"";
vpnKeyStr += "}";
vpnKeyStr += "}";
QByteArray vpnKeyCompressed = escapeUnicode(vpnKeyStr).toUtf8();
vpnKeyCompressed = qCompress(vpnKeyCompressed, 6);
vpnKeyCompressed = vpnKeyCompressed.mid(4);
QByteArray signedData = AMNEZIA_CONFIG_SIGNATURE + vpnKeyCompressed;
vpnKeyText = QString("vpn://%1").arg(QString(signedData.toBase64(QByteArray::Base64UrlEncoding)));
return vpnKeyText;
}
+1 -4
View File
@@ -18,12 +18,9 @@ namespace apiUtils
apiDefs::ConfigType getConfigType(const QJsonObject &serverConfigObject);
apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject);
amnezia::ErrorCode checkNetworkReplyErrors(const QList<QSslError> &sslErrors, const QString &replyErrorString,
const QNetworkReply::NetworkError &replyError, const int httpStatusCode,
const QByteArray &responseBody);
amnezia::ErrorCode checkNetworkReplyErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply);
QString getPremiumV1VpnKey(const QJsonObject &serverConfigObject);
QString getPremiumV2VpnKey(const QJsonObject &serverConfigObject);
}
#endif // APIUTILS_H
+4 -21
View File
@@ -26,8 +26,9 @@ CoreController::CoreController(const QSharedPointer<VpnConnection> &vpnConnectio
initNotificationHandler();
auto locale = m_settings->getAppLanguage();
m_translator.reset(new QTranslator());
updateTranslator(m_settings->getAppLanguage());
updateTranslator(locale);
}
void CoreController::initModels()
@@ -99,9 +100,6 @@ void CoreController::initModels()
m_apiDevicesModel.reset(new ApiDevicesModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ApiDevicesModel", m_apiDevicesModel.get());
m_newsModel.reset(new NewsModel(m_settings, this));
m_engine->rootContext()->setContextProperty("NewsModel", m_newsModel.get());
}
void CoreController::initControllers()
@@ -122,9 +120,6 @@ void CoreController::initControllers()
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
&ConnectionController::onCurrentContainerUpdated); // TODO remove this
connect(m_installController.get(), &InstallController::profileCleared,
m_protocolsModel.get(), &ProtocolsModel::updateModel);
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
@@ -156,9 +151,6 @@ void CoreController::initControllers()
m_apiPremV1MigrationController.reset(new ApiPremV1MigrationController(m_serversModel, m_settings, this));
m_engine->rootContext()->setContextProperty("ApiPremV1MigrationController", m_apiPremV1MigrationController.get());
m_apiNewsController.reset(new ApiNewsController(m_newsModel, m_settings, m_serversModel, this));
m_engine->rootContext()->setContextProperty("ApiNewsController", m_apiNewsController.get());
}
void CoreController::initAndroidController()
@@ -238,7 +230,7 @@ void CoreController::initSignalHandlers()
void CoreController::initNotificationHandler()
{
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
#ifndef Q_OS_ANDROID
m_notificationHandler.reset(NotificationHandler::create(nullptr));
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
@@ -250,10 +242,7 @@ void CoreController::initNotificationHandler()
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
&ConnectionController::closeConnection);
connect(this, &CoreController::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
auto* trayHandler = qobject_cast<SystemTrayNotificationHandler*>(m_notificationHandler.get());
connect(this, &CoreController::websiteUrlChanged, trayHandler, &SystemTrayNotificationHandler::updateWebsiteUrl);
#endif
#endif
}
void CoreController::updateTranslator(const QLocale &locale)
@@ -290,7 +279,6 @@ void CoreController::updateTranslator(const QLocale &locale)
m_engine->retranslate();
emit translationsUpdated();
emit websiteUrlChanged(m_languageModel->getCurrentSiteUrl());
}
void CoreController::initErrorMessagesHandler()
@@ -322,11 +310,6 @@ void CoreController::initContainerModelUpdateHandler()
connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(), &ContainersModel::updateModel);
connect(m_serversModel.get(), &ServersModel::defaultServerContainersUpdated, m_defaultServerContainersModel.get(),
&ContainersModel::updateModel);
connect(m_serversModel.get(), &ServersModel::gatewayStacksExpanded, this, [this]() {
if (m_serversModel->hasServersFromGatewayApi()) {
m_apiNewsController->fetchNews();
}
});
m_serversModel->resetModel();
}
+2 -11
View File
@@ -5,14 +5,9 @@
#include <QQmlContext>
#include <QThread>
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
#include "ui/systemtray_notificationhandler.h"
#endif
#include "ui/controllers/api/apiConfigsController.h"
#include "ui/controllers/api/apiSettingsController.h"
#include "ui/controllers/api/apiPremV1MigrationController.h"
#include "ui/controllers/api/apiNewsController.h"
#include "ui/controllers/appSplitTunnelingController.h"
#include "ui/controllers/allowedDnsController.h"
#include "ui/controllers/connectionController.h"
@@ -48,9 +43,8 @@
#include "ui/models/services/sftpConfigModel.h"
#include "ui/models/services/socks5ProxyConfigModel.h"
#include "ui/models/sites_model.h"
#include "ui/models/newsModel.h"
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
#ifndef Q_OS_ANDROID
#include "ui/notificationhandler.h"
#endif
@@ -67,7 +61,6 @@ public:
signals:
void translationsUpdated();
void websiteUrlChanged(const QString &newUrl);
private:
void initModels();
@@ -99,7 +92,7 @@ private:
QSharedPointer<VpnConnection> m_vpnConnection;
QSharedPointer<QTranslator> m_translator;
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
#ifndef Q_OS_ANDROID
QScopedPointer<NotificationHandler> m_notificationHandler;
#endif
@@ -120,7 +113,6 @@ private:
QScopedPointer<ApiSettingsController> m_apiSettingsController;
QScopedPointer<ApiConfigsController> m_apiConfigsController;
QScopedPointer<ApiPremV1MigrationController> m_apiPremV1MigrationController;
QScopedPointer<ApiNewsController> m_apiNewsController;
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<ContainersModel> m_defaultServerContainersModel;
@@ -128,7 +120,6 @@ private:
QSharedPointer<LanguageModel> m_languageModel;
QSharedPointer<ProtocolsModel> m_protocolsModel;
QSharedPointer<SitesModel> m_sitesModel;
QSharedPointer<NewsModel> m_newsModel;
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
QSharedPointer<ClientManagementModel> m_clientManagementModel;
+95 -101
View File
@@ -50,6 +50,68 @@ GatewayController::GatewayController(const QString &gatewayEndpoint, const bool
{
}
ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBody)
{
#ifdef Q_OS_IOS
IosController::Instance()->requestInetAccess();
QThread::msleep(10);
#endif
QNetworkRequest request;
request.setTransferTimeout(m_requestTimeoutMsecs);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setUrl(QString(endpoint).arg(m_gatewayEndpoint));
// bypass killSwitch exceptions for API-gateway
#ifdef AMNEZIA_DESKTOP
if (m_isStrictKillSwitchEnabled) {
QString host = QUrl(request.url()).host();
QString ip = NetworkUtilities::getIPAddress(host);
if (!ip.isEmpty()) {
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip });
}
}
#endif
QNetworkReply *reply;
reply = amnApp->networkManager()->get(request);
QEventLoop wait;
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
QList<QSslError> sslErrors;
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec();
responseBody = reply->readAll();
if (sslErrors.isEmpty() && shouldBypassProxy(reply, responseBody, false)) {
auto requestFunction = [&request, &responseBody](const QString &url) {
request.setUrl(url);
return amnApp->networkManager()->get(request);
};
auto replyProcessingFunction = [&responseBody, &reply, &sslErrors, this](QNetworkReply *nestedReply,
const QList<QSslError> &nestedSslErrors) {
responseBody = nestedReply->readAll();
if (!sslErrors.isEmpty() || !shouldBypassProxy(nestedReply, responseBody, false)) {
sslErrors = nestedSslErrors;
reply = nestedReply;
return true;
}
return false;
};
bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
}
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
reply->deleteLater();
return errorCode;
}
ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody)
{
#ifdef Q_OS_IOS
@@ -60,9 +122,8 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
QNetworkRequest request;
request.setTransferTimeout(m_requestTimeoutMsecs);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader(QString("X-Client-Request-ID").toUtf8(), QUuid::createUuid().toString(QUuid::WithoutBraces).toUtf8());
request.setUrl(endpoint.arg(m_proxyUrl.isEmpty() ? m_gatewayEndpoint : m_proxyUrl));
request.setUrl(endpoint.arg(m_gatewayEndpoint));
// bypass killSwitch exceptions for API-gateway
#ifdef AMNEZIA_DESKTOP
@@ -125,37 +186,29 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
wait.exec();
QByteArray encryptedResponseBody = reply->readAll();
QString replyErrorString = reply->errorString();
auto replyError = reply->error();
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
reply->deleteLater();
if (sslErrors.isEmpty() && shouldBypassProxy(replyError, encryptedResponseBody, true, key, iv, salt)) {
if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
auto requestFunction = [&request, &encryptedResponseBody, &requestBody](const QString &url) {
request.setUrl(url);
return amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
};
auto replyProcessingFunction = [&encryptedResponseBody, &replyErrorString, &replyError, &httpStatusCode, &sslErrors, &key, &iv,
&salt, this](QNetworkReply *reply, const QList<QSslError> &nestedSslErrors) {
encryptedResponseBody = reply->readAll();
replyErrorString = reply->errorString();
replyError = reply->error();
httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (!sslErrors.isEmpty() || shouldBypassProxy(replyError, encryptedResponseBody, true, key, iv, salt)) {
auto replyProcessingFunction = [&encryptedResponseBody, &reply, &sslErrors, &key, &iv, &salt,
this](QNetworkReply *nestedReply, const QList<QSslError> &nestedSslErrors) {
encryptedResponseBody = nestedReply->readAll();
reply = nestedReply;
if (!sslErrors.isEmpty() || shouldBypassProxy(nestedReply, encryptedResponseBody, true, key, iv, salt)) {
sslErrors = nestedSslErrors;
return false;
}
return true;
};
auto serviceType = apiPayload.value(apiDefs::key::serviceType).toString("");
auto userCountryCode = apiPayload.value(apiDefs::key::userCountryCode).toString("");
bypassProxy(endpoint, serviceType, userCountryCode, requestFunction, replyProcessingFunction);
bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
}
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, replyErrorString, replyError, httpStatusCode, encryptedResponseBody);
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
reply->deleteLater();
if (errorCode) {
return errorCode;
}
@@ -170,7 +223,7 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
}
}
QStringList GatewayController::getProxyUrls(const QString &serviceType, const QString &userCountryCode)
QStringList GatewayController::getProxyUrls()
{
QNetworkRequest request;
request.setTransferTimeout(m_requestTimeoutMsecs);
@@ -180,26 +233,15 @@ QStringList GatewayController::getProxyUrls(const QString &serviceType, const QS
QList<QSslError> sslErrors;
QNetworkReply *reply;
QStringList baseUrls;
QStringList proxyStorageUrls;
if (m_isDevEnvironment) {
baseUrls = QString(DEV_S3_ENDPOINT).split(", ");
proxyStorageUrls = QString(DEV_S3_ENDPOINT).split(", ");
} else {
baseUrls = QString(PROD_S3_ENDPOINT).split(", ");
proxyStorageUrls = QString(PROD_S3_ENDPOINT).split(", ");
}
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
QStringList proxyStorageUrls;
if (!serviceType.isEmpty()) {
for (const auto &baseUrl : baseUrls) {
QByteArray path = ("endpoints-" + serviceType + "-" + userCountryCode).toUtf8();
proxyStorageUrls.push_back(baseUrl + path.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals) + ".json");
}
}
for (const auto &baseUrl : baseUrls) {
proxyStorageUrls.push_back(baseUrl + "endpoints.json");
}
for (const auto &proxyStorageUrl : proxyStorageUrls) {
request.setUrl(proxyStorageUrl);
reply = amnApp->networkManager()->get(request);
@@ -244,10 +286,7 @@ QStringList GatewayController::getProxyUrls(const QString &serviceType, const QS
}
return endpoints;
} else {
auto replyError = reply->error();
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << replyError;
qDebug() << httpStatusCode;
apiUtils::checkNetworkReplyErrors(sslErrors, reply);
qDebug() << "go to the next storage endpoint";
reply->deleteLater();
@@ -256,33 +295,33 @@ QStringList GatewayController::getProxyUrls(const QString &serviceType, const QS
return {};
}
bool GatewayController::shouldBypassProxy(const QNetworkReply::NetworkError &replyError, const QByteArray &responseBody,
bool checkEncryption, const QByteArray &key, const QByteArray &iv, const QByteArray &salt)
bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key,
const QByteArray &iv, const QByteArray &salt)
{
if (replyError == QNetworkReply::NetworkError::OperationCanceledError || replyError == QNetworkReply::NetworkError::TimeoutError) {
if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError || reply->error() == QNetworkReply::NetworkError::TimeoutError) {
qDebug() << "timeout occurred";
qDebug() << replyError;
qDebug() << reply->error();
return true;
} else if (responseBody.contains("html")) {
qDebug() << "the response contains an html tag";
return true;
} else if (replyError == QNetworkReply::NetworkError::ContentNotFoundError) {
} else if (reply->error() == QNetworkReply::NetworkError::ContentNotFoundError) {
if (responseBody.contains(errorResponsePattern1) || responseBody.contains(errorResponsePattern2)
|| responseBody.contains(errorResponsePattern3)) {
return false;
} else {
qDebug() << replyError;
qDebug() << reply->error();
return true;
}
} else if (replyError == QNetworkReply::NetworkError::OperationNotImplementedError) {
} else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) {
if (responseBody.contains(updateRequestResponsePattern)) {
return false;
} else {
qDebug() << replyError;
qDebug() << reply->error();
return true;
}
} else if (replyError != QNetworkReply::NetworkError::NoError) {
qDebug() << replyError;
} else if (reply->error() != QNetworkReply::NetworkError::NoError) {
qDebug() << reply->error();
return true;
} else if (checkEncryption) {
try {
@@ -296,74 +335,29 @@ bool GatewayController::shouldBypassProxy(const QNetworkReply::NetworkError &rep
return false;
}
void GatewayController::bypassProxy(const QString &endpoint, const QString &serviceType, const QString &userCountryCode,
void GatewayController::bypassProxy(const QString &endpoint, QNetworkReply *reply,
std::function<QNetworkReply *(const QString &url)> requestFunction,
std::function<bool(QNetworkReply *reply, const QList<QSslError> &sslErrors)> replyProcessingFunction)
{
QStringList proxyUrls = getProxyUrls(serviceType, userCountryCode);
QStringList proxyUrls = getProxyUrls();
std::random_device randomDevice;
std::mt19937 generator(randomDevice());
std::shuffle(proxyUrls.begin(), proxyUrls.end(), generator);
QEventLoop wait;
QList<QSslError> sslErrors;
QByteArray responseBody;
auto bypassFunction = [this](const QString &endpoint, const QString &proxyUrl,
std::function<QNetworkReply *(const QString &url)> requestFunction,
std::function<bool(QNetworkReply * reply, const QList<QSslError> &sslErrors)> replyProcessingFunction) {
QEventLoop wait;
QList<QSslError> sslErrors;
for (const QString &proxyUrl : proxyUrls) {
qDebug() << "go to the next proxy endpoint";
QNetworkReply *reply = requestFunction(endpoint.arg(proxyUrl));
reply->deleteLater(); // delete the previous reply
reply = requestFunction(endpoint.arg(proxyUrl));
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec();
auto result = replyProcessingFunction(reply, sslErrors);
reply->deleteLater();
return result;
};
if (m_proxyUrl.isEmpty()) {
QNetworkRequest request;
request.setTransferTimeout(1000);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QEventLoop wait;
QList<QSslError> sslErrors;
QNetworkReply *reply;
for (const QString &proxyUrl : proxyUrls) {
request.setUrl(proxyUrl + "lmbd-health");
reply = amnApp->networkManager()->get(request);
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec();
if (reply->error() == QNetworkReply::NetworkError::NoError) {
reply->deleteLater();
m_proxyUrl = proxyUrl;
if (!m_proxyUrl.isEmpty()) {
break;
}
} else {
reply->deleteLater();
}
}
}
if (!m_proxyUrl.isEmpty()) {
if (bypassFunction(endpoint, m_proxyUrl, requestFunction, replyProcessingFunction)) {
return;
}
}
for (const QString &proxyUrl : proxyUrls) {
if (bypassFunction(endpoint, proxyUrl, requestFunction, replyProcessingFunction)) {
m_proxyUrl = proxyUrl;
if (replyProcessingFunction(reply, sslErrors)) {
break;
}
}
+5 -7
View File
@@ -18,22 +18,20 @@ public:
explicit GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
const bool isStrictKillSwitchEnabled, QObject *parent = nullptr);
amnezia::ErrorCode get(const QString &endpoint, QByteArray &responseBody);
amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody);
private:
QStringList getProxyUrls(const QString &serviceType, const QString &userCountryCode);
bool shouldBypassProxy(const QNetworkReply::NetworkError &replyError, const QByteArray &responseBody, bool checkEncryption,
const QByteArray &key = "", const QByteArray &iv = "", const QByteArray &salt = "");
void bypassProxy(const QString &endpoint, const QString &serviceType, const QString &userCountryCode,
std::function<QNetworkReply *(const QString &url)> requestFunction,
QStringList getProxyUrls();
bool shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key = "",
const QByteArray &iv = "", const QByteArray &salt = "");
void bypassProxy(const QString &endpoint, QNetworkReply *reply, std::function<QNetworkReply *(const QString &url)> requestFunction,
std::function<bool(QNetworkReply *reply, const QList<QSslError> &sslErrors)> replyProcessingFunction);
int m_requestTimeoutMsecs;
QString m_gatewayEndpoint;
bool m_isDevEnvironment = false;
bool m_isStrictKillSwitchEnabled = false;
inline static QString m_proxyUrl;
};
#endif // GATEWAYCONTROLLER_H
-1
View File
@@ -120,7 +120,6 @@ namespace amnezia
ApiNotFoundError = 1109,
ApiMigrationError = 1110,
ApiUpdateRequestError = 1111,
ApiSubscriptionExpiredError = 1112,
// QFile errors
OpenError = 1200,
-1
View File
@@ -77,7 +77,6 @@ QString errorString(ErrorCode code) {
case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break;
case (ErrorCode::ApiMigrationError): errorMessage = QObject::tr("A migration error has occurred. Please contact our technical support"); break;
case (ErrorCode::ApiUpdateRequestError): errorMessage = QObject::tr("Please update the application to use this feature"); break;
case (ErrorCode::ApiSubscriptionExpiredError): errorMessage = QObject::tr("Your Amnezia Premium subscription has expired.\n Please check your email for renewal instructions.\n If you haven't received an email, please contact our support."); break;
// QFile errors
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;
+2 -2
View File
@@ -23,7 +23,7 @@
#include <sys/socket.h>
#include <unistd.h>
#endif
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS)
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
@@ -390,7 +390,7 @@ QString NetworkUtilities::getGatewayAndIface()
close(sock);
return gateway_address;
#endif
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS)
QString gateway;
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_FLAGS, RTF_GATEWAY};
int afinet_type[] = {AF_INET, AF_INET6};
+2 -2
View File
@@ -101,10 +101,10 @@ QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
out << "MTU = " << m_deviceMTU << "\n";
}
if (!m_primaryDnsServer.isEmpty()) {
if (!m_primaryDnsServer.isNull()) {
QStringList dnsServers;
dnsServers.append(m_primaryDnsServer);
if (!m_secondaryDnsServer.isEmpty()) {
if (!m_secondaryDnsServer.isNull()) {
dnsServers.append(m_secondaryDnsServer);
}
// If the DNS is not the Gateway, it's a user defined DNS
-14
View File
@@ -1,14 +0,0 @@
<svg width="24" height="24" viewBox="0 0 74 74" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_4_34)">
<path d="M55.5 12.3333H18.5C15.0942 12.3333 12.3333 15.0943 12.3333 18.5V55.5C12.3333 58.9058 15.0942 61.6667 18.5 61.6667H55.5C58.9057 61.6667 61.6666 58.9058 61.6666 55.5V18.5C61.6666 15.0943 58.9057 12.3333 55.5 12.3333Z" stroke="#CBCAC8" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M21.5833 24.6667H52.4167" stroke="#CBCAC8" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M21.5833 37H52.4167" stroke="#CBCAC8" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M21.5833 49.3333H40.0833" stroke="#CBCAC8" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="61.5" cy="12.5" r="15" fill="#FBB36B" stroke="#1C1D21" stroke-width="5"/>
</g>
<defs>
<clipPath id="clip0_4_34">
<rect width="74" height="74" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 982 B

-8
View File
@@ -1,8 +0,0 @@
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="#CBCAC8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<!-- Основа газеты -->
<rect x="4" y="4" width="16" height="16" rx="2"/>
<!-- Линии текста -->
<line x1="7" y1="8" x2="17" y2="8"/>
<line x1="7" y1="12" x2="17" y2="12"/>
<line x1="7" y1="16" x2="13" y2="16"/>
</svg>

Before

Width:  |  Height:  |  Size: 410 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.9 KiB

-3
View File
@@ -1,3 +0,0 @@
<svg width="16" height="16" viewBox="0 0 35 35" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="17.5" cy="17.5" r="15" fill="#FBB36B" stroke="#1C1D21" stroke-width="5"/>
</svg>

Before

Width:  |  Height:  |  Size: 188 B

+2 -26
View File
@@ -32,41 +32,17 @@
<false/>
<key>UILaunchStoryboardName</key>
<string>AmneziaVPNLaunchScreen</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>QIOSWindowSceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UIRequiredDeviceCapabilities</key>
<array/>
<key>UIRequiresFullScreen</key>
<false/>
<true/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<array/>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
<key>com.wireguard.ios.app_group_id</key>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 682 B

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 340 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

+3 -65
View File
@@ -1,68 +1,6 @@
{
"images": [
{
"idiom": "mac",
"size": "16x16",
"scale": "1x",
"filename": "16.png"
},
{
"idiom": "mac",
"size": "16x16",
"scale": "2x",
"filename": "16@2x.png"
},
{
"idiom": "mac",
"size": "32x32",
"scale": "1x",
"filename": "32.png"
},
{
"idiom": "mac",
"size": "32x32",
"scale": "2x",
"filename": "32@2x.png"
},
{
"idiom": "mac",
"size": "128x128",
"scale": "1x",
"filename": "128.png"
},
{
"idiom": "mac",
"size": "128x128",
"scale": "2x",
"filename": "128@2x.png"
},
{
"idiom": "mac",
"size": "256x256",
"scale": "1x",
"filename": "256.png"
},
{
"idiom": "mac",
"size": "256x256",
"scale": "2x",
"filename": "256@2x.png"
},
{
"idiom": "mac",
"size": "512x512",
"scale": "1x",
"filename": "512.png"
},
{
"idiom": "mac",
"size": "512x512",
"scale": "2x",
"filename": "512@2x.png"
}
],
"info": {
"version": 1,
"author": "xcode"
"info" : {
"author" : "xcode",
"version" : 1
}
}
+50
View File
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleAllowMixedLocalizations</key>
<true/>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>LSMultipleInstancesProhibited</key>
<true/>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>
</plist>
-172
View File
@@ -1,172 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleAllowMixedLocalizations</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>${QT_INTERNAL_DOLLAR_VAR}{PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>com.wireguard.ios.app_group_id</key>
<string>group.org.amnezia.AmneziaVPN</string>
<key>NSCameraUsageDescription</key>
<string>Amnezia VPN needs access to the camera for reading QR-codes.</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
<key>CFBundleIcons</key>
<dict/>
<key>UTImportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>Amnezia VPN config</string>
<key>UTTypeIconFiles</key>
<array/>
<key>UTTypeIdentifier</key>
<string>org.amnezia.AmneziaVPN.amnezia-config</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>vpn</string>
</array>
<key>public.mime-type</key>
<array>
<string>text/plain</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>WireGuard config</string>
<key>UTTypeIconFiles</key>
<array/>
<key>UTTypeIdentifier</key>
<string>org.amnezia.AmneziaVPN.wireguard-config</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>conf</string>
<string>cfg</string>
</array>
<key>public.mime-type</key>
<array>
<string>text/plain</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>OpenVPN config</string>
<key>UTTypeIconFiles</key>
<array/>
<key>UTTypeIdentifier</key>
<string>org.amnezia.AmneziaVPN.openvpn-config</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>ovpn</string>
</array>
<key>public.mime-type</key>
<array>
<string>text/plain</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>AmneziaVPN backup file</string>
<key>UTTypeIconFiles</key>
<array/>
<key>UTTypeIdentifier</key>
<string>org.amnezia.AmneziaVPN.backup-config</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>backup</string>
</array>
<key>public.mime-type</key>
<array>
<string>text/plain</string>
</array>
</dict>
</dict>
</array>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>Amnezia VPN config</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>org.amnezia.AmneziaVPN.amnezia-config</string>
<string>org.amnezia.AmneziaVPN.wireguard-config</string>
<string>org.amnezia.AmneziaVPN.openvpn-config</string>
<string>org.amnezia.AmneziaVPN.backup-config</string>
</array>
</dict>
</array>
<key>NSExtensions</key>
<array>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.networkextension.packet-tunnel</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
</dict>
</array>
</dict>
</plist>
+21 -27
View File
@@ -2,40 +2,34 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.networking.custom-protocol</key>
<true/>
<key>com.apple.application-identifier</key>
<string>$(DEVELOPMENT_TEAM).$(APP_ID_MACOS)</string>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>app-proxy-provider</string>
<string>packet-tunnel-provider</string>
<string>dns-settings</string>
<string>relay</string>
<string>content-filter-provider</string>
<string>dns-proxy</string>
</array>
<key>com.apple.developer.system-extension.install</key>
<true/>
<key>com.apple.developer.networking.vpn.api</key>
<array>
<string>allow-vpn</string>
</array>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.org.amnezia.AmneziaVPN</string>
</array>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>keychain-access-groups</key>
<array>
<string>$(DEVELOPMENT_TEAM).*</string>
</array>
<key>com.apple.developer.team-identifier</key>
<string>$(DEVELOPMENT_TEAM)</string>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>$(DEVELOPMENT_TEAM).$(GROUP_ID_MACOS)</string>
</array>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>
@@ -2,30 +2,41 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.networking.custom-protocol</key>
<true/>
<key>com.apple.application-identifier</key>
<string>$(DEVELOPMENT_TEAM).$(NETEXT_ID_MACOS)</string>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>dns-settings</string>
<string>relay</string>
<string>packet-tunnel-provider</string>
<string>content-filter-provider</string>
<string>dns-proxy</string>
<string>app-proxy-provider</string>
</array>
<key>com.apple.developer.networking.vpn.api</key>
<key>keychain-access-groups</key>
<array>
<string>allow-vpn</string>
<string>$(DEVELOPMENT_TEAM).*</string>
</array>
<key>com.apple.developer.team-identifier</key>
<string>$(DEVELOPMENT_TEAM)</string>
<key>com.apple.developer.system-extension.install</key>
<true/>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.org.amnezia.AmneziaVPN</string>
<string>$(DEVELOPMENT_TEAM).$(GROUP_ID_MACOS)</string>
</array>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.private.network.socket-delegate</key>
<true/>
</dict>
</plist>
@@ -1,138 +0,0 @@
enable_language(Swift)
message("Client message >> macos build >> AmneziaVPNNetworkExtension")
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
add_executable(AmneziaVPNNetworkExtension)
message("executable_path is: @executable_path/../../Frameworks")
set_target_properties(AmneziaVPNNetworkExtension PROPERTIES
XCODE_PRODUCT_TYPE com.apple.product-type.app-extension
# MACOSX_BUNDLE YES
BUNDLE_EXTENSION appex
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
MACOSX_BUNDLE_INFO_STRING "AmneziaVPNNetworkExtension"
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPNNetworkExtension"
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}.network-extension"
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_NAME "${BUILD_IOS_APP_IDENTIFIER}.network-extension"
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS ${CMAKE_CURRENT_SOURCE_DIR}/AmneziaVPNNetworkExtension.entitlements
XCODE_ATTRIBUTE_MARKETING_VERSION "${APP_MAJOR_VERSION}"
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${BUILD_ID}"
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPNNetworkExtension"
XCODE_ATTRIBUTE_APPLICATION_EXTENSION_API_ONLY "YES"
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "11.0"
XCODE_ATTRIBUTE_INFOPLIST_FILE ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../../../Frameworks @loader_path/../../../../Frameworks"
)
if(DEPLOY)
message("DEPLOY is ON")
set_target_properties(AmneziaVPNNetworkExtension PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr macos.org.amnezia.amneziaVPN.NE"
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev macos.org.amnezia.amneziaVPN.NE"
)
else()
set_target_properties(AmneziaVPNNetworkExtension PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
)
endif()
set_target_properties(AmneziaVPNNetworkExtension PROPERTIES
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/WireGuardNetworkExtension-Bridging-Header.h"
XCODE_ATTRIBUTE_SWIFT_OPTIMIZATION_LEVEL "-Onone"
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
)
set_target_properties("AmneziaVPNNetworkExtension" PROPERTIES
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
)
find_library(FW_ASSETS_LIBRARY AssetsLibrary)
find_library(FW_MOBILE_CORE MobileCoreServices)
find_library(FW_UI_KIT UIKit)
find_library(FW_LIBRESOLV libresolv.9.tbd)
# Set the root directory
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${FW_LIBRESOLV})
target_compile_options(AmneziaVPNNetworkExtension PRIVATE -DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\")
target_compile_options(AmneziaVPNNetworkExtension PRIVATE -DNETWORK_EXTENSION=1)
set(WG_APPLE_SOURCE_DIR ${CLIENT_ROOT_DIR}/3rd/amneziawg-apple/Sources)
message("WG_APPLE_SOURCE_DIR is: ${WG_APPLE_SOURCE_DIR}")
message("CLIENT_ROOT_DIR is: ${CLIENT_ROOT_DIR}")
target_sources(AmneziaVPNNetworkExtension PRIVATE
${WG_APPLE_SOURCE_DIR}/WireGuardKit/WireGuardAdapter.swift
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PacketTunnelSettingsGenerator.swift
${WG_APPLE_SOURCE_DIR}/WireGuardKit/DNSResolver.swift
${WG_APPLE_SOURCE_DIR}/WireGuardNetworkExtension/ErrorNotifier.swift
${WG_APPLE_SOURCE_DIR}/Shared/Keychain.swift
${WG_APPLE_SOURCE_DIR}/Shared/Model/TunnelConfiguration+WgQuickConfig.swift
${WG_APPLE_SOURCE_DIR}/Shared/Model/NETunnelProviderProtocol+Extension.swift
${WG_APPLE_SOURCE_DIR}/Shared/Model/String+ArrayConversion.swift
${WG_APPLE_SOURCE_DIR}/WireGuardKit/TunnelConfiguration.swift
${WG_APPLE_SOURCE_DIR}/WireGuardKit/IPAddressRange.swift
${WG_APPLE_SOURCE_DIR}/WireGuardKit/Endpoint.swift
${WG_APPLE_SOURCE_DIR}/WireGuardKit/DNSServer.swift
${WG_APPLE_SOURCE_DIR}/WireGuardKit/InterfaceConfiguration.swift
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PeerConfiguration.swift
${WG_APPLE_SOURCE_DIR}/Shared/FileManager+Extension.swift
${WG_APPLE_SOURCE_DIR}/WireGuardKitC/x25519.c
${WG_APPLE_SOURCE_DIR}/WireGuardKit/Array+ConcurrentMap.swift
${WG_APPLE_SOURCE_DIR}/WireGuardKit/IPAddress+AddrInfo.swift
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PrivateKey.swift
${CLIENT_ROOT_DIR}/platforms/ios/HevSocksTunnel.swift
${CLIENT_ROOT_DIR}/platforms/ios/NELogController.swift
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider.swift
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+WireGuard.swift
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+OpenVPN.swift
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+Xray.swift
${CLIENT_ROOT_DIR}/platforms/ios/WGConfig.swift
${CLIENT_ROOT_DIR}/platforms/ios/iosglue.mm
${CLIENT_ROOT_DIR}/platforms/ios/XrayConfig.swift
)
target_sources(AmneziaVPNNetworkExtension PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
)
set_property(TARGET AmneziaVPNNetworkExtension APPEND PROPERTY RESOURCE
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
)
## Build wireguard-go-version.h
execute_process(
COMMAND go list -m golang.zx2c4.com/wireguard
WORKING_DIRECTORY ${CLIENT_ROOT_DIR}/3rd/wireguard-apple/Sources/WireGuardKitGo
OUTPUT_VARIABLE WG_VERSION_FULL
)
string(REGEX REPLACE ".*v\([0-9.]*\).*" "\\1" WG_VERSION_STRING 1.1.1)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/wireguard-go-version.h.in
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
target_sources(AmneziaVPNNetworkExtension PRIVATE
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR})
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/wireguard/macos/universal2/libwg-go.a)
message(${CLIENT_ROOT_DIR})
message(${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/libhev-socks5-tunnel.a)
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/libhev-socks5-tunnel.a)
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/Headers)
@@ -3,32 +3,27 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>AmneziaVPNNetworkExtension</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.amnezia.AmneziaVPN.network-extension</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>AmneziaVPNNetworkExtension</string>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>${APPLE_PROJECT_VERSION}</string>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>${CMAKE_PROJECT_VERSION_TWEAK}</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSMinimumSystemVersion</key>
<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
<key>CFBundleDisplayName</key>
<string>AmneziaVPNNetworkExtension</string>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
@@ -36,11 +31,5 @@
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
</dict>
<key>com.wireguard.ios.app_group_id</key>
<string>group.org.amnezia.AmneziaVPN</string>
<key>com.wireguard.macos.app_group_id</key>
<string>${BUILD_VPN_DEVELOPMENT_TEAM}.group.org.amnezia.AmneziaVPN</string>
</dict>
</plist>
@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>1C8F.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
</array>
</dict>
</array>
</dict>
</plist>
@@ -1,10 +1,10 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 "macos/gobridge/wireguard.h"
#include "wireguard-go-version.h"
#include "3rd/amneziawg-apple/Sources/WireGuardKitGo/wireguard.h"
#include "3rd/amneziawg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
#include "3rd/awg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
#include <stdbool.h>
#include <stdint.h>
@@ -23,8 +23,3 @@ bool key_from_hex(uint8_t key[WG_KEY_LEN], const char* hex);
bool key_eq(const uint8_t key1[WG_KEY_LEN], const uint8_t key2[WG_KEY_LEN]);
void write_msg_to_log(const char* tag, const char* msg);
// init function definition in C
void hev_socks5_tunnel_quit(void);
// Updated function definition in C
int hev_socks5_tunnel_main(const char* configFile, int fd);
@@ -1,3 +0,0 @@
#ifndef WIREGUARD_GO_VERSION
#define WIREGUARD_GO_VERSION "@WG_VERSION_STRING@"
#endif // WIREGUARD_GO_VERSION
+2 -2
View File
@@ -15,7 +15,7 @@
#include "platforms/ios/QtAppDelegate-C-Interface.h"
#endif
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
bool isAnotherInstanceRunning()
{
QLocalSocket socket;
@@ -45,7 +45,7 @@ int main(int argc, char *argv[])
AmneziaApplication app(argc, argv);
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
if (isAnotherInstanceRunning()) {
QTimer::singleShot(1000, &app, [&]() { app.quit(); });
return app.exec();
+15 -15
View File
@@ -264,13 +264,13 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
&& !wgConfig.value(amnezia::config_key::junkPacketMaxSize).isUndefined()
&& !wgConfig.value(amnezia::config_key::initPacketJunkSize).isUndefined()
&& !wgConfig.value(amnezia::config_key::responsePacketJunkSize).isUndefined()
// && !wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize).isUndefined()
// && !wgConfig.value(amnezia::config_key::transportPacketJunkSize).isUndefined()
&& !wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize).isUndefined()
&& !wgConfig.value(amnezia::config_key::transportPacketJunkSize).isUndefined()
&& !wgConfig.value(amnezia::config_key::initPacketMagicHeader).isUndefined()
&& !wgConfig.value(amnezia::config_key::responsePacketMagicHeader).isUndefined()
&& !wgConfig.value(amnezia::config_key::underloadPacketMagicHeader).isUndefined()
&& !wgConfig.value(amnezia::config_key::transportPacketMagicHeader).isUndefined()
/* && !wgConfig.value(amnezia::config_key::specialJunk1).isUndefined()
&& !wgConfig.value(amnezia::config_key::specialJunk1).isUndefined()
&& !wgConfig.value(amnezia::config_key::specialJunk2).isUndefined()
&& !wgConfig.value(amnezia::config_key::specialJunk3).isUndefined()
&& !wgConfig.value(amnezia::config_key::specialJunk4).isUndefined()
@@ -278,27 +278,27 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
&& !wgConfig.value(amnezia::config_key::controlledJunk1).isUndefined()
&& !wgConfig.value(amnezia::config_key::controlledJunk2).isUndefined()
&& !wgConfig.value(amnezia::config_key::controlledJunk3).isUndefined()
&& !wgConfig.value(amnezia::config_key::specialHandshakeTimeout).isUndefined()*/) {
&& !wgConfig.value(amnezia::config_key::specialHandshakeTimeout).isUndefined()) {
json.insert(amnezia::config_key::junkPacketCount, wgConfig.value(amnezia::config_key::junkPacketCount));
json.insert(amnezia::config_key::junkPacketMinSize, wgConfig.value(amnezia::config_key::junkPacketMinSize));
json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize));
json.insert(amnezia::config_key::initPacketJunkSize, wgConfig.value(amnezia::config_key::initPacketJunkSize));
json.insert(amnezia::config_key::responsePacketJunkSize, wgConfig.value(amnezia::config_key::responsePacketJunkSize));
// json.insert(amnezia::config_key::cookieReplyPacketJunkSize, wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize));
// json.insert(amnezia::config_key::transportPacketJunkSize, wgConfig.value(amnezia::config_key::transportPacketJunkSize));
json.insert(amnezia::config_key::cookieReplyPacketJunkSize, wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize));
json.insert(amnezia::config_key::transportPacketJunkSize, wgConfig.value(amnezia::config_key::transportPacketJunkSize));
json.insert(amnezia::config_key::initPacketMagicHeader, wgConfig.value(amnezia::config_key::initPacketMagicHeader));
json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader));
json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader));
json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader));
// json.insert(amnezia::config_key::specialJunk1, wgConfig.value(amnezia::config_key::specialJunk1));
// json.insert(amnezia::config_key::specialJunk2, wgConfig.value(amnezia::config_key::specialJunk2));
// json.insert(amnezia::config_key::specialJunk3, wgConfig.value(amnezia::config_key::specialJunk3));
// json.insert(amnezia::config_key::specialJunk4, wgConfig.value(amnezia::config_key::specialJunk4));
// json.insert(amnezia::config_key::specialJunk5, wgConfig.value(amnezia::config_key::specialJunk5));
// json.insert(amnezia::config_key::controlledJunk1, wgConfig.value(amnezia::config_key::controlledJunk1));
// json.insert(amnezia::config_key::controlledJunk2, wgConfig.value(amnezia::config_key::controlledJunk2));
// json.insert(amnezia::config_key::controlledJunk3, wgConfig.value(amnezia::config_key::controlledJunk3));
// json.insert(amnezia::config_key::specialHandshakeTimeout, wgConfig.value(amnezia::config_key::specialHandshakeTimeout));
json.insert(amnezia::config_key::specialJunk1, wgConfig.value(amnezia::config_key::specialJunk1));
json.insert(amnezia::config_key::specialJunk2, wgConfig.value(amnezia::config_key::specialJunk2));
json.insert(amnezia::config_key::specialJunk3, wgConfig.value(amnezia::config_key::specialJunk3));
json.insert(amnezia::config_key::specialJunk4, wgConfig.value(amnezia::config_key::specialJunk4));
json.insert(amnezia::config_key::specialJunk5, wgConfig.value(amnezia::config_key::specialJunk5));
json.insert(amnezia::config_key::controlledJunk1, wgConfig.value(amnezia::config_key::controlledJunk1));
json.insert(amnezia::config_key::controlledJunk2, wgConfig.value(amnezia::config_key::controlledJunk2));
json.insert(amnezia::config_key::controlledJunk3, wgConfig.value(amnezia::config_key::controlledJunk3));
json.insert(amnezia::config_key::specialHandshakeTimeout, wgConfig.value(amnezia::config_key::specialHandshakeTimeout));
}
write(json);
@@ -1,82 +0,0 @@
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
#include <dispatch/dispatch.h>
#include <QByteArray>
#include <QFile>
#include <QString>
#include "ios_controller.h"
using SceneOpenURLContexts = void (*)(id, SEL, UIScene *, NSSet<UIOpenURLContext *> *);
static SceneOpenURLContexts g_originalSceneOpenURLContexts = nullptr;
static void amnezia_handleURL(NSURL *url)
{
if (!url || !url.isFileURL) {
return;
}
QString filePath(url.path.UTF8String);
if (filePath.isEmpty()) {
return;
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (filePath.contains("backup")) {
IosController::Instance()->importBackupFromOutside(filePath);
return;
}
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly)) {
return;
}
const QByteArray data = file.readAll();
IosController::Instance()->importConfigFromOutside(QString::fromUtf8(data));
});
}
static void amnezia_scene_openURLContexts(id self, SEL _cmd, UIScene *scene, NSSet<UIOpenURLContext *> *contexts)
{
if (g_originalSceneOpenURLContexts) {
g_originalSceneOpenURLContexts(self, _cmd, scene, contexts);
}
if (!contexts || contexts.count == 0) {
return;
}
if (@available(iOS 13.0, *)) {
for (UIOpenURLContext *context in contexts) {
amnezia_handleURL(context.URL);
}
}
}
@interface AmneziaSceneDelegateHooks : NSObject
@end
@implementation AmneziaSceneDelegateHooks
+ (void)load
{
Class cls = objc_getClass("QIOSWindowSceneDelegate");
if (!cls) {
return;
}
SEL selector = @selector(scene:openURLContexts:);
Method method = class_getInstanceMethod(cls, selector);
if (method) {
g_originalSceneOpenURLContexts = reinterpret_cast<SceneOpenURLContexts>(method_getImplementation(method));
method_setImplementation(method, reinterpret_cast<IMP>(amnezia_scene_openURLContexts));
} else {
const char *types = "v@:@@";
class_addMethod(cls, selector, reinterpret_cast<IMP>(amnezia_scene_openURLContexts), types);
}
}
@end
+3 -35
View File
@@ -2,8 +2,7 @@ import Foundation
import os.log
struct Log {
private static let subsystemIdentifier = Bundle.main.bundleIdentifier ?? "org.amnezia.AmneziaVPN"
static let osLog = Logger(subsystem: subsystemIdentifier, category: "App")
static let osLog = Logger()
private static let IsLoggingEnabledKey = "IsLoggingEnabled"
static var isLoggingEnabled: Bool {
@@ -78,41 +77,10 @@ struct Log {
static func log(_ type: OSLogType, title: String = "", message: String, url: URL = neLogURL) {
NSLog("\(title) \(message)")
switch type {
case .debug:
if title.isEmpty {
osLog.debug("\(message, privacy: .public)")
} else {
osLog.debug("\(title, privacy: .public) \(message, privacy: .public)")
}
case .info:
if title.isEmpty {
osLog.info("\(message, privacy: .public)")
} else {
osLog.info("\(title, privacy: .public) \(message, privacy: .public)")
}
case .error:
if title.isEmpty {
osLog.error("\(message, privacy: .public)")
} else {
osLog.error("\(title, privacy: .public) \(message, privacy: .public)")
}
case .fault:
if title.isEmpty {
osLog.fault("\(message, privacy: .public)")
} else {
osLog.fault("\(title, privacy: .public) \(message, privacy: .public)")
}
default:
if title.isEmpty {
osLog.log("\(message, privacy: .public)")
} else {
osLog.log("\(title, privacy: .public) \(message, privacy: .public)")
}
}
guard isLoggingEnabled else { return }
osLog.log(level: type, "\(title) \(message)")
let date = Date()
let level = Record.Level(from: type)
let messages = message.split(whereSeparator: \.isNewline)
+1 -55
View File
@@ -1,76 +1,22 @@
import Foundation
import os.log
private let subsystemIdentifier = Bundle.main.bundleIdentifier ?? "org.amnezia.AmneziaVPN"
private let wireGuardSystemLogger = Logger(subsystem: subsystemIdentifier, category: "WireGuard")
private let openVPNSystemLogger = Logger(subsystem: subsystemIdentifier, category: "OpenVPN")
private let xraySystemLogger = Logger(subsystem: subsystemIdentifier, category: "Xray")
private let networkExtensionLogger = Logger(subsystem: subsystemIdentifier, category: "NetworkExtension")
private func logToSystem(_ logger: Logger, type: OSLogType, prefix: String, title: String, message: String) {
let combinedTitle: String
if title.isEmpty {
combinedTitle = prefix
} else {
combinedTitle = "\(prefix): \(title)"
}
switch type {
case .debug:
if combinedTitle.isEmpty {
logger.debug("\(message, privacy: .public)")
} else {
logger.debug("\(combinedTitle, privacy: .public) \(message, privacy: .public)")
}
case .info:
if combinedTitle.isEmpty {
logger.info("\(message, privacy: .public)")
} else {
logger.info("\(combinedTitle, privacy: .public) \(message, privacy: .public)")
}
case .error:
if combinedTitle.isEmpty {
logger.error("\(message, privacy: .public)")
} else {
logger.error("\(combinedTitle, privacy: .public) \(message, privacy: .public)")
}
case .fault:
if combinedTitle.isEmpty {
logger.fault("\(message, privacy: .public)")
} else {
logger.fault("\(combinedTitle, privacy: .public) \(message, privacy: .public)")
}
default:
if combinedTitle.isEmpty {
logger.log("\(message, privacy: .public)")
} else {
logger.log("\(combinedTitle, privacy: .public) \(message, privacy: .public)")
}
}
}
public func wg_log(_ type: OSLogType, title: String = "", staticMessage: StaticString) {
let stringMessage = String(describing: staticMessage)
logToSystem(wireGuardSystemLogger, type: type, prefix: "WG", title: title, message: stringMessage)
neLog(type, title: "WG: \(title)", message: stringMessage)
neLog(type, title: "WG: \(title)", message: "\(staticMessage)")
}
public func wg_log(_ type: OSLogType, title: String = "", message: String) {
logToSystem(wireGuardSystemLogger, type: type, prefix: "WG", title: title, message: message)
neLog(type, title: "WG: \(title)", message: message)
}
public func ovpnLog(_ type: OSLogType, title: String = "", message: String) {
logToSystem(openVPNSystemLogger, type: type, prefix: "OVPN", title: title, message: message)
neLog(type, title: "OVPN: \(title)", message: message)
}
public func xrayLog(_ type: OSLogType, title: String = "", message: String) {
logToSystem(xraySystemLogger, type: type, prefix: "XRAY", title: title, message: message)
neLog(type, title: "XRAY: \(title)", message: message)
}
public func neLog(_ type: OSLogType, title: String = "", message: String) {
logToSystem(networkExtensionLogger, type: type, prefix: "NE", title: title, message: message)
Log.log(type, title: "NE: \(title)", message: message)
}
@@ -1,7 +1,6 @@
import Foundation
import NetworkExtension
import OpenVPNAdapter
import CryptoKit
struct OpenVPNConfig: Decodable {
let config: String
@@ -28,83 +27,26 @@ extension PacketTunnelProvider {
let ovpnConfiguration = Data(openVPNConfig.config.utf8)
setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, completionHandler: completionHandler)
} catch {
ovpnLog(.error, message: "Can't parse OpenVPN config: \(error.localizedDescription)")
ovpnLog(.error, message: "Can't parse config: \(error.localizedDescription)")
if let underlyingError = (error as NSError).userInfo[NSUnderlyingErrorKey] as? NSError {
ovpnLog(.error, message: "Can't parse config: \(underlyingError.localizedDescription)")
}
return
}
}
private func logOpenVPNError(_ error: NSError) {
let fatalFlag = (error.userInfo[OpenVPNAdapterErrorFatalKey] as? Bool) ?? false
var lines: [String] = []
lines.append("domain=\(error.domain) code=\(error.code) fatal=\(fatalFlag)")
if let adapterMessage = error.userInfo[OpenVPNAdapterErrorMessageKey] as? String, !adapterMessage.isEmpty {
lines.append("message=\(adapterMessage)")
}
let userInfoKeys = error.userInfo.keys.map { String(describing: $0) }.sorted()
if !userInfoKeys.isEmpty {
lines.append("userInfoKeys=[\(userInfoKeys.joined(separator: ","))]")
}
if let underlying = error.userInfo[NSUnderlyingErrorKey] as? NSError {
lines.append("underlying=\(underlying.domain)#\(underlying.code) fatal=\((underlying.userInfo[OpenVPNAdapterErrorFatalKey] as? Bool) ?? false)")
if let underlyingMessage = underlying.userInfo[OpenVPNAdapterErrorMessageKey] as? String, !underlyingMessage.isEmpty {
lines.append("underlyingMessage=\(underlyingMessage)")
} else if !underlying.localizedDescription.isEmpty {
lines.append("underlyingLocalized=\(underlying.localizedDescription)")
}
} else if let underlying = error.userInfo[NSUnderlyingErrorKey] {
lines.append("underlyingRaw=\(underlying)")
}
let formatted = lines.joined(separator: "\n ")
ovpnLog(.error, title: "Error", message: formatted)
}
private func setupAndlaunchOpenVPN(withConfig ovpnConfiguration: Data,
withShadowSocks viaSS: Bool = false,
completionHandler: @escaping (Error?) -> Void) {
ovpnLog(.info, message: "Setup and launch")
var configString = String(decoding: ovpnConfiguration, as: UTF8.self)
let digest = SHA256.hash(data: ovpnConfiguration)
let digestString = digest.map { String(format: "%02x", $0) }.joined()
ovpnLog(.info, title: "ConfigDigest", message: digestString)
let hasTlsAuthOpen = configString.contains("<tls-auth>")
let hasTlsAuthClose = configString.contains("</tls-auth>")
ovpnLog(.info, title: "ConfigFlags", message: "tls-auth open=\(hasTlsAuthOpen) close=\(hasTlsAuthClose)")
let lines = configString.split(separator: "\n")
let head = lines.prefix(10).joined(separator: "\n")
let tail = lines.suffix(10).joined(separator: "\n")
ovpnLog(.debug, title: "ConfigHead", message: head)
ovpnLog(.debug, title: "ConfigTail", message: tail)
if let start = configString.range(of: "<tls-auth>"),
let end = configString.range(of: "</tls-auth>", range: start.upperBound..<configString.endIndex) {
let keyBody = String(configString[start.upperBound..<end.lowerBound])
ovpnLog(.debug, title: "TLSAuthInline", message: keyBody)
let sanitizedLines = keyBody
.split(whereSeparator: { $0.isNewline })
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
.filter { !$0.isEmpty }
.filter { !$0.hasPrefix("#") }
let sanitizedKey = sanitizedLines.joined(separator: "\n")
ovpnLog(.debug, title: "TLSAuthSanitized", message: sanitizedKey)
let sanitizedBlock = "<tls-auth>\n\(sanitizedKey)\n</tls-auth>"
configString.replaceSubrange(start.lowerBound..<end.upperBound, with: sanitizedBlock)
}
let normalizedConfig = configString.replacingOccurrences(of: "\r\n", with: "\n")
let sanitizedData = Data(normalizedConfig.utf8)
let str = String(decoding: ovpnConfiguration, as: UTF8.self)
let configuration = OpenVPNConfiguration()
configuration.fileContent = sanitizedData
if configString.contains("cloak") {
configuration.fileContent = ovpnConfiguration
if str.contains("cloak") {
configuration.setPTCloak()
}
@@ -115,8 +57,6 @@ extension PacketTunnelProvider {
evaluation = try ovpnAdapter?.apply(configuration: configuration)
} catch {
let nsError = error as NSError
ovpnLog(.error, title: "ApplyConfig", message: "domain=\(nsError.domain) code=\(nsError.code) info=\(nsError.userInfo)")
completionHandler(error)
return
}
@@ -133,7 +73,7 @@ extension PacketTunnelProvider {
startHandler = completionHandler
ovpnAdapter?.connect(using: packetFlow)
}
func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
guard let completionHandler = completionHandler else { return }
let bytesin = ovpnAdapter?.transportStatistics.bytesIn
@@ -268,11 +208,8 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
// Handle errors thrown by the OpenVPN library
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleError error: Error) {
let nsError = error as NSError
logOpenVPNError(nsError)
// Handle only fatal errors
guard let fatal = nsError.userInfo[OpenVPNAdapterErrorFatalKey] as? Bool,
guard let fatal = (error as NSError).userInfo[OpenVPNAdapterErrorFatalKey] as? Bool,
fatal == true else { return }
if vpnReachability.isTracking {
@@ -112,19 +112,9 @@ extension PacketTunnelProvider {
}
}
let lastHandshakeString = settingsDictionary["last_handshake_time_sec"]
let lastHandshake: Int64
if let lastHandshakeValue = lastHandshakeString, let handshakeValue = Int64(lastHandshakeValue) {
lastHandshake = handshakeValue
} else {
lastHandshake = -2 // Return an error if there is no value for `last_handshake_time_sec`
}
let response: [String: Any] = [
"rx_bytes": settingsDictionary["rx_bytes"] ?? "0",
"tx_bytes": settingsDictionary["tx_bytes"] ?? "0",
"last_handshake_time_sec": lastHandshake
"tx_bytes": settingsDictionary["tx_bytes"] ?? "0"
]
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
-17
View File
@@ -1,4 +1,3 @@
#if !MACOS_NE
#include "QRCodeReaderBase.h"
#import <UIKit/UIKit.h>
@@ -109,19 +108,3 @@ void QRCodeReader::startReading() {
void QRCodeReader::stopReading() {
[m_qrCodeReader stopReading];
}
#else
#include "QRCodeReaderBase.h"
QRCodeReader::QRCodeReader()
{
}
QRect QRCodeReader::cameraSize() {
return QRect();
}
void QRCodeReader::startReading() {}
void QRCodeReader::stopReading() {}
void QRCodeReader::setCameraSize(QRect) {}
#endif
+1 -2
View File
@@ -1,6 +1,5 @@
#if !MACOS_NE
#import <UIKit/UIKit.h>
#endif
@interface QIOSApplicationDelegate
@end
+2 -2
View File
@@ -5,7 +5,7 @@
@implementation QIOSApplicationDelegate (AmneziaVPNDelegate)
#if !MACOS_NE
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[application setMinimumBackgroundFetchInterval: UIApplicationBackgroundFetchIntervalMinimum];
@@ -57,5 +57,5 @@
}
return NO;
}
#endif
@end
@@ -1,13 +1,3 @@
#if MACOS_NE
public func toggleScreenshots(_ isEnabled: Bool) {
}
class ScreenProtection {
}
#else
import UIKit
public func toggleScreenshots(_ isEnabled: Bool) {
@@ -100,4 +90,3 @@ struct ProtectionPair {
textField.removeFromSuperview()
}
}
#endif
-1
View File
@@ -46,7 +46,6 @@ public:
void disconnectVpn();
void vpnStatusDidChange(void *pNotification);
void vpnConfigurationDidChange(void *pNotification);
void getBackendLogs(std::function<void(const QString &)> &&callback);
+8 -62
View File
@@ -27,51 +27,15 @@ const char* MessageKey::isOnDemand = "is-on-demand";
const char* MessageKey::SplitTunnelType = "SplitTunnelType";
const char* MessageKey::SplitTunnelSites = "SplitTunnelSites";
#if !MACOS_NE
static UIViewController* getViewController() {
UIApplication *application = [UIApplication sharedApplication];
if (@available(iOS 13.0, *)) {
for (UIScene *scene in application.connectedScenes) {
if (scene.activationState != UISceneActivationStateForegroundActive) {
continue;
}
if (![scene isKindOfClass:[UIWindowScene class]]) {
continue;
}
UIWindowScene *windowScene = (UIWindowScene *)scene;
for (UIWindow *window in windowScene.windows) {
if (window.isKeyWindow && window.rootViewController) {
return window.rootViewController;
}
}
for (UIWindow *window in windowScene.windows) {
if (!window.isHidden && window.rootViewController) {
return window.rootViewController;
}
}
}
}
for (UIWindow *window in application.windows) {
if (window.isKeyWindow && window.rootViewController) {
NSArray *windows = [[UIApplication sharedApplication]windows];
for (UIWindow *window in windows) {
if (window.isKeyWindow) {
return window.rootViewController;
}
}
for (UIWindow *window in application.windows) {
if (window.rootViewController) {
return window.rootViewController;
}
}
return nil;
}
#endif
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
switch (status) {
@@ -285,21 +249,6 @@ void IosController::checkStatus()
sendVpnExtensionMessage(message, [&](NSDictionary* response){
uint64_t txBytes = [response[@"tx_bytes"] intValue];
uint64_t rxBytes = [response[@"rx_bytes"] intValue];
uint64_t last_handshake_time_sec = 0;
#if !MACOS_NE
if (response[@"last_handshake_time_sec"] && ![response[@"last_handshake_time_sec"] isKindOfClass:[NSNull class]]) {
last_handshake_time_sec = [response[@"last_handshake_time_sec"] intValue];
} else {
qDebug() << "Key last_handshake_time_sec is missing or null";
}
if (last_handshake_time_sec < 0) {
disconnectVpn();
qDebug() << "Invalid handshake time, disconnecting VPN.";
}
#endif
emit bytesChanged(rxBytes - m_rxBytes, txBytes - m_txBytes);
m_rxBytes = rxBytes;
m_txBytes = txBytes;
@@ -854,14 +803,14 @@ bool IosController::shareText(const QStringList& filesToSend) {
NSURL *logFileUrl = [[NSURL alloc] initFileURLWithPath:filesToSend[i].toNSString()];
[sharingItems addObject:logFileUrl];
}
#if !MACOS_NE
UIViewController *qtController = getViewController();
if (!qtController) return;
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
#endif
__block bool isAccepted = false;
#if !MACOS_NE
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
isAccepted = completed;
emit finished();
@@ -874,7 +823,6 @@ bool IosController::shareText(const QStringList& filesToSend) {
popController.sourceRect = CGRectMake(100, 100, 100, 100);
}
#endif
QEventLoop wait;
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
wait.exec();
@@ -883,7 +831,6 @@ bool IosController::shareText(const QStringList& filesToSend) {
}
QString IosController::openFile() {
#if !MACOS_NE
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen];
DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init];
@@ -894,9 +841,8 @@ QString IosController::openFile() {
[qtController presentViewController:documentPicker animated:YES completion:nil];
#endif
__block QString filePath;
#if !MACOS_NE
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
if (path) {
filePath = QString::fromUtf8(path.UTF8String);
@@ -905,7 +851,7 @@ QString IosController::openFile() {
}
emit finished();
};
#endif
QEventLoop wait;
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
wait.exec();
@@ -1,11 +1,7 @@
#import <NetworkExtension/NetworkExtension.h>
#import <NetworkExtension/NETunnelProviderSession.h>
#import <Foundation/Foundation.h>
#if !MACOS_NE
#include <UIKit/UIKit.h>
#endif
#include <Security/Security.h>
class IosController;
@@ -21,10 +17,9 @@ class IosController;
@end
typedef void (^DocumentPickerClosedCallback)(NSString *path);
#if !MACOS_NE
@interface DocumentPickerDelegate : NSObject <UIDocumentPickerDelegate>
@property (nonatomic, copy) DocumentPickerClosedCallback documentPickerClosedCallback;
@end
#endif
@@ -26,8 +26,7 @@
@end
#if !MACOS_NE
@implementation DocumentPickerDelegate
@implementation DocumentPickerDelegate
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
for (NSURL *url in urls) {
@@ -43,5 +42,4 @@
}
}
@end
#endif
@end
@@ -6,8 +6,6 @@
#import <UserNotifications/UserNotifications.h>
#import <Foundation/Foundation.h>
#if !MACOS_NE
#import <UIKit/UIKit.h>
@interface IOSNotificationDelegate
@@ -89,86 +87,3 @@ void IOSNotificationHandler::notify(NotificationHandler::Message type, const QSt
}
}];
}
#else
// Removed the UIResponder and UIApplicationDelegate references as these are not available in macOS
@interface IOSNotificationDelegate
: NSObject <UNUserNotificationCenterDelegate> {
IOSNotificationHandler* m_iosNotificationHandler;
}
@end
@implementation IOSNotificationDelegate
- (id)initWithObject:(IOSNotificationHandler*)notification {
self = [super init]; // Removed `super init` as it refers to UIResponder, which is iOS specific
if (self) {
m_iosNotificationHandler = notification;
}
return self;
}
- (void)userNotificationCenter:(UNUserNotificationCenter*)center
willPresentNotification:(UNNotification*)notification
withCompletionHandler:
(void (^)(UNNotificationPresentationOptions options))completionHandler {
Q_UNUSED(center)
completionHandler(UNNotificationPresentationOptionList | UNNotificationPresentationOptionBanner);
}
- (void)userNotificationCenter:(UNUserNotificationCenter*)center
didReceiveNotificationResponse:(UNNotificationResponse*)response
withCompletionHandler:(void (^)())completionHandler {
Q_UNUSED(center)
Q_UNUSED(response)
completionHandler();
}
@end
IOSNotificationHandler::IOSNotificationHandler(QObject* parent) : NotificationHandler(parent) {
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert |
UNAuthorizationOptionBadge)
completionHandler:^(BOOL granted, NSError* _Nullable error) {
Q_UNUSED(granted);
if (!error) {
m_delegate = [[IOSNotificationDelegate alloc] initWithObject:this];
}
}];
}
IOSNotificationHandler::~IOSNotificationHandler() { }
void IOSNotificationHandler::notify(NotificationHandler::Message type, const QString& title,
const QString& message, int timerMsec) {
Q_UNUSED(type);
if (!m_delegate) {
return;
}
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
content.title = title.toNSString();
content.body = message.toNSString();
content.sound = [UNNotificationSound defaultSound];
int timerSec = timerMsec / 1000;
UNTimeIntervalNotificationTrigger* trigger =
[UNTimeIntervalNotificationTrigger triggerWithTimeInterval:timerSec repeats:NO];
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:@"amneziavpn"
content:content
trigger:trigger];
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = (id<UNUserNotificationCenterDelegate>)m_delegate;
[center addNotificationRequest:request
withCompletionHandler:^(NSError* _Nullable error) {
if (error) {
NSLog(@"Local Notification failed");
}
}];
}
#endif
@@ -30,6 +30,7 @@ Ikev2Protocol::Ikev2Protocol(const QJsonObject &configuration, QObject* parent)
Ikev2Protocol::~Ikev2Protocol()
{
qDebug() << "IpsecProtocol::~IpsecProtocol()";
disconnect_vpn();
Ikev2Protocol::stop();
}
@@ -37,7 +38,7 @@ void Ikev2Protocol::stop()
{
setConnectionState(Vpn::ConnectionState::Disconnecting);
{
if (!disconnect_vpn()){
if (! disconnect_vpn() ){
qDebug()<<"We don't disconnect";
setConnectionState(Vpn::ConnectionState::Error);
}
@@ -310,9 +311,7 @@ bool Ikev2Protocol::connect_to_vpn(const QString & vpn_name){
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Ikev2Protocol::disconnect_vpn(){
if ( hRasConn != nullptr ){
auto ret = RasHangUp(hRasConn);
qDebug() << "RasHangUp " << ret;
if (ret != ERROR_SUCCESS)
if ( RasHangUp(hRasConn) != ERROR_SUCCESS)
return false;
}
QThread::msleep(3000);
+2 -2
View File
@@ -192,7 +192,7 @@ namespace amnezia
constexpr char defaultPort[] = "51820";
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
constexpr char defaultMtu[] = "1280";
#else
constexpr char defaultMtu[] = "1376";
@@ -212,7 +212,7 @@ namespace amnezia
namespace awg
{
constexpr char defaultPort[] = "55424";
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
constexpr char defaultMtu[] = "1280";
#else
constexpr char defaultMtu[] = "1376";
+2 -2
View File
@@ -4,7 +4,7 @@
#include "core/errorstrings.h"
#include "vpnprotocol.h"
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) and !defined MACOS_NE || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
#include "openvpnovercloakprotocol.h"
#include "openvpnprotocol.h"
#include "shadowsocksvpnprotocol.h"
@@ -109,7 +109,7 @@ VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject &
#if defined(Q_OS_WINDOWS)
case DockerContainer::Ipsec: return new Ikev2Protocol(configuration);
#endif
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) and !defined MACOS_NE || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
case DockerContainer::OpenVpn: return new OpenVpnProtocol(configuration);
case DockerContainer::Cloak: return new OpenVpnOverCloakProtocol(configuration);
case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration);
-1
View File
@@ -169,7 +169,6 @@ void XrayProtocol::stop()
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
IpcClient::Interface()->disableKillSwitch();
IpcClient::Interface()->StartRoutingIpv6();
IpcClient::Interface()->restoreResolvers();
#endif
qDebug() << "XrayProtocol::stop()";
m_xrayProcess.disconnect();
+1 -9
View File
@@ -35,9 +35,6 @@
<file>images/controls/mail.svg</file>
<file>images/controls/map-pin.svg</file>
<file>images/controls/more-vertical.svg</file>
<file>images/controls/news.svg</file>
<file>images/controls/news-unread.svg</file>
<file>images/controls/unread-dot.svg</file>
<file>images/controls/plus.svg</file>
<file>images/controls/qr-code.svg</file>
<file>images/controls/radio-button-inner-circle-pressed.png</file>
@@ -52,7 +49,6 @@
<file>images/controls/server.svg</file>
<file>images/controls/settings-2.svg</file>
<file>images/controls/settings.svg</file>
<file>images/controls/settings-news.svg</file>
<file>images/controls/share-2.svg</file>
<file>images/controls/split-tunneling.svg</file>
<file>images/controls/tag.svg</file>
@@ -131,7 +127,7 @@
<file>ui/qml/Components/SelectLanguageDrawer.qml</file>
<file>ui/qml/Components/ServersListView.qml</file>
<file>ui/qml/Components/SettingsContainersListView.qml</file>
<file>ui/qml/Components/ShareConnectionDrawer.qml</file>
<file>ui/qml/Components/TransportProtoSelector.qml</file>
<file>ui/qml/Components/AddSitePanel.qml</file>
<file>ui/qml/Config/GlobalConfig.qml</file>
@@ -216,8 +212,6 @@
<file>ui/qml/Pages2/PageSettingsServerServices.qml</file>
<file>ui/qml/Pages2/PageSettingsServersList.qml</file>
<file>ui/qml/Pages2/PageSettingsSplitTunneling.qml</file>
<file>ui/qml/Pages2/PageSettingsNewsNotifications.qml</file>
<file>ui/qml/Pages2/PageSettingsNewsDetail.qml</file>
<file>ui/qml/Pages2/PageProtocolAwgClientSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml</file>
<file>ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml</file>
@@ -234,7 +228,6 @@
<file>ui/qml/Pages2/PageSetupWizardViewConfig.qml</file>
<file>ui/qml/Pages2/PageShare.qml</file>
<file>ui/qml/Pages2/PageShareFullAccess.qml</file>
<file>ui/qml/Pages2/PageShareConnection.qml</file>
<file>ui/qml/Pages2/PageStart.qml</file>
<file>ui/qml/Components/RenameServerDrawer.qml</file>
<file>ui/qml/Controls2/ListViewType.qml</file>
@@ -247,7 +240,6 @@
<file>ui/qml/Components/ApiPremV1SubListDrawer.qml</file>
<file>ui/qml/Components/OtpCodeDrawer.qml</file>
<file>ui/qml/Components/AwgTextField.qml</file>
<file>ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml</file>
</qresource>
<qresource prefix="/countriesFlags">
<file>images/flagKit/ZW.svg</file>
-519
View File
@@ -1,519 +0,0 @@
#!/bin/sh
LOG_DATE=$(date -u +'%Y%m%d-%H%M%S')
SCRIPT_DIR=$(dirname "$0")
LOG_FILE="${SCRIPT_DIR}/server-diagnostics-${LOG_DATE}.log"
# Logging function (sh compatible)
log_and_display() {
if [ "$1" = "-n" ]; then
shift
printf "%s" "$*" | tee -a "$LOG_FILE"
else
echo "$1" | tee -a "$LOG_FILE"
fi
}
# Redirect stderr to stdout for logging
exec 2>&1
header() {
log_and_display ""
log_and_display "=== $1 ==="
}
# Pause for cancellation
log_and_display ""
log_and_display "VPN Server Diagnostics will start in 9s. Press Ctrl+C to cancel."
sleep 9
log_and_display ""
header "STARTING VPN SERVER DIAGNOSTICS"
log_and_display ""
# ------------------------------------------------------------------------------
# 1. Basic system information
# ------------------------------------------------------------------------------
header "System Information"
# Uptime
UPTIME_STR=$(awk '{printf "%d:%02d:%02d", int($1/3600), int(($1%3600)/60), int($1%60)}' /proc/uptime 2>/dev/null || echo "unknown")
log_and_display "Uptime (H:M:S): $UPTIME_STR"
# Date/time UTC
DATE_UTC=$(date -u +'%d %b %Y|%T' 2>/dev/null || echo "unknown")
log_and_display "Date|Time (UTC): $DATE_UTC"
# Init system (PID 1)
INIT_NAME=$(cat /proc/1/status 2>/dev/null | head -1 | awk '{print $2}' 2>/dev/null || echo "unknown")
log_and_display "Init system (PID 1): $INIT_NAME"
# Locale
if echo "$LANG" | grep -E '^(en_US.UTF-8|C.UTF-8|C)$' >/dev/null 2>&1; then
log_and_display "Locale: $LANG"
else
log_and_display "Locale: $LANG (not en_US.UTF-8, C.UTF-8 or C)"
fi
# ------------------------------------------------------------------------------
# 2. Package manager detection
# ------------------------------------------------------------------------------
header "Package Manager Information"
if command -v apt-get >/dev/null 2>&1; then
log_and_display "Package Manager: APT"
PM="apt-get"
PM_VER_OPT="--version"
DOCKER_PKG="docker.io"
elif command -v dnf >/dev/null 2>&1; then
log_and_display "Package Manager: DNF"
PM="dnf"
PM_VER_OPT="--version"
DOCKER_PKG="docker"
elif command -v yum >/dev/null 2>&1; then
log_and_display "Package Manager: YUM"
PM="yum"
PM_VER_OPT="--version"
DOCKER_PKG="docker"
elif command -v zypper >/dev/null 2>&1; then
log_and_display "Package Manager: ZYPPER"
PM="zypper"
PM_VER_OPT="--version"
DOCKER_PKG="docker"
elif command -v pacman >/dev/null 2>&1; then
log_and_display "Package Manager: PACMAN"
PM="pacman"
PM_VER_OPT="--version"
DOCKER_PKG="docker"
elif command -v opkg >/dev/null 2>&1; then
log_and_display "Package Manager: OPKG - Not supported on this platform"
PM="opkg"
PM_VER_OPT="--version"
DOCKER_PKG="docker"
else
log_and_display "Package Manager: Unknown"
# fallback
PM="uname"
PM_VER_OPT="-a"
DOCKER_PKG="docker"
fi
# Check package versions
log_and_display ""
log_and_display "Package versions:"
# Check sudo
if [ "$PM" = "apt-get" ]; then
sudo_version=$(dpkg -s "sudo" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
elif [ "$PM" = "dnf" ] || [ "$PM" = "yum" ] || [ "$PM" = "zypper" ]; then
sudo_version=$(rpm -q "sudo" 2>/dev/null || echo "not installed")
elif [ "$PM" = "pacman" ]; then
sudo_version=$(pacman -Q "sudo" 2>/dev/null || echo "not installed")
elif [ "$PM" = "opkg" ]; then
sudo_version=$(opkg info "sudo" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
else
sudo_version="unknown"
fi
log_and_display " sudo: $sudo_version"
# Check Docker package
if [ "$PM" = "apt-get" ]; then
docker_pkg_version=$(dpkg -s "$DOCKER_PKG" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
elif [ "$PM" = "dnf" ] || [ "$PM" = "yum" ] || [ "$PM" = "zypper" ]; then
docker_pkg_version=$(rpm -q "$DOCKER_PKG" 2>/dev/null || echo "not installed")
elif [ "$PM" = "pacman" ]; then
docker_pkg_version=$(pacman -Q "$DOCKER_PKG" 2>/dev/null || echo "not installed")
elif [ "$PM" = "opkg" ]; then
docker_pkg_version=$(opkg info "$DOCKER_PKG" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
else
docker_pkg_version="unknown"
fi
log_and_display " $DOCKER_PKG: $docker_pkg_version"
# Check lsof
if [ "$PM" = "apt-get" ]; then
lsof_version=$(dpkg -s "lsof" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
elif [ "$PM" = "dnf" ] || [ "$PM" = "yum" ] || [ "$PM" = "zypper" ]; then
lsof_version=$(rpm -q "lsof" 2>/dev/null || echo "not installed")
elif [ "$PM" = "pacman" ]; then
lsof_version=$(pacman -Q "lsof" 2>/dev/null || echo "not installed")
elif [ "$PM" = "opkg" ]; then
lsof_version=$(opkg info "lsof" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
else
lsof_version="unknown"
fi
log_and_display " lsof: $lsof_version"
# ------------------------------------------------------------------------------
# 3. Additional system information (hostnamectl / /proc/version)
# ------------------------------------------------------------------------------
header "OS / Kernel Information"
if command -v hostnamectl >/dev/null 2>&1; then
hostnamectl 2>/dev/null | grep -E 'Operating System:|Virtualization:|Kernel:|Architecture:' | sed 's/^[ \t]*//;s/:/: /' | while read line; do
log_and_display " $line"
done
else
log_and_display "Operating System: $(cat /proc/version 2>/dev/null || echo 'unknown')"
fi
# CPU threads
CPU_THREADS=$(nproc 2>/dev/null || grep -c "^processor" /proc/cpuinfo 2>/dev/null || echo "unknown")
log_and_display " CPU threads: $CPU_THREADS"
# ------------------------------------------------------------------------------
# 4. Memory (RAM) check
# ------------------------------------------------------------------------------
header "Memory Information"
if command -v free >/dev/null 2>&1; then
# Remove extra spaces in header
free -h 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error getting memory info"
elif command -v vmstat >/dev/null 2>&1; then
vmstat -S M -s 2>/dev/null | grep -iE 'total memory|total swap' | sed 's/ *//' | tee -a "$LOG_FILE" || log_and_display " Error getting memory info"
else
grep -iE 'MemTotal|SwapTotal' /proc/meminfo 2>/dev/null | sed 's/ \+/ /' | tee -a "$LOG_FILE" || log_and_display " Error getting memory info"
fi
if command -v free >/dev/null 2>&1; then
log_and_display ""
log_and_display "Detailed Memory Info:"
free -h 2>/dev/null | awk 'NR==2{printf " Used: %s / %s (%.1f%%)\n", $3, $2, $3/$2*100}' 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error calculating memory usage"
free -h 2>/dev/null | awk 'NR==3{printf " Swap: %s / %s (%.1f%%)\n", $3, $2, $2>0 ? $3/$2*100 : 0}' 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error calculating swap usage"
fi
# Disk usage
header "Disk Usage"
df -h 2>/dev/null | awk '
BEGIN {print " Filesystem Size Used Avail Use% Mounted"}
NR>1 {printf " %-10s %5s %5s %5s %4s %s\n", $1, $2, $3, $4, $5, $6}' | tee -a "$LOG_FILE" || log_and_display " Error getting disk usage"
# ------------------------------------------------------------------------------
# 5. Current user and sudo check
# ------------------------------------------------------------------------------
header "User Check"
CUR_USER=$(whoami 2>/dev/null || echo ~ | sed 's/.*\///')
USER_GROUP=$(groups "$CUR_USER" 2>/dev/null || echo "")
USER_GOOD=0
log_and_display -n "Current user: $CUR_USER => "
if [ "$CUR_USER" = "root" ]; then
log_and_display "passed.. (is root)"
USER_GOOD="r" # root
else
if echo "$USER_GROUP" | grep -qE '(^|[[:space:]])sudo($|[[:space:]])'; then
log_and_display "passed.. (in sudo group)"
USER_GOOD=1
elif echo "$USER_GROUP" | grep -qE '(^|[[:space:]])wheel($|[[:space:]])'; then
log_and_display "passed.. (in wheel group)"
USER_GOOD=1
elif echo "$USER_GROUP" | grep -qE '(^|[[:space:]])docker($|[[:space:]])'; then
log_and_display "failed.. (only in docker group)"
USER_GOOD="d"
else
log_and_display "failed.. (not a member of the sudo or wheel groups)"
USER_GOOD=0
fi
fi
# Check if password is required for sudo
if [ "$USER_GOOD" = "0" ] || [ "$USER_GOOD" = "d" ]; then
log_and_display -n "Passwd request: "
log_and_display "check skipped (not sudoer)"
else
if command -v sudo >/dev/null 2>&1; then
# Try sudo without password - more thorough check
PASSWD_REQUEST=$(sudo -K 2>&1 && sudo -nu $CUR_USER $PM $PM_VER_OPT 2>&1 >/dev/null && sudo -n $PM $PM_VER_OPT 2>&1 >/dev/null)
if [ -n "$PASSWD_REQUEST" ]; then
USER_GOOD=0
log_and_display -n "Passwd request: "
log_and_display "failed.. ($PASSWD_REQUEST)" \
| sed "s/$CUR_USER/User/g;s/$(hostname 2>/dev/null || echo 'Server')/Server/g;s/ user / /g"
else
log_and_display -n "Passwd request: "
log_and_display "passed.. (not required)"
fi
else
if [ "$USER_GOOD" = "r" ]; then
log_and_display -n "Passwd request: "
log_and_display "check skipped (sudo not installed, but root user)"
else
log_and_display "Warning! The sudo package must be pre-installed!"
USER_GOOD=0
fi
fi
fi
# Home directory check
log_and_display -n "Home dir: "
if cd ~ 2>/dev/null; then
log_and_display "passed.. (accessible)"
else
log_and_display "failed.. (not accessible)"
fi
log_and_display "Default shell: $SHELL"
# ------------------------------------------------------------------------------
# 6. Important components check (sudo, lsof, fuser, apparmor)
# ------------------------------------------------------------------------------
header "Component Checks"
log_and_display -n " sudo: "
if command -v sudo >/dev/null 2>&1; then
log_and_display "passed.. (installed)"
else
log_and_display "not installed"
fi
log_and_display -n " lsof: "
if command -v lsof >/dev/null 2>&1; then
log_and_display "passed.. (installed)"
else
log_and_display "not installed"
fi
log_and_display -n " fuser: "
if command -v fuser >/dev/null 2>&1; then
log_and_display "passed.. (installed)"
else
log_and_display "psmisc not installed"
fi
log_and_display -n "apparmor: "
AA_ENABLED=$(cat /sys/module/apparmor/parameters/enabled 2>/dev/null || echo "N")
if [ "$AA_ENABLED" = "Y" ]; then
if command -v apparmor_parser >/dev/null 2>&1; then
log_and_display "passed.. (used)"
else
log_and_display "failed.. (installation required)"
fi
else
if command -v apparmor_parser >/dev/null 2>&1; then
log_and_display "passed.. (not used)"
else
log_and_display "passed.. (not required)"
fi
fi
# ------------------------------------------------------------------------------
# 7. SELinux check
# ------------------------------------------------------------------------------
header "SELinux Check"
if command -v getenforce >/dev/null 2>&1; then
SELINUX_STATUS=$(getenforce 2>/dev/null || echo "unknown")
if [ "$SELINUX_STATUS" = "Enforcing" ]; then
log_and_display "SELinux status: $SELINUX_STATUS (strict mode)"
elif [ "$SELINUX_STATUS" = "Permissive" ]; then
log_and_display "SELinux status: $SELINUX_STATUS (permissive mode)"
else
log_and_display "SELinux status: $SELINUX_STATUS (disabled)"
fi
else
log_and_display "SELinux: not found (or not applicable)"
fi
# ------------------------------------------------------------------------------
# 8. Docker + Docker/Podman service check
# ------------------------------------------------------------------------------
header "Docker / Podman Status"
CHECK_CONTAINERS=0
if ! command -v docker >/dev/null 2>&1; then
log_and_display "Docker: $DOCKER_PKG not installed"
else
# If user is in sudoers, use sudo without password
if [ "$USER_GOOD" = "1" ]; then
SUD="sudo -n"
elif [ "$USER_GOOD" = "r" ]; then
SUD="" # root
else
SUD=""
fi
DOCKER_VERSION=$($SUD docker -v 2>/dev/null || echo 'docker -v error')
log_and_display "Installed: $DOCKER_VERSION"
# Check for podman
if echo "$DOCKER_VERSION" | grep -qi "podman"; then
log_and_display " WARNING: Podman detected - not supported at the moment!"
log_and_display " Podman (podman-docker) is not supported and is installed by mistake"
docker_service="podman.socket"
else
docker_service="docker.service"
fi
log_and_display " service: $docker_service"
# Check status
if command -v systemctl >/dev/null 2>&1; then
docker_status=$(systemctl is-active "$docker_service" 2>/dev/null || echo "unknown")
docker_loading=$(systemctl is-enabled "$docker_service" 2>/dev/null || echo "unknown")
else
docker_status="unknown (systemctl not found)"
docker_loading="unknown"
fi
if [ "$docker_status" = "active" ]; then
log_and_display " status: passed.. ($docker_status)"
CHECK_CONTAINERS=1
else
log_and_display " status: incorrect.. ($docker_status)"
CHECK_CONTAINERS=0
fi
if [ "$docker_loading" = "enabled" ]; then
log_and_display " loading: good (startup $docker_loading)"
else
log_and_display " loading: bad (startup $docker_loading)"
fi
fi
# ------------------------------------------------------------------------------
# 9. Docker pull test + container check with improved Docker Hub verification
# ------------------------------------------------------------------------------
header "Docker Hub: pull hello-world test"
if [ "$CHECK_CONTAINERS" = "1" ] && [ "$USER_GOOD" != "0" ]; then
# First check Docker Hub availability
log_and_display "Checking Docker Hub connectivity..."
# Try to execute docker pull with timeout
if timeout 30 $SUD docker pull docker.io/library/hello-world >/dev/null 2>&1; then
log_and_display "Docker Hub: available"
# Start container for testing
if $SUD docker run --rm docker.io/library/hello-world >/dev/null 2>&1; then
log_and_display "Hello-world container: successfully started and completed"
else
log_and_display "Hello-world container: startup error"
fi
else
log_and_display "Docker Hub: unavailable or blocked (possibly exceeded download limit)"
log_and_display "Docker Hub has download limits, try again later"
fi
log_and_display ""
total_cont=$($SUD docker ps -aq 2>/dev/null | wc -l || echo "0")
active_cont=$($SUD docker ps -q 2>/dev/null | wc -l || echo "0")
amnezia_cont=$($SUD docker ps -a 2>/dev/null | grep -c amnezia || echo "0")
log_and_display "Containers check: Total $total_cont / Active $active_cont / Amnezia $amnezia_cont"
$SUD docker ps -a --format "{{.Names}} ({{.Image}}) ({{.Status}}) ({{.Ports}})" 2>/dev/null | grep amnezia || true
# Peers check
if $SUD docker ps 2>/dev/null | grep -qE '\<(amnezia-awg|amnezia-wireguard)\>'; then
log_and_display ""
log_and_display "Peers check (beta):"
if $SUD docker ps 2>/dev/null | grep -q amnezia-awg; then
AMNEZIA_WG_CONTAINER=$($SUD docker ps 2>/dev/null | grep amnezia-awg | awk '{print $1}' | head -1)
if [ -n "$AMNEZIA_WG_CONTAINER" ]; then
WG_PEERS=$($SUD docker exec -it "$AMNEZIA_WG_CONTAINER" wg show 2>/dev/null | grep -c 'peer' || echo "0")
log_and_display "AmneziaWG peers: $WG_PEERS"
fi
fi
if $SUD docker ps 2>/dev/null | grep -q amnezia-wireguard; then
WIREGUARD_CONTAINER=$($SUD docker ps 2>/dev/null | grep amnezia-wireguard | awk '{print $1}' | head -1)
if [ -n "$WIREGUARD_CONTAINER" ]; then
WG_PEERS=$($SUD docker exec -it "$WIREGUARD_CONTAINER" wg show 2>/dev/null | grep -c 'peer' || echo "0")
log_and_display "WireGuard peers: $WG_PEERS"
fi
fi
fi
else
log_and_display "skipped.."
fi
# ------------------------------------------------------------------------------
# 10. Additional improvements
# ------------------------------------------------------------------------------
#
# 10.1. CPU and memory load check (Load average, top processes)
#
header "CPU & Memory usage (top)"
# Load average (last 1,5,15 minutes)
LOAD_AVG=$(uptime 2>/dev/null | awk -F'load average:' '{print $2}' || echo "unknown")
log_and_display "Load average: $LOAD_AVG"
log_and_display ""
log_and_display "Top 5 processes by CPU:"
ps aux 2>/dev/null | sort -k3 -nr | head -n 6 | awk '{printf "%s %s %s %s %s\n", $1,$2,$3"%",$4"%",$11}' | column -t 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error getting CPU processes"
log_and_display ""
log_and_display "Top 5 processes by MEM:"
ps aux 2>/dev/null | sort -k4 -nr | head -n 6 | awk '{printf "%s %s %s %s %s\n", $1,$2,$3"%",$4"%",$11}' | column -t 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error getting MEM processes"
# 10.2. System logs check (latest critical messages)
header "Last 10 critical/error messages (journalctl)"
if command -v journalctl >/dev/null 2>&1; then
journalctl -p 3 -n 10 --no-pager 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error getting system logs"
else
log_and_display "journalctl not found (non-systemd system?)"
fi
# 10.3. System package versions check (examples)
# Open ports check
header "Network Ports Check"
if command -v netstat >/dev/null 2>&1; then
log_and_display "Listening ports:"
netstat -tlnp 2>/dev/null | grep LISTEN | head -10 | while read line; do
log_and_display " $line"
done
elif command -v ss >/dev/null 2>&1; then
log_and_display "Listening ports:"
ss -tlnp 2>/dev/null | head -10 | while read line; do
log_and_display " $line"
done
else
log_and_display "netstat/ss not found"
fi
# SSH check
header "SSH Service Check"
if command -v systemctl >/dev/null 2>&1; then
ssh_status=$(systemctl is-active ssh 2>/dev/null || systemctl is-active sshd 2>/dev/null || echo "not found")
if [ "$ssh_status" = "active" ]; then
log_and_display "SSH service: $ssh_status"
else
log_and_display "SSH service: $ssh_status"
fi
else
log_and_display "systemctl not found"
fi
# Time check
header "Time Synchronization"
if command -v timedatectl >/dev/null 2>&1; then
timedatectl status 2>/dev/null | grep -E "System clock|NTP service" | while read line; do
log_and_display " $line"
done
else
log_and_display " System time: $(date 2>/dev/null || echo 'unknown')"
fi
# Kernel check
header "Kernel Information"
log_and_display "Kernel version: $(uname -r 2>/dev/null || echo 'unknown')"
log_and_display "Kernel architecture: $(uname -m 2>/dev/null || echo 'unknown')"
if [ -f /proc/cmdline ]; then
log_and_display "Kernel parameters:"
cat /proc/cmdline 2>/dev/null | tr ' ' '\n' | head -5 | while read param; do
log_and_display " $param"
done
fi
# ------------------------------------------------------------------------------
# Completion
# ------------------------------------------------------------------------------
log_and_display ""
header "FINISH"
log_and_display ""
log_and_display "Diagnostics completed. Log saved to: $LOG_FILE"
log_and_display ""
# Variable cleanup
pm="" && opt="" && docker_pkg="" && CUR_USER="" && USER_GOOD="" && USER_GROUP="" && PASSWD_REQUEST="" && CHECK_CONTAINERS="" && SUD="" && docker_service="" && docker_status="" && docker_loading=""
+1 -1
View File
@@ -1,7 +1,7 @@
FROM alpine:3.15
LABEL maintainer="AmneziaVPN"
ARG XRAY_RELEASE="v25.8.3"
ARG XRAY_RELEASE="v1.8.6"
RUN apk add --no-cache curl unzip bash openssl netcat-openbsd dumb-init rng-tools xz
RUN apk --update upgrade --no-cache
+2 -12
View File
@@ -541,12 +541,12 @@ QString Settings::getGatewayEndpoint()
bool Settings::isDevGatewayEnv()
{
return value("Conf/devGatewayEnv", false).toBool();
return m_isDevGatewayEnv;
}
void Settings::toggleDevGatewayEnv(bool enabled)
{
setValue("Conf/devGatewayEnv", enabled);
m_isDevGatewayEnv = enabled;
}
bool Settings::isHomeAdLabelVisible()
@@ -578,13 +578,3 @@ void Settings::setAllowedDnsServers(const QStringList &servers)
{
setValue("Conf/allowedDnsServers", servers);
}
QStringList Settings::readNewsIds() const
{
return value("News/readIds").toStringList();
}
void Settings::setReadNewsIds(const QStringList &ids)
{
setValue("News/readIds", ids);
}
+2 -4
View File
@@ -174,7 +174,7 @@ public:
QLocale getAppLanguage()
{
QString localeStr = m_settings.value("Conf/appLanguage", QLocale::system().name()).toString();
QString localeStr = m_settings.value("Conf/appLanguage").toString();
return QLocale(localeStr);
};
void setAppLanguage(QLocale locale)
@@ -236,9 +236,6 @@ public:
QStringList allowedDnsServers() const;
void setAllowedDnsServers(const QStringList &servers);
QStringList readNewsIds() const;
void setReadNewsIds(const QStringList &ids);
signals:
void saveLogsChanged(bool enabled);
void screenshotsEnabledChanged(bool enabled);
@@ -254,6 +251,7 @@ private:
mutable SecureQSettings m_settings;
QString m_gatewayEndpoint;
bool m_isDevGatewayEnv = false;
};
#endif // SETTINGS_H
File diff suppressed because it is too large Load Diff
@@ -29,7 +29,6 @@ namespace
constexpr char uuid[] = "installation_uuid";
constexpr char osVersion[] = "os_version";
constexpr char appVersion[] = "app_version";
constexpr char appLanguage[] = "app_language";
constexpr char userCountryCode[] = "user_country_code";
constexpr char serverCountryCode[] = "server_country_code";
@@ -44,11 +43,6 @@ namespace
constexpr char authData[] = "auth_data";
constexpr char config[] = "config";
constexpr char subscription[] = "subscription";
constexpr char endDate[] = "end_date";
constexpr char isConnectEvent[] = "is_connect_event";
}
struct ProtocolData
@@ -169,7 +163,7 @@ namespace
auto clientProtocolConfig =
QJsonDocument::fromJson(serverProtocolConfig.value(config_key::last_config).toString().toUtf8()).object();
// TODO looks like this block can be removed after v1 configs EOL
//TODO looks like this block can be removed after v1 configs EOL
serverProtocolConfig[config_key::junkPacketCount] = clientProtocolConfig.value(config_key::junkPacketCount);
serverProtocolConfig[config_key::junkPacketMinSize] = clientProtocolConfig.value(config_key::junkPacketMinSize);
@@ -229,19 +223,6 @@ namespace
return ErrorCode::NoError;
}
bool isSubscriptionExpired(const QJsonObject &apiConfig)
{
auto subscription = apiConfig.value(configKey::subscription).toObject();
if (subscription.isEmpty()) {
return false;
}
auto subscriptionEndDate = subscription.value(configKey::endDate).toString();
if (apiUtils::isSubscriptionExpired(subscriptionEndDate)) {
return true;
}
return false;
}
}
ApiConfigsController::ApiConfigsController(const QSharedPointer<ServersModel> &serversModel,
@@ -251,23 +232,6 @@ ApiConfigsController::ApiConfigsController(const QSharedPointer<ServersModel> &s
{
}
bool ApiConfigsController::exportVpnKey(const QString &fileName)
{
if (fileName.isEmpty()) {
emit errorOccurred(ErrorCode::PermissionsError);
return false;
}
prepareVpnKeyExport();
if (m_vpnKey.isEmpty()) {
emit errorOccurred(ErrorCode::ApiConfigEmptyError);
return false;
}
SystemController::saveFile(fileName, m_vpnKey);
return true;
}
bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode, const QString &fileName)
{
if (fileName.isEmpty()) {
@@ -278,11 +242,6 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
if (isSubscriptionExpired(apiConfigObject)) {
emit errorOccurred(ErrorCode::ApiSubscriptionExpiredError);
return false;
}
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
QString(APP_VERSION),
m_settings->getInstallationUuid(true),
@@ -318,11 +277,6 @@ bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
if (isSubscriptionExpired(apiConfigObject)) {
emit errorOccurred(ErrorCode::ApiSubscriptionExpiredError);
return false;
}
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
QString(APP_VERSION),
m_settings->getInstallationUuid(true),
@@ -349,13 +303,6 @@ void ApiConfigsController::prepareVpnKeyExport()
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
auto vpnKey = apiConfigObject.value(apiDefs::key::vpnKey).toString();
if (vpnKey.isEmpty()) {
vpnKey = apiUtils::getPremiumV2VpnKey(serverConfigObject);
apiConfigObject.insert(apiDefs::key::vpnKey, vpnKey);
serverConfigObject.insert(configKey::apiConfig, apiConfigObject);
m_serversModel->editServer(serverConfigObject, m_serversModel->getProcessedServerIndex());
}
m_vpnKey = vpnKey;
vpnKey.replace("vpn://", "");
@@ -375,7 +322,6 @@ bool ApiConfigsController::fillAvailableServices()
{
QJsonObject apiPayload;
apiPayload[configKey::osVersion] = QSysInfo::productType();
apiPayload[configKey::appLanguage] = m_settings->getAppLanguage().name().split("_").first();
QByteArray responseBody;
ErrorCode errorCode = executeRequest(QString("%1v1/services"), apiPayload, responseBody);
@@ -450,11 +396,6 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
if (isSubscriptionExpired(apiConfig)) {
emit errorOccurred(ErrorCode::ApiSubscriptionExpiredError);
return false;
}
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
QString(APP_VERSION),
m_settings->getInstallationUuid(true),
@@ -469,10 +410,6 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
appendProtocolDataToApiPayload(gatewayRequestData.serviceProtocol, protocolData, apiPayload);
if (newCountryCode.isEmpty() && newCountryName.isEmpty() && !reloadServiceConfig) {
apiPayload.insert(configKey::isConnectEvent, true);
}
QByteArray responseBody;
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody);
@@ -492,7 +429,6 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
newServerConfig.insert(configKey::apiConfig, newApiConfig);
newServerConfig.insert(configKey::authData, gatewayRequestData.authData);
newServerConfig.insert(config_key::crc, serverConfig.value(config_key::crc));
if (serverConfig.value(config_key::nameOverriddenByUser).toBool()) {
newServerConfig.insert(config_key::name, serverConfig.value(config_key::name));
@@ -556,7 +492,7 @@ bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex)
}
}
bool ApiConfigsController::deactivateDevice(const bool isRemoveEvent)
bool ApiConfigsController::deactivateDevice()
{
auto serverIndex = m_serversModel->getProcessedServerIndex();
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
@@ -566,15 +502,6 @@ bool ApiConfigsController::deactivateDevice(const bool isRemoveEvent)
return true;
}
if (isSubscriptionExpired(apiConfigObject)) {
if (isRemoveEvent) {
return true;
} else {
emit errorOccurred(ErrorCode::ApiSubscriptionExpiredError);
return false;
}
}
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
QString(APP_VERSION),
m_settings->getInstallationUuid(true),
@@ -609,11 +536,6 @@ bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const Q
return true;
}
if (isSubscriptionExpired(apiConfigObject)) {
emit errorOccurred(ErrorCode::ApiSubscriptionExpiredError);
return false;
}
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
QString(APP_VERSION),
uuid,
@@ -21,7 +21,7 @@ public:
public slots:
bool exportNativeConfig(const QString &serverCountryCode, const QString &fileName);
bool revokeNativeConfig(const QString &serverCountryCode);
bool exportVpnKey(const QString &fileName);
// bool exportVpnKey(const QString &fileName);
void prepareVpnKeyExport();
void copyVpnKeyToClipboard();
@@ -30,7 +30,7 @@ public slots:
bool updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
bool reloadServiceConfig = false);
bool updateServiceFromTelegram(const int serverIndex);
bool deactivateDevice(const bool isRemoveEvent);
bool deactivateDevice();
bool deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode);
bool isConfigValid();
@@ -1,65 +0,0 @@
#include "apiNewsController.h"
#include "core/api/apiUtils.h"
#include <QJsonDocument>
#include <QJsonObject>
namespace
{
namespace configKey
{
constexpr char userCountryCode[] = "user_country_code";
constexpr char serviceType[] = "service_type";
}
}
ApiNewsController::ApiNewsController(const QSharedPointer<NewsModel> &newsModel, const std::shared_ptr<Settings> &settings,
const QSharedPointer<ServersModel> &serversModel, QObject *parent)
: QObject(parent), m_newsModel(newsModel), m_settings(settings), m_serversModel(serversModel)
{
}
void ApiNewsController::fetchNews()
{
if (m_serversModel.isNull()) {
qWarning() << "ServersModel is null, skip fetchNews";
return;
}
const auto stacks = m_serversModel->gatewayStacks();
if (stacks.isEmpty()) {
qDebug() << "No Gateway stacks, skip fetchNews";
return;
}
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
QByteArray responseBody;
QJsonObject payload;
payload.insert("locale", m_settings->getAppLanguage().name().split("_").first());
const QJsonObject stacksJson = stacks.toJson();
if (stacksJson.contains(configKey::userCountryCode)) {
payload.insert(configKey::userCountryCode, stacksJson.value(configKey::userCountryCode));
}
if (stacksJson.contains(configKey::serviceType)) {
payload.insert(configKey::serviceType, stacksJson.value(configKey::serviceType));
}
ErrorCode errorCode = gatewayController.post(QString("%1v1/news"), payload, responseBody);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return;
}
QJsonDocument doc = QJsonDocument::fromJson(responseBody);
QJsonArray newsArray;
if (doc.isArray()) {
newsArray = doc.array();
} else if (doc.isObject()) {
QJsonObject obj = doc.object();
if (obj.value("news").isArray()) {
newsArray = obj.value("news").toArray();
}
}
m_newsModel->updateModel(newsArray);
}
@@ -1,33 +0,0 @@
#ifndef APINEWSCONTROLLER_H
#define APINEWSCONTROLLER_H
#include <QJsonArray>
#include <QObject>
#include <QSharedPointer>
#include <memory>
#include "core/api/apiDefs.h"
#include "core/controllers/gatewayController.h"
#include "settings.h"
#include "ui/models/newsModel.h"
#include "ui/models/servers_model.h"
class ApiNewsController : public QObject
{
Q_OBJECT
public:
explicit ApiNewsController(const QSharedPointer<NewsModel> &newsModel, const std::shared_ptr<Settings> &settings,
const QSharedPointer<ServersModel> &serversModel, QObject *parent = nullptr);
Q_INVOKABLE void fetchNews();
signals:
void errorOccurred(ErrorCode errorCode);
private:
QSharedPointer<NewsModel> m_newsModel;
std::shared_ptr<Settings> m_settings;
QSharedPointer<ServersModel> m_serversModel;
};
#endif // APINEWSCONTROLLER_H

Some files were not shown because too many files have changed in this diff Show More