Compare commits
232 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6caf958bb3 | |||
| 70e6a3d303 | |||
| cc89939d05 | |||
| 7d4a01c757 | |||
| e2d61cb518 | |||
| fb1a9c9867 | |||
| 776ae04cbe | |||
| 2447ab4305 | |||
| 52124b15e8 | |||
| b1e9e8677b | |||
| 617e772cc1 | |||
| 5bfc581ad2 | |||
| 2664a52007 | |||
| 4e6c1094f3 | |||
| a6134ca10f | |||
| 3d999a503c | |||
| 4e3955b39d | |||
| 784ae0da53 | |||
| ed1afa7549 | |||
| b2072c06b7 | |||
| 762018883f | |||
| 0322c01c0e | |||
| 16ccfb8714 | |||
| 058f8b544e | |||
| 37024eb91d | |||
| ee99565b63 | |||
| 1a8c08799f | |||
| 8b08a5bee0 | |||
| 51497d87e0 | |||
| 7ede1a8d83 | |||
| b4df5c076e | |||
| 52400252dd | |||
| 49923c4214 | |||
| 81b77c9688 | |||
| af23d9fd14 | |||
| e7aead292c | |||
| fd2678ce2f | |||
| a6d660e708 | |||
| de35a26285 | |||
| 18bb045e9a | |||
| 6fa9994366 | |||
| 665f2412f1 | |||
| 395099aa40 | |||
| 97a72a9ee2 | |||
| 3414202b7b | |||
| 52e5453d56 | |||
| dd039a612f | |||
| f5ab034aeb | |||
| 893ec2d61c | |||
| ff41b26e94 | |||
| d4d6fbab88 | |||
| e38fe871b2 | |||
| 152d7bc3b3 | |||
| 9e7cf3ccd9 | |||
| f7370a0280 | |||
| 814c574f26 | |||
| fd9f9ee178 | |||
| 29e8f8f5fb | |||
| 279692afea | |||
| fd09321f8e | |||
| ad236baa86 | |||
| 8965b1fbba | |||
| 9b32411659 | |||
| 32dda9b904 | |||
| c0cb5b96bf | |||
| ff60030ffb | |||
| f40bf2d9ba | |||
| 9eebee3ce3 | |||
| 8a3bdf136b | |||
| f62076d3fd | |||
| 92f4d6b392 | |||
| 96ffd7e147 | |||
| 07c38e9b6c | |||
| c0aca97083 | |||
| 2fd25f53cc | |||
| 2b3383a163 | |||
| 421a27ceae | |||
| 10022451b4 | |||
| 3c8d923299 | |||
| 154044e32a | |||
| bfc8c10f3d | |||
| d93b5a7b5c | |||
| 4ae608ed93 | |||
| e2aef1fc1d | |||
| 16cadfeae8 | |||
| 3c9b42b9f7 | |||
| 9c0f27edb4 | |||
| f81ee1b267 | |||
| a964d955f4 | |||
| f11c65c393 | |||
| f8e5e9f675 | |||
| 72eb36f5b3 | |||
| 97c0fe1ece | |||
| 844b552bf3 | |||
| 6bb85deca6 | |||
| 551f7616f0 | |||
| 285c508329 | |||
| f751657903 | |||
| 89096554e8 | |||
| 4bf6cce4ba | |||
| 85eae0b74a | |||
| 0a5657738e | |||
| 3aa0adbf39 | |||
| 7dc21ce8a7 | |||
| 6a6b200861 | |||
| 1c7868312d | |||
| 90d1c16783 | |||
| 3cfca046ba | |||
| 85414eb65f | |||
| fdff57da7c | |||
| 7c223feef5 | |||
| 3740cb2c30 | |||
| b5dd48ad7b | |||
| e46025739a | |||
| 66a3538d05 | |||
| e1fa24c251 | |||
| a76e22c021 | |||
| c166327835 | |||
| 4ab006f065 | |||
| 7eaaef6e75 | |||
| c4f94efe24 | |||
| 2eb729d712 | |||
| 0343b6cf98 | |||
| 7fc4ea0c68 | |||
| 195a3ab170 | |||
| a96f485e3c | |||
| 1b3a32f83f | |||
| cacf74af3c | |||
| 4e9f68acff | |||
| b58295d1d6 | |||
| cbcf187814 | |||
| 4baa003c0d | |||
| 8cf8c3c122 | |||
| e3e2c0ab6a | |||
| e8862a3811 | |||
| 1c1a82696b | |||
| a2893bac7e | |||
| 0eda42f29f | |||
| 810da0db61 | |||
| 8f6aa950cd | |||
| 36a2482165 | |||
| 639c18395b | |||
| fe08fd3f0a | |||
| 29bef052c7 | |||
| a74736b100 | |||
| 3f7e7f2601 | |||
| 259eff3fea | |||
| c271235d16 | |||
| 7539afa91e | |||
| 4c79905f5b | |||
| 23ad006187 | |||
| f7926847ac | |||
| 420c616e9d | |||
| 822009ec5f | |||
| b5e1c78461 | |||
| 0060f57b63 | |||
| ddaa5b784d | |||
| a40f365a54 | |||
| 3964bffce4 | |||
| a8deb3593b | |||
| e0d6e0117e | |||
| e157160337 | |||
| 14fa0b4fd3 | |||
| c1c68cf72d | |||
| 591d98d8b6 | |||
| e0d93eaa9f | |||
| 0c40a954fa | |||
| 784f99a900 | |||
| 90ae0b3e44 | |||
| 2c429fd406 | |||
| ebcca0c3b8 | |||
| 925fd9f268 | |||
| 0058edc24e | |||
| aa66133813 | |||
| 66f9a82f31 | |||
| 1092abe776 | |||
| 0411792ca5 | |||
| b9a13d3a32 | |||
| 0a1359ed16 | |||
| 5b8a0881b7 | |||
| 5d677a9115 | |||
| 75489c00c2 | |||
| 3aaa7b62ef | |||
| c13b9754eb | |||
| a97417fd38 | |||
| 43261f8469 | |||
| b32935dd97 | |||
| 35660ff5e7 | |||
| 9b07909ed8 | |||
| b4eb317b00 | |||
| 464d77dfb5 | |||
| d0c9c1043c | |||
| 795405c47d | |||
| 2ef53c6df9 | |||
| 249be451f7 | |||
| 3a264e6baf | |||
| 4224e8314b | |||
| 7b14ad9616 | |||
| be7386f0d7 | |||
| cd3263db50 | |||
| 3034019d5a | |||
| 1fd48a1cf8 | |||
| c3f39ad24d | |||
| 68d9394d9f | |||
| 80fca589af | |||
| 420c33d3ba | |||
| de0cd976de | |||
| 1e180489a4 | |||
| e00656d757 | |||
| ca6b7fbeb2 | |||
| 0479113949 | |||
| acca85b99a | |||
| 03a0e2084a | |||
| 116fa6777b | |||
| 35d4222c7a | |||
| dd0de7e8be | |||
| e3e7503a7c | |||
| b66f4bf2be | |||
| 1c8dbae359 | |||
| 4f36349630 | |||
| 68b27451f2 | |||
| c7acd63ea7 | |||
| cfc17cf290 | |||
| 904e173037 | |||
| a9ebf534c6 | |||
| 87f01007cc | |||
| 3d63d6c0f2 | |||
| 905a3a30f3 | |||
| ec96c1b534 | |||
| c74c5e0c6d | |||
| 167d57408d | |||
| 8e61d77497 |
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Log files**
|
||||
Attach log files to help explain your problem.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. Windows 10]
|
||||
- Version [e.g. 2.1.2]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Version [e.g. 2.1.2]
|
||||
|
||||
**Server (please complete the following information):**
|
||||
- OS: [e.g. Ubuntu 22.04]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -8,6 +8,7 @@ deploy/build/*
|
||||
deploy/build_32/*
|
||||
deploy/build_64/*
|
||||
winbuild*.bat
|
||||
.cache/
|
||||
|
||||
|
||||
# Qt-es
|
||||
|
||||
@@ -2,11 +2,14 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
|
||||
project(${PROJECT} VERSION 3.1.0.0
|
||||
project(${PROJECT} VERSION 4.0.7.1
|
||||
DESCRIPTION "AmneziaVPN"
|
||||
HOMEPAGE_URL "https://amnezia.org/"
|
||||
)
|
||||
set(RELEASE_DATE "2023-08-28")
|
||||
|
||||
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})
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
|
||||
@@ -10,30 +10,34 @@ set_property(GLOBAL PROPERTY AUTOMOC_TARGETS_FOLDER "Autogen")
|
||||
set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "Autogen")
|
||||
|
||||
set(PACKAGES
|
||||
Widgets Core Gui Network Xml
|
||||
Core Gui Network Xml
|
||||
RemoteObjects Quick Svg QuickControls2
|
||||
Core5Compat Concurrent LinguistTools
|
||||
)
|
||||
|
||||
if(IOS)
|
||||
set(PACKAGES
|
||||
${PACKAGES}
|
||||
Multimedia
|
||||
)
|
||||
set(PACKAGES ${PACKAGES} Multimedia)
|
||||
endif()
|
||||
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
set(PACKAGES ${PACKAGES} Widgets)
|
||||
endif()
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS ${PACKAGES})
|
||||
|
||||
set(LIBS ${LIBS}
|
||||
Qt6::Widgets Qt6::Core Qt6::Gui
|
||||
Qt6::Core Qt6::Gui
|
||||
Qt6::Network Qt6::Xml Qt6::RemoteObjects
|
||||
Qt6::Quick Qt6::Svg Qt6::QuickControls2
|
||||
Qt6::Core5Compat Qt6::Concurrent
|
||||
)
|
||||
|
||||
if(IOS)
|
||||
set(LIBS
|
||||
${LIBS}
|
||||
Qt6::Multimedia
|
||||
)
|
||||
set(LIBS ${LIBS} Qt6::Multimedia)
|
||||
endif()
|
||||
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
set(LIBS ${LIBS} Qt6::Widgets)
|
||||
endif()
|
||||
|
||||
qt_standard_project_setup()
|
||||
@@ -91,7 +95,6 @@ set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/pages.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/property_helper.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/uilogic.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/version.h
|
||||
@@ -128,7 +131,6 @@ set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/servercontroller.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/uilogic.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.cpp
|
||||
@@ -162,20 +164,33 @@ file(GLOB_RECURSE PAGE_LOGIC_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/
|
||||
file(GLOB CONFIGURATORS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/configurators/*.h)
|
||||
file(GLOB CONFIGURATORS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/configurators/*.cpp)
|
||||
|
||||
file(GLOB UI_MODELS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/models/*.h)
|
||||
file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/models/*.cpp)
|
||||
file(GLOB UI_MODELS_H CONFIGURE_DEPENDS
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/models/*.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/models/protocols/*.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/models/services/*.h
|
||||
)
|
||||
file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/models/*.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/models/protocols/*.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/models/services/*.cpp
|
||||
)
|
||||
|
||||
file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.h)
|
||||
file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.cpp)
|
||||
|
||||
set(HEADERS ${HEADERS}
|
||||
${COMMON_FILES_H}
|
||||
${PAGE_LOGIC_H}
|
||||
${CONFIGURATORS_H}
|
||||
${UI_MODELS_H}
|
||||
${UI_CONTROLLERS_H}
|
||||
)
|
||||
set(SOURCES ${SOURCES}
|
||||
${COMMON_FILES_CPP}
|
||||
${PAGE_LOGIC_CPP}
|
||||
${CONFIGURATORS_CPP}
|
||||
${UI_MODELS_CPP}
|
||||
${UI_CONTROLLERS_CPP}
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
@@ -307,16 +322,5 @@ if(NOT IOS AND NOT ANDROID)
|
||||
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_custom_command(
|
||||
TARGET ${PROJECT} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E $<IF:$<CONFIG:Debug>,copy,true>
|
||||
$<TARGET_FILE_DIR:${PROJECT}>/../service/wireguard-service/wireguard-service.exe
|
||||
$<TARGET_FILE_DIR:${PROJECT}>/wireguard/wireguard-service.exe
|
||||
COMMAND_EXPAND_LISTS
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC})
|
||||
qt_finalize_target(${PROJECT})
|
||||
|
||||
@@ -3,55 +3,35 @@
|
||||
#include <QClipboard>
|
||||
#include <QFontDatabase>
|
||||
#include <QMimeData>
|
||||
#include <QQuickStyle>
|
||||
#include <QResource>
|
||||
#include <QStandardPaths>
|
||||
#include <QTextDocument>
|
||||
#include <QTimer>
|
||||
#include <QTranslator>
|
||||
|
||||
#include <QQuickItem>
|
||||
|
||||
#include "core/servercontroller.h"
|
||||
#include "logger.h"
|
||||
#include "version.h"
|
||||
#include <QQuickStyle>
|
||||
|
||||
#include "platforms/ios/QRCodeReaderBase.h"
|
||||
|
||||
#include "ui/pages.h"
|
||||
|
||||
#include "ui/pages_logic/AppSettingsLogic.h"
|
||||
#include "ui/pages_logic/GeneralSettingsLogic.h"
|
||||
#include "ui/pages_logic/NetworkSettingsLogic.h"
|
||||
#include "ui/pages_logic/NewServerProtocolsLogic.h"
|
||||
#include "ui/pages_logic/QrDecoderLogic.h"
|
||||
#include "ui/pages_logic/ServerConfiguringProgressLogic.h"
|
||||
#include "ui/pages_logic/ServerContainersLogic.h"
|
||||
#include "ui/pages_logic/ServerListLogic.h"
|
||||
#include "ui/pages_logic/ServerSettingsLogic.h"
|
||||
#include "ui/pages_logic/ServerContainersLogic.h"
|
||||
#include "ui/pages_logic/ShareConnectionLogic.h"
|
||||
#include "ui/pages_logic/SitesLogic.h"
|
||||
#include "ui/pages_logic/StartPageLogic.h"
|
||||
#include "ui/pages_logic/VpnLogic.h"
|
||||
#include "ui/pages_logic/WizardLogic.h"
|
||||
|
||||
#include "ui/pages_logic/protocols/CloakLogic.h"
|
||||
#include "ui/pages_logic/protocols/OpenVpnLogic.h"
|
||||
#include "ui/pages_logic/protocols/ShadowSocksLogic.h"
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#include "platforms/android/android_controller.h"
|
||||
#endif
|
||||
|
||||
#include "protocols/qml_register_protocols.h"
|
||||
|
||||
#if defined(Q_OS_IOS)
|
||||
#include "platforms/ios/QtAppDelegate-C-Interface.h"
|
||||
#include "platforms/ios/ios_controller.h"
|
||||
#include "platforms/ios/ios_controller.h"
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]):
|
||||
AMNEZIA_BASE_CLASS(argc, argv)
|
||||
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
|
||||
#else
|
||||
AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecondary,
|
||||
SingleApplication::Options options, int timeout, const QString &userData):
|
||||
SingleApplication(argc, argv, allowSecondary, options, timeout, userData)
|
||||
AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecondary, SingleApplication::Options options,
|
||||
int timeout, const QString &userData)
|
||||
: SingleApplication(argc, argv, allowSecondary, options, timeout, userData)
|
||||
#endif
|
||||
{
|
||||
setQuitOnLastWindowClosed(false);
|
||||
@@ -73,49 +53,94 @@
|
||||
#endif
|
||||
|
||||
m_settings = std::shared_ptr<Settings>(new Settings);
|
||||
m_configurator = std::shared_ptr<VpnConfigurator>(new VpnConfigurator(m_settings, this));
|
||||
}
|
||||
|
||||
AmneziaApplication::~AmneziaApplication()
|
||||
{
|
||||
m_vpnConnectionThread.quit();
|
||||
m_vpnConnectionThread.wait(3000);
|
||||
|
||||
if (m_engine) {
|
||||
QObject::disconnect(m_engine, 0,0,0);
|
||||
QObject::disconnect(m_engine, 0, 0, 0);
|
||||
delete m_engine;
|
||||
}
|
||||
if (m_uiLogic) {
|
||||
QObject::disconnect(m_uiLogic, 0,0,0);
|
||||
delete m_uiLogic;
|
||||
}
|
||||
|
||||
if (m_protocolProps) delete m_protocolProps;
|
||||
if (m_containerProps) delete m_containerProps;
|
||||
}
|
||||
|
||||
void AmneziaApplication::init()
|
||||
{
|
||||
m_engine = new QQmlApplicationEngine;
|
||||
m_uiLogic = new UiLogic(m_settings, m_configurator);
|
||||
|
||||
const QUrl url(QStringLiteral("qrc:/ui/qml/main.qml"));
|
||||
QObject::connect(m_engine, &QQmlApplicationEngine::objectCreated,
|
||||
this, [url](QObject *obj, const QUrl &objUrl) {
|
||||
if (!obj && url == objUrl)
|
||||
QCoreApplication::exit(-1);
|
||||
}, Qt::QueuedConnection);
|
||||
const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml"));
|
||||
QObject::connect(
|
||||
m_engine, &QQmlApplicationEngine::objectCreated, this,
|
||||
[url](QObject *obj, const QUrl &objUrl) {
|
||||
if (!obj && url == objUrl)
|
||||
QCoreApplication::exit(-1);
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance());
|
||||
m_uiLogic->registerPagesLogic();
|
||||
|
||||
#if defined(Q_OS_IOS)
|
||||
setStartPageLogic(m_uiLogic->pageLogic<StartPageLogic>());
|
||||
IosController::Instance()->initialize();
|
||||
m_configurator = std::shared_ptr<VpnConfigurator>(new VpnConfigurator(m_settings, this));
|
||||
m_vpnConnection.reset(new VpnConnection(m_settings, m_configurator));
|
||||
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
|
||||
m_vpnConnectionThread.start();
|
||||
|
||||
initModels();
|
||||
initControllers();
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
connect(AndroidController::instance(), &AndroidController::initialized, this,
|
||||
[this](bool status, bool connected, const QDateTime &connectionDate) {
|
||||
if (connected) {
|
||||
m_connectionController->onConnectionStateChanged(Vpn::ConnectionState::Connected);
|
||||
if (m_vpnConnection)
|
||||
m_vpnConnection->restoreConnection();
|
||||
}
|
||||
});
|
||||
if (!AndroidController::instance()->initialize()) {
|
||||
qCritical() << QString("Init failed");
|
||||
if (m_vpnConnection)
|
||||
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, [this](QString data) {
|
||||
m_pageController->replaceStartPage();
|
||||
m_importController->extractConfigFromData(data);
|
||||
m_pageController->goToPageViewConfig();
|
||||
});
|
||||
#endif
|
||||
|
||||
m_engine->load(url);
|
||||
#ifdef Q_OS_IOS
|
||||
IosController::Instance()->initialize();
|
||||
connect(IosController::Instance(), &IosController::importConfigFromOutside, [this](QString data) {
|
||||
m_pageController->replaceStartPage();
|
||||
m_importController->extractConfigFromData(data);
|
||||
m_pageController->goToPageViewConfig();
|
||||
});
|
||||
|
||||
if (m_engine->rootObjects().size() > 0) {
|
||||
m_uiLogic->setQmlRoot(m_engine->rootObjects().at(0));
|
||||
}
|
||||
connect(IosController::Instance(), &IosController::importBackupFromOutside, [this](QString filePath) {
|
||||
m_pageController->replaceStartPage();
|
||||
m_pageController->goToPageSettingsBackup();
|
||||
m_settingsController->importBackupFromOutside(filePath);
|
||||
});
|
||||
#endif
|
||||
|
||||
m_notificationHandler.reset(NotificationHandler::create(nullptr));
|
||||
|
||||
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
|
||||
&NotificationHandler::setConnectionState);
|
||||
|
||||
connect(m_notificationHandler.get(), &NotificationHandler::raiseRequested, m_pageController.get(),
|
||||
&PageController::raiseMainWindow);
|
||||
connect(m_notificationHandler.get(), &NotificationHandler::connectRequested, m_connectionController.get(),
|
||||
&ConnectionController::openConnection);
|
||||
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
|
||||
&ConnectionController::closeConnection);
|
||||
|
||||
m_engine->load(url);
|
||||
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
|
||||
|
||||
if (m_settings->isSaveLogs()) {
|
||||
if (!Logger::init()) {
|
||||
@@ -124,19 +149,20 @@ void AmneziaApplication::init()
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (m_parser.isSet("a")) m_uiLogic->showOnStartup();
|
||||
else emit m_uiLogic->show();
|
||||
if (m_parser.isSet("a"))
|
||||
m_pageController->showOnStartup();
|
||||
else
|
||||
emit m_pageController->raiseMainWindow();
|
||||
#else
|
||||
m_uiLogic->showOnStartup();
|
||||
m_pageController->showOnStartup();
|
||||
#endif
|
||||
|
||||
// TODO - fix
|
||||
// TODO - fix
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
if (isPrimary()) {
|
||||
QObject::connect(this, &SingleApplication::instanceStarted, m_uiLogic, [this](){
|
||||
QObject::connect(this, &SingleApplication::instanceStarted, m_pageController.get(), [this]() {
|
||||
qDebug() << "Secondary instance started, showing this window instead";
|
||||
emit m_uiLogic->show();
|
||||
emit m_uiLogic->raise();
|
||||
emit m_pageController->raiseMainWindow();
|
||||
});
|
||||
}
|
||||
#endif
|
||||
@@ -144,7 +170,7 @@ void AmneziaApplication::init()
|
||||
// Android TextField clipboard workaround
|
||||
// https://bugreports.qt.io/browse/QTBUG-113461
|
||||
#ifdef Q_OS_ANDROID
|
||||
QObject::connect(qApp, &QApplication::applicationStateChanged, [](Qt::ApplicationState state) {
|
||||
QObject::connect(qApp, &QGuiApplication::applicationStateChanged, [](Qt::ApplicationState state) {
|
||||
if (state == Qt::ApplicationActive) {
|
||||
if (qApp->clipboard()->mimeData()->formats().contains("text/html")) {
|
||||
QTextDocument doc;
|
||||
@@ -158,76 +184,91 @@ void AmneziaApplication::init()
|
||||
|
||||
void AmneziaApplication::registerTypes()
|
||||
{
|
||||
qRegisterMetaType<VpnProtocol::VpnConnectionState>("VpnProtocol::VpnConnectionState");
|
||||
qRegisterMetaType<ServerCredentials>("ServerCredentials");
|
||||
|
||||
qRegisterMetaType<DockerContainer>("DockerContainer");
|
||||
qRegisterMetaType<TransportProto>("TransportProto");
|
||||
qRegisterMetaType<Proto>("Proto");
|
||||
qRegisterMetaType<ServiceType>("ServiceType");
|
||||
qRegisterMetaType<Page>("Page");
|
||||
qRegisterMetaType<VpnProtocol::VpnConnectionState>("ConnectionState");
|
||||
|
||||
qRegisterMetaType<PageProtocolLogicBase *>("PageProtocolLogicBase *");
|
||||
|
||||
|
||||
declareQmlPageEnum();
|
||||
declareQmlProtocolEnum();
|
||||
declareQmlContainerEnum();
|
||||
|
||||
qmlRegisterType<PageType>("PageType", 1, 0, "PageType");
|
||||
qmlRegisterType<QRCodeReader>("QRCodeReader", 1, 0, "QRCodeReader");
|
||||
|
||||
m_containerProps = new ContainerProps;
|
||||
qmlRegisterSingletonInstance("ContainerProps", 1, 0, "ContainerProps", m_containerProps);
|
||||
m_containerProps.reset(new ContainerProps());
|
||||
qmlRegisterSingletonInstance("ContainerProps", 1, 0, "ContainerProps", m_containerProps.get());
|
||||
|
||||
m_protocolProps = new ProtocolProps;
|
||||
qmlRegisterSingletonInstance("ProtocolProps", 1, 0, "ProtocolProps", m_protocolProps);
|
||||
m_protocolProps.reset(new ProtocolProps());
|
||||
qmlRegisterSingletonInstance("ProtocolProps", 1, 0, "ProtocolProps", m_protocolProps.get());
|
||||
|
||||
qmlRegisterSingletonType(QUrl("qrc:/ui/qml/Filters/ContainersModelFilters.qml"), "ContainersModelFilters", 1, 0,
|
||||
"ContainersModelFilters");
|
||||
|
||||
//
|
||||
Vpn::declareQmlVpnConnectionStateEnum();
|
||||
PageLoader::declareQmlPageEnum();
|
||||
}
|
||||
|
||||
void AmneziaApplication::loadFonts()
|
||||
{
|
||||
QQuickStyle::setStyle("Basic");
|
||||
|
||||
QFontDatabase::addApplicationFont(":/fonts/Lato-Black.ttf");
|
||||
QFontDatabase::addApplicationFont(":/fonts/Lato-BlackItalic.ttf");
|
||||
QFontDatabase::addApplicationFont(":/fonts/Lato-Bold.ttf");
|
||||
QFontDatabase::addApplicationFont(":/fonts/Lato-BoldItalic.ttf");
|
||||
QFontDatabase::addApplicationFont(":/fonts/Lato-Italic.ttf");
|
||||
QFontDatabase::addApplicationFont(":/fonts/Lato-Light.ttf");
|
||||
QFontDatabase::addApplicationFont(":/fonts/Lato-LightItalic.ttf");
|
||||
QFontDatabase::addApplicationFont(":/fonts/Lato-Regular.ttf");
|
||||
QFontDatabase::addApplicationFont(":/fonts/Lato-Thin.ttf");
|
||||
QFontDatabase::addApplicationFont(":/fonts/Lato-ThinItalic.ttf");
|
||||
QFontDatabase::addApplicationFont(":/fonts/pt-root-ui_vf.ttf");
|
||||
}
|
||||
|
||||
void AmneziaApplication::loadTranslator()
|
||||
{
|
||||
m_translator = new QTranslator;
|
||||
if (m_translator->load(QLocale(), QString("amneziavpn"), QLatin1String("_"), QLatin1String(":/translations"))) {
|
||||
installTranslator(m_translator);
|
||||
auto locale = m_settings->getAppLanguage();
|
||||
m_translator.reset(new QTranslator());
|
||||
if (locale != QLocale::English) {
|
||||
if (m_translator->load(locale, QString("amneziavpn"), QLatin1String("_"), QLatin1String(":/i18n"))) {
|
||||
if (QCoreApplication::installTranslator(m_translator.get())) {
|
||||
m_settings->setAppLanguage(locale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AmneziaApplication::updateTranslator(const QLocale &locale)
|
||||
{
|
||||
QResource::registerResource(":/translations.qrc");
|
||||
if (!m_translator->isEmpty())
|
||||
QCoreApplication::removeTranslator(m_translator.get());
|
||||
|
||||
if (locale == QLocale::English) {
|
||||
m_settings->setAppLanguage(locale);
|
||||
m_engine->retranslate();
|
||||
}
|
||||
|
||||
if (m_translator->load(locale, QString("amneziavpn"), QLatin1String("_"), QLatin1String(":/i18n"))) {
|
||||
if (QCoreApplication::installTranslator(m_translator.get())) {
|
||||
m_settings->setAppLanguage(locale);
|
||||
}
|
||||
|
||||
m_engine->retranslate();
|
||||
}
|
||||
|
||||
emit translationsUpdated();
|
||||
}
|
||||
|
||||
bool AmneziaApplication::parseCommands()
|
||||
{
|
||||
m_parser.setApplicationDescription(APPLICATION_NAME);
|
||||
m_parser.addHelpOption();
|
||||
m_parser.addVersionOption();
|
||||
|
||||
QCommandLineOption c_autostart {{"a", "autostart"}, "System autostart"};
|
||||
QCommandLineOption c_autostart { { "a", "autostart" }, "System autostart" };
|
||||
m_parser.addOption(c_autostart);
|
||||
|
||||
QCommandLineOption c_cleanup {{"c", "cleanup"}, "Cleanup logs"};
|
||||
QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" };
|
||||
m_parser.addOption(c_cleanup);
|
||||
|
||||
m_parser.process(*this);
|
||||
|
||||
if (m_parser.isSet(c_cleanup)) {
|
||||
Logger::cleanUp();
|
||||
QTimer::singleShot(100, this, [this]{
|
||||
quit();
|
||||
});
|
||||
QTimer::singleShot(100, this, [this] { quit(); });
|
||||
exec();
|
||||
return false;
|
||||
}
|
||||
@@ -239,3 +280,88 @@ QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
|
||||
return m_engine;
|
||||
}
|
||||
|
||||
void AmneziaApplication::initModels()
|
||||
{
|
||||
m_containersModel.reset(new ContainersModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
|
||||
|
||||
m_serversModel.reset(new ServersModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
|
||||
connect(m_serversModel.get(), &ServersModel::currentlyProcessedServerIndexChanged, m_containersModel.get(),
|
||||
&ContainersModel::setCurrentlyProcessedServerIndex);
|
||||
|
||||
m_languageModel.reset(new LanguageModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
|
||||
connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &AmneziaApplication::updateTranslator);
|
||||
connect(this, &AmneziaApplication::translationsUpdated, m_languageModel.get(), &LanguageModel::translationsUpdated);
|
||||
|
||||
m_sitesModel.reset(new SitesModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
|
||||
connect(m_containersModel.get(), &ContainersModel::defaultContainerChanged, this, [this]() {
|
||||
if (m_containersModel->getDefaultContainer() == DockerContainer::WireGuard
|
||||
&& m_sitesModel->getRouteMode() != Settings::RouteMode::VpnAllSites) {
|
||||
m_sitesModel->setRouteMode(Settings::RouteMode::VpnAllSites);
|
||||
emit m_pageController->showNotificationMessage(
|
||||
tr("Split tunneling for WireGuard is not implemented, the option was disabled"));
|
||||
}
|
||||
});
|
||||
|
||||
m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
|
||||
|
||||
m_openVpnConfigModel.reset(new OpenVpnConfigModel(this));
|
||||
m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get());
|
||||
|
||||
m_shadowSocksConfigModel.reset(new ShadowSocksConfigModel(this));
|
||||
m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get());
|
||||
|
||||
m_cloakConfigModel.reset(new CloakConfigModel(this));
|
||||
m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get());
|
||||
|
||||
m_wireguardConfigModel.reset(new WireGuardConfigModel(this));
|
||||
m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireguardConfigModel.get());
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this));
|
||||
m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get());
|
||||
#endif
|
||||
|
||||
m_sftpConfigModel.reset(new SftpConfigModel(this));
|
||||
m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
|
||||
}
|
||||
|
||||
void AmneziaApplication::initControllers()
|
||||
{
|
||||
m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_vpnConnection));
|
||||
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
|
||||
|
||||
m_pageController.reset(new PageController(m_serversModel, m_settings));
|
||||
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
|
||||
|
||||
m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_settings));
|
||||
m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
|
||||
connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(),
|
||||
&PageController::showPassphraseRequestDrawer);
|
||||
connect(m_pageController.get(), &PageController::passphraseRequestDrawerClosed, m_installController.get(),
|
||||
&InstallController::setEncryptedPassphrase);
|
||||
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
|
||||
&ConnectionController::onCurrentContainerUpdated);
|
||||
|
||||
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
|
||||
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
|
||||
|
||||
m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_settings, m_configurator));
|
||||
m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
|
||||
|
||||
m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_settings));
|
||||
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
|
||||
if (m_settingsController->isAutoStartEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
|
||||
QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
|
||||
}
|
||||
|
||||
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
|
||||
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
||||
|
||||
m_systemController.reset(new SystemController(m_settings));
|
||||
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
|
||||
}
|
||||
|
||||
@@ -1,27 +1,52 @@
|
||||
#ifndef AMNEZIA_APPLICATION_H
|
||||
#define AMNEZIA_APPLICATION_H
|
||||
|
||||
#include <QApplication>
|
||||
#include <QGuiApplication>
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
#include <QThread>
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
#include <QGuiApplication>
|
||||
#else
|
||||
#include <QApplication>
|
||||
#endif
|
||||
|
||||
#include "settings.h"
|
||||
#include "vpnconnection.h"
|
||||
|
||||
#include "ui/uilogic.h"
|
||||
#include "configurators/vpn_configurator.h"
|
||||
|
||||
#include "ui/controllers/connectionController.h"
|
||||
#include "ui/controllers/exportController.h"
|
||||
#include "ui/controllers/importController.h"
|
||||
#include "ui/controllers/installController.h"
|
||||
#include "ui/controllers/pageController.h"
|
||||
#include "ui/controllers/settingsController.h"
|
||||
#include "ui/controllers/sitesController.h"
|
||||
#include "ui/controllers/systemController.h"
|
||||
#include "ui/models/containers_model.h"
|
||||
#include "ui/models/languageModel.h"
|
||||
#include "ui/models/protocols/cloakConfigModel.h"
|
||||
#include "ui/notificationhandler.h"
|
||||
#ifdef Q_OS_WINDOWS
|
||||
#include "ui/models/protocols/ikev2ConfigModel.h"
|
||||
#endif
|
||||
#include "ui/models/protocols/openvpnConfigModel.h"
|
||||
#include "ui/models/protocols/shadowsocksConfigModel.h"
|
||||
#include "ui/models/protocols/wireguardConfigModel.h"
|
||||
#include "ui/models/protocols_model.h"
|
||||
#include "ui/models/servers_model.h"
|
||||
#include "ui/models/services/sftpConfigModel.h"
|
||||
#include "ui/models/sites_model.h"
|
||||
|
||||
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
|
||||
|
||||
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
#define AMNEZIA_BASE_CLASS QApplication
|
||||
#define AMNEZIA_BASE_CLASS QGuiApplication
|
||||
#else
|
||||
#define AMNEZIA_BASE_CLASS SingleApplication
|
||||
#define QAPPLICATION_CLASS QApplication
|
||||
#include "singleapplication.h"
|
||||
#define AMNEZIA_BASE_CLASS SingleApplication
|
||||
#define QAPPLICATION_CLASS QApplication
|
||||
#include "singleapplication.h"
|
||||
#endif
|
||||
|
||||
class AmneziaApplication : public AMNEZIA_BASE_CLASS
|
||||
@@ -32,7 +57,8 @@ public:
|
||||
AmneziaApplication(int &argc, char *argv[]);
|
||||
#else
|
||||
AmneziaApplication(int &argc, char *argv[], bool allowSecondary = false,
|
||||
SingleApplication::Options options = SingleApplication::User, int timeout = 1000, const QString &userData = {} );
|
||||
SingleApplication::Options options = SingleApplication::User, int timeout = 1000,
|
||||
const QString &userData = {});
|
||||
#endif
|
||||
virtual ~AmneziaApplication();
|
||||
|
||||
@@ -40,22 +66,56 @@ public:
|
||||
void registerTypes();
|
||||
void loadFonts();
|
||||
void loadTranslator();
|
||||
void updateTranslator(const QLocale &locale);
|
||||
bool parseCommands();
|
||||
|
||||
QQmlApplicationEngine *qmlEngine() const;
|
||||
|
||||
signals:
|
||||
void translationsUpdated();
|
||||
|
||||
private:
|
||||
void initModels();
|
||||
void initControllers();
|
||||
|
||||
QQmlApplicationEngine *m_engine {};
|
||||
UiLogic *m_uiLogic {};
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
std::shared_ptr<VpnConfigurator> m_configurator;
|
||||
|
||||
ContainerProps* m_containerProps {};
|
||||
ProtocolProps* m_protocolProps {};
|
||||
QSharedPointer<ContainerProps> m_containerProps;
|
||||
QSharedPointer<ProtocolProps> m_protocolProps;
|
||||
|
||||
QTranslator* m_translator;
|
||||
QSharedPointer<QTranslator> m_translator;
|
||||
QCommandLineParser m_parser;
|
||||
|
||||
QSharedPointer<ContainersModel> m_containersModel;
|
||||
QSharedPointer<ServersModel> m_serversModel;
|
||||
QSharedPointer<LanguageModel> m_languageModel;
|
||||
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
||||
QSharedPointer<SitesModel> m_sitesModel;
|
||||
|
||||
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
|
||||
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
|
||||
QScopedPointer<CloakConfigModel> m_cloakConfigModel;
|
||||
QScopedPointer<WireGuardConfigModel> m_wireguardConfigModel;
|
||||
#ifdef Q_OS_WINDOWS
|
||||
QScopedPointer<Ikev2ConfigModel> m_ikev2ConfigModel;
|
||||
#endif
|
||||
|
||||
QScopedPointer<SftpConfigModel> m_sftpConfigModel;
|
||||
|
||||
QSharedPointer<VpnConnection> m_vpnConnection;
|
||||
QThread m_vpnConnectionThread;
|
||||
QScopedPointer<NotificationHandler> m_notificationHandler;
|
||||
|
||||
QScopedPointer<ConnectionController> m_connectionController;
|
||||
QScopedPointer<PageController> m_pageController;
|
||||
QScopedPointer<InstallController> m_installController;
|
||||
QScopedPointer<ImportController> m_importController;
|
||||
QScopedPointer<ExportController> m_exportController;
|
||||
QScopedPointer<SettingsController> m_settingsController;
|
||||
QScopedPointer<SitesController> m_sitesController;
|
||||
QScopedPointer<SystemController> m_systemController;
|
||||
};
|
||||
|
||||
#endif // AMNEZIA_APPLICATION_H
|
||||
|
||||
@@ -36,7 +36,8 @@
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:allowNativeHeapPointerTagging="false"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar"
|
||||
android:icon="@drawable/icon">
|
||||
android:icon="@drawable/icon"
|
||||
android:roundIcon="@drawable/icon_round">
|
||||
|
||||
<activity
|
||||
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
|
||||
|
||||
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 11 KiB |
@@ -10,6 +10,7 @@ set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_notificationhandler.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidutils.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidvpnactivity.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.h
|
||||
)
|
||||
|
||||
@@ -18,6 +19,7 @@ set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_notificationhandler.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidutils.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidvpnactivity.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
#include "ikev2_configurator.h"
|
||||
#include <QApplication>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
#include <QTemporaryDir>
|
||||
#include <QDebug>
|
||||
#include <QTemporaryFile>
|
||||
#include <QJsonDocument>
|
||||
#include <QUuid>
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/server_defs.h"
|
||||
#include "core/scripts_registry.h"
|
||||
#include "utilities.h"
|
||||
#include "core/server_defs.h"
|
||||
#include "core/servercontroller.h"
|
||||
#include "utilities.h"
|
||||
|
||||
|
||||
Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings, QObject *parent):
|
||||
ConfiguratorBase(settings, parent)
|
||||
Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: ConfiguratorBase(settings, parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const ServerCredentials &credentials,
|
||||
DockerContainer container, ErrorCode *errorCode)
|
||||
DockerContainer container, ErrorCode *errorCode)
|
||||
{
|
||||
Ikev2Configurator::ConnectionData connData;
|
||||
connData.host = credentials.hostName;
|
||||
@@ -32,26 +30,27 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
|
||||
|
||||
QString certFileName = "/opt/amnezia/ikev2/clients/" + connData.clientId + ".p12";
|
||||
|
||||
QString scriptCreateCert = QString("certutil -z <(head -c 1024 /dev/urandom) "\
|
||||
"-S -c \"IKEv2 VPN CA\" -n \"%1\" "\
|
||||
"-s \"O=IKEv2 VPN,CN=%1\" "\
|
||||
"-k rsa -g 3072 -v 120 "\
|
||||
"-d sql:/etc/ipsec.d -t \",,\" "\
|
||||
"--keyUsage digitalSignature,keyEncipherment "\
|
||||
"--extKeyUsage serverAuth,clientAuth -8 \"%1\"")
|
||||
.arg(connData.clientId);
|
||||
QString scriptCreateCert = QString("certutil -z <(head -c 1024 /dev/urandom) "
|
||||
"-S -c \"IKEv2 VPN CA\" -n \"%1\" "
|
||||
"-s \"O=IKEv2 VPN,CN=%1\" "
|
||||
"-k rsa -g 3072 -v 120 "
|
||||
"-d sql:/etc/ipsec.d -t \",,\" "
|
||||
"--keyUsage digitalSignature,keyEncipherment "
|
||||
"--extKeyUsage serverAuth,clientAuth -8 \"%1\"")
|
||||
.arg(connData.clientId);
|
||||
|
||||
ServerController serverController(m_settings);
|
||||
ErrorCode e = serverController.runContainerScript(credentials, container, scriptCreateCert);
|
||||
|
||||
QString scriptExportCert = QString("pk12util -W \"%1\" -d sql:/etc/ipsec.d -n \"%2\" -o \"%3\"")
|
||||
.arg(connData.password)
|
||||
.arg(connData.clientId)
|
||||
.arg(certFileName);
|
||||
.arg(connData.password)
|
||||
.arg(connData.clientId)
|
||||
.arg(certFileName);
|
||||
e = serverController.runContainerScript(credentials, container, scriptExportCert);
|
||||
|
||||
connData.clientCert = serverController.getTextFileFromContainer(container, credentials, certFileName, &e);
|
||||
connData.caCert = serverController.getTextFileFromContainer(container, credentials, "/etc/ipsec.d/ca_cert_base64.p12", &e);
|
||||
connData.caCert =
|
||||
serverController.getTextFileFromContainer(container, credentials, "/etc/ipsec.d/ca_cert_base64.p12", &e);
|
||||
|
||||
qDebug() << "Ikev2Configurator::ConnectionData client cert size:" << connData.clientCert.size();
|
||||
qDebug() << "Ikev2Configurator::ConnectionData ca cert size:" << connData.caCert.size();
|
||||
@@ -59,8 +58,8 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
|
||||
return connData;
|
||||
}
|
||||
|
||||
QString Ikev2Configurator::genIkev2Config(const ServerCredentials &credentials,
|
||||
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
|
||||
QString Ikev2Configurator::genIkev2Config(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode *errorCode)
|
||||
{
|
||||
Q_UNUSED(containerConfig)
|
||||
|
||||
@@ -120,4 +119,3 @@ QString Ikev2Configurator::genStrongSwanConfig(const ConnectionData &connData)
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,82 +1,94 @@
|
||||
#include "openvpn_configurator.h"
|
||||
#include <QApplication>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
#include <QTemporaryDir>
|
||||
#include <QDebug>
|
||||
#include <QTemporaryFile>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
#include <QGuiApplication>
|
||||
#else
|
||||
#include <QApplication>
|
||||
#endif
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/scripts_registry.h"
|
||||
#include "core/server_defs.h"
|
||||
#include "core/servercontroller.h"
|
||||
#include "core/scripts_registry.h"
|
||||
#include "utilities.h"
|
||||
#include "settings.h"
|
||||
#include "utilities.h"
|
||||
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
|
||||
OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
|
||||
ConfiguratorBase(settings, parent)
|
||||
OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: ConfiguratorBase(settings, parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(const ServerCredentials &credentials,
|
||||
DockerContainer container, ErrorCode *errorCode)
|
||||
DockerContainer container,
|
||||
ErrorCode *errorCode)
|
||||
{
|
||||
OpenVpnConfigurator::ConnectionData connData = OpenVpnConfigurator::createCertRequest();
|
||||
connData.host = credentials.hostName;
|
||||
|
||||
if (connData.privKey.isEmpty() || connData.request.isEmpty()) {
|
||||
if (errorCode) *errorCode = ErrorCode::OpenSslFailed;
|
||||
if (errorCode)
|
||||
*errorCode = ErrorCode::OpenSslFailed;
|
||||
return connData;
|
||||
}
|
||||
|
||||
QString reqFileName = QString("%1/%2.req").
|
||||
arg(amnezia::protocols::openvpn::clientsDirPath).
|
||||
arg(connData.clientId);
|
||||
QString reqFileName = QString("%1/%2.req").arg(amnezia::protocols::openvpn::clientsDirPath).arg(connData.clientId);
|
||||
|
||||
ServerController serverController(m_settings);
|
||||
ErrorCode e = serverController.uploadTextFileToContainer(container, credentials, connData.request, reqFileName);
|
||||
if (e) {
|
||||
if (errorCode) *errorCode = e;
|
||||
if (errorCode)
|
||||
*errorCode = e;
|
||||
return connData;
|
||||
}
|
||||
|
||||
e = signCert(container, credentials, connData.clientId);
|
||||
if (e) {
|
||||
if (errorCode) *errorCode = e;
|
||||
if (errorCode)
|
||||
*errorCode = e;
|
||||
return connData;
|
||||
}
|
||||
|
||||
connData.caCert = serverController.getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::caCertPath, &e);
|
||||
connData.clientCert = serverController.getTextFileFromContainer(container, credentials,
|
||||
QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath).arg(connData.clientId), &e);
|
||||
connData.caCert = serverController.getTextFileFromContainer(container, credentials,
|
||||
amnezia::protocols::openvpn::caCertPath, &e);
|
||||
connData.clientCert = serverController.getTextFileFromContainer(
|
||||
container, credentials,
|
||||
QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath).arg(connData.clientId), &e);
|
||||
|
||||
if (e) {
|
||||
if (errorCode) *errorCode = e;
|
||||
if (errorCode)
|
||||
*errorCode = e;
|
||||
return connData;
|
||||
}
|
||||
|
||||
connData.taKey = serverController.getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::taKeyPath, &e);
|
||||
connData.taKey = serverController.getTextFileFromContainer(container, credentials,
|
||||
amnezia::protocols::openvpn::taKeyPath, &e);
|
||||
|
||||
if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) {
|
||||
if (errorCode) *errorCode = ErrorCode::SshSftpFailureError;
|
||||
if (errorCode)
|
||||
*errorCode = ErrorCode::SshSftpFailureError;
|
||||
}
|
||||
|
||||
return connData;
|
||||
}
|
||||
|
||||
QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials,
|
||||
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
|
||||
QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode *errorCode)
|
||||
{
|
||||
ServerController serverController(m_settings);
|
||||
QString config = serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container),
|
||||
serverController.genVarsForScript(credentials, container, containerConfig));
|
||||
QString config =
|
||||
serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container),
|
||||
serverController.genVarsForScript(credentials, container, containerConfig));
|
||||
|
||||
ConnectionData connData = prepareOpenVpnConfig(credentials, container, errorCode);
|
||||
if (errorCode && *errorCode) {
|
||||
@@ -89,8 +101,7 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia
|
||||
|
||||
if (config.contains("$OPENVPN_TA_KEY")) {
|
||||
config.replace("$OPENVPN_TA_KEY", connData.taKey);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
config.replace("<tls-auth>", "");
|
||||
config.replace("</tls-auth>", "");
|
||||
}
|
||||
@@ -133,12 +144,11 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig)
|
||||
config.replace("block-outside-dns", "");
|
||||
#endif
|
||||
|
||||
#if (defined (MZ_MACOS) || defined(MZ_LINUX))
|
||||
QString dnsConf = QString(
|
||||
"\nscript-security 2\n"
|
||||
"up %1/update-resolv-conf.sh\n"
|
||||
"down %1/update-resolv-conf.sh\n").
|
||||
arg(qApp->applicationDirPath());
|
||||
#if (defined(MZ_MACOS) || defined(MZ_LINUX))
|
||||
QString dnsConf = QString("\nscript-security 2\n"
|
||||
"up %1/update-resolv-conf.sh\n"
|
||||
"down %1/update-resolv-conf.sh\n")
|
||||
.arg(qApp->applicationDirPath());
|
||||
|
||||
config.append(dnsConf);
|
||||
#endif
|
||||
@@ -168,23 +178,23 @@ QString OpenVpnConfigurator::processConfigWithExportSettings(QString jsonConfig)
|
||||
return QJsonDocument(json).toJson();
|
||||
}
|
||||
|
||||
ErrorCode OpenVpnConfigurator::signCert(DockerContainer container,
|
||||
const ServerCredentials &credentials, QString clientId)
|
||||
ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId)
|
||||
{
|
||||
QString script_import = QString("sudo docker exec -i %1 bash -c \"cd /opt/amnezia/openvpn && "
|
||||
"easyrsa import-req %2/%3.req %3\"")
|
||||
.arg(ContainerProps::containerToString(container))
|
||||
.arg(amnezia::protocols::openvpn::clientsDirPath)
|
||||
.arg(clientId);
|
||||
"easyrsa import-req %2/%3.req %3\"")
|
||||
.arg(ContainerProps::containerToString(container))
|
||||
.arg(amnezia::protocols::openvpn::clientsDirPath)
|
||||
.arg(clientId);
|
||||
|
||||
QString script_sign = QString("sudo docker exec -i %1 bash -c \"export EASYRSA_BATCH=1; cd /opt/amnezia/openvpn && "
|
||||
"easyrsa sign-req client %2\"")
|
||||
.arg(ContainerProps::containerToString(container))
|
||||
.arg(clientId);
|
||||
"easyrsa sign-req client %2\"")
|
||||
.arg(ContainerProps::containerToString(container))
|
||||
.arg(clientId);
|
||||
|
||||
ServerController serverController(m_settings);
|
||||
QStringList scriptList {script_import, script_sign};
|
||||
QString script = serverController.replaceVars(scriptList.join("\n"), serverController.genVarsForScript(credentials, container));
|
||||
QStringList scriptList { script_import, script_sign };
|
||||
QString script = serverController.replaceVars(scriptList.join("\n"),
|
||||
serverController.genVarsForScript(credentials, container));
|
||||
|
||||
return serverController.runScript(credentials, script);
|
||||
}
|
||||
@@ -194,18 +204,17 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
|
||||
ConnectionData connData;
|
||||
connData.clientId = Utils::getRandomString(32);
|
||||
|
||||
int ret = 0;
|
||||
int nVersion = 1;
|
||||
int ret = 0;
|
||||
int nVersion = 1;
|
||||
|
||||
QByteArray clientIdUtf8 = connData.clientId.toUtf8();
|
||||
|
||||
EVP_PKEY * pKey = EVP_PKEY_new();
|
||||
EVP_PKEY *pKey = EVP_PKEY_new();
|
||||
q_check_ptr(pKey);
|
||||
RSA * rsa = RSA_generate_key(2048, RSA_F4, nullptr, nullptr);
|
||||
RSA *rsa = RSA_generate_key(2048, RSA_F4, nullptr, nullptr);
|
||||
q_check_ptr(rsa);
|
||||
EVP_PKEY_assign_RSA(pKey, rsa);
|
||||
|
||||
|
||||
// 2. set version of x509 req
|
||||
X509_REQ *x509_req = X509_REQ_new();
|
||||
ret = X509_REQ_set_version(x509_req, nVersion);
|
||||
@@ -219,16 +228,14 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
|
||||
// 3. set subject of x509 req
|
||||
X509_NAME *x509_name = X509_REQ_get_subject_name(x509_req);
|
||||
|
||||
X509_NAME_add_entry_by_txt(x509_name, "C", MBSTRING_ASC,
|
||||
(unsigned char *)"ORG", -1, -1, 0);
|
||||
X509_NAME_add_entry_by_txt(x509_name, "O", MBSTRING_ASC,
|
||||
(unsigned char *)"", -1, -1, 0);
|
||||
X509_NAME_add_entry_by_txt(x509_name, "C", MBSTRING_ASC, (unsigned char *)"ORG", -1, -1, 0);
|
||||
X509_NAME_add_entry_by_txt(x509_name, "O", MBSTRING_ASC, (unsigned char *)"", -1, -1, 0);
|
||||
X509_NAME_add_entry_by_txt(x509_name, "CN", MBSTRING_ASC,
|
||||
reinterpret_cast<unsigned char const *>(clientIdUtf8.data()), clientIdUtf8.size(), -1, 0);
|
||||
|
||||
// 4. set public key of x509 req
|
||||
ret = X509_REQ_set_pubkey(x509_req, pKey);
|
||||
if (ret != 1){
|
||||
if (ret != 1) {
|
||||
qWarning() << "Could not set pubkey!";
|
||||
X509_REQ_free(x509_req);
|
||||
EVP_PKEY_free(pKey);
|
||||
@@ -236,8 +243,8 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
|
||||
}
|
||||
|
||||
// 5. set sign key of x509 req
|
||||
ret = X509_REQ_sign(x509_req, pKey, EVP_sha256()); // return x509_req->signature->length
|
||||
if (ret <= 0){
|
||||
ret = X509_REQ_sign(x509_req, pKey, EVP_sha256()); // return x509_req->signature->length
|
||||
if (ret <= 0) {
|
||||
qWarning() << "Could not sign request!";
|
||||
X509_REQ_free(x509_req);
|
||||
EVP_PKEY_free(pKey);
|
||||
@@ -245,10 +252,9 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
|
||||
}
|
||||
|
||||
// save private key
|
||||
BIO * bp_private = BIO_new(BIO_s_mem());
|
||||
BIO *bp_private = BIO_new(BIO_s_mem());
|
||||
q_check_ptr(bp_private);
|
||||
if (PEM_write_bio_PrivateKey(bp_private, pKey, nullptr, nullptr, 0, nullptr, nullptr) != 1)
|
||||
{
|
||||
if (PEM_write_bio_PrivateKey(bp_private, pKey, nullptr, nullptr, 0, nullptr, nullptr) != 1) {
|
||||
qFatal("PEM_write_bio_PrivateKey");
|
||||
EVP_PKEY_free(pKey);
|
||||
BIO_free_all(bp_private);
|
||||
@@ -256,7 +262,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
|
||||
return connData;
|
||||
}
|
||||
|
||||
const char * buffer = nullptr;
|
||||
const char *buffer = nullptr;
|
||||
size_t size = BIO_get_mem_data(bp_private, &buffer);
|
||||
q_check_ptr(buffer);
|
||||
connData.privKey = QByteArray(buffer, size);
|
||||
@@ -270,7 +276,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
|
||||
BIO_free_all(bp_private);
|
||||
|
||||
// save req
|
||||
BIO * bio_req = BIO_new(BIO_s_mem());
|
||||
BIO *bio_req = BIO_new(BIO_s_mem());
|
||||
PEM_write_bio_X509_REQ(bio_req, x509_req);
|
||||
|
||||
BUF_MEM *bio_buf;
|
||||
@@ -278,7 +284,6 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
|
||||
connData.request = QByteArray(bio_buf->data, bio_buf->length);
|
||||
BIO_free(bio_req);
|
||||
|
||||
|
||||
EVP_PKEY_free(pKey); // this will also free the rsa key
|
||||
|
||||
return connData;
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
#include "ssh_configurator.h"
|
||||
#include <QApplication>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
#include <QTemporaryDir>
|
||||
#include <QDebug>
|
||||
#include <QTemporaryFile>
|
||||
#include <QThread>
|
||||
#include <QObject>
|
||||
#include <QTextEdit>
|
||||
#include <QPlainTextEdit>
|
||||
#include <qtimer.h>
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
#include <QGuiApplication>
|
||||
#else
|
||||
#include <QApplication>
|
||||
#endif
|
||||
|
||||
#include "core/server_defs.h"
|
||||
#include "utilities.h"
|
||||
|
||||
|
||||
SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
|
||||
ConfiguratorBase(settings, parent)
|
||||
SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: ConfiguratorBase(settings, parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QString SshConfigurator::convertOpenSShKey(const QString &key)
|
||||
@@ -28,23 +29,30 @@ QString SshConfigurator::convertOpenSShKey(const QString &key)
|
||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||
|
||||
QTemporaryFile tmp;
|
||||
#ifdef QT_DEBUG
|
||||
#ifdef QT_DEBUG
|
||||
tmp.setAutoRemove(false);
|
||||
#endif
|
||||
#endif
|
||||
tmp.open();
|
||||
tmp.write(key.toUtf8());
|
||||
tmp.close();
|
||||
|
||||
// ssh-keygen -p -P "" -N "" -m pem -f id_ssh
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#ifdef Q_OS_WIN
|
||||
p.setProcessEnvironment(prepareEnv());
|
||||
p.setProgram("cmd.exe");
|
||||
p.setNativeArguments(QString("/C \"ssh-keygen.exe -p -P \"\" -N \"\" -m pem -f \"%1\"\"").arg(tmp.fileName()));
|
||||
#else
|
||||
#else
|
||||
p.setProgram("ssh-keygen");
|
||||
p.setArguments(QStringList() << "-p" << "-P" << "" << "-N" << "" << "-m" << "pem" << "-f" << tmp.fileName());
|
||||
#endif
|
||||
p.setArguments(QStringList() << "-p"
|
||||
<< "-P"
|
||||
<< ""
|
||||
<< "-N"
|
||||
<< ""
|
||||
<< "-m"
|
||||
<< "pem"
|
||||
<< "-f" << tmp.fileName());
|
||||
#endif
|
||||
|
||||
p.start();
|
||||
p.waitForFinished();
|
||||
@@ -65,22 +73,21 @@ void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
|
||||
QProcess *p = new QProcess();
|
||||
p->setProcessChannelMode(QProcess::SeparateChannels);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#ifdef Q_OS_WIN
|
||||
p->setProcessEnvironment(prepareEnv());
|
||||
p->setProgram(qApp->applicationDirPath() + "\\cygwin\\putty.exe");
|
||||
|
||||
if (credentials.password.contains("PRIVATE KEY")) {
|
||||
if (credentials.secretData.contains("PRIVATE KEY")) {
|
||||
// todo: connect by key
|
||||
// p->setNativeArguments(QString("%1@%2")
|
||||
// .arg(credentials.userName).arg(credentials.hostName).arg(credentials.password));
|
||||
// p->setNativeArguments(QString("%1@%2")
|
||||
// .arg(credentials.userName).arg(credentials.hostName).arg(credentials.secretData));
|
||||
} else {
|
||||
p->setNativeArguments(
|
||||
QString("%1@%2 -pw %3").arg(credentials.userName).arg(credentials.hostName).arg(credentials.secretData));
|
||||
}
|
||||
else {
|
||||
p->setNativeArguments(QString("%1@%2 -pw %3")
|
||||
.arg(credentials.userName).arg(credentials.hostName).arg(credentials.password));
|
||||
}
|
||||
#else
|
||||
#else
|
||||
p->setProgram("/bin/bash");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
p->startDetached();
|
||||
#endif
|
||||
@@ -95,11 +102,11 @@ QProcessEnvironment SshConfigurator::prepareEnv()
|
||||
pathEnvVar.clear();
|
||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
|
||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;");
|
||||
#else
|
||||
#elif defined(Q_OS_MACX)
|
||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
|
||||
#endif
|
||||
|
||||
env.insert("PATH", pathEnvVar);
|
||||
//qDebug().noquote() << "ENV PATH" << pathEnvVar;
|
||||
// qDebug().noquote() << "ENV PATH" << pathEnvVar;
|
||||
return env;
|
||||
}
|
||||
|
||||
@@ -1,30 +1,27 @@
|
||||
#include "wireguard_configurator.h"
|
||||
#include <QApplication>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
#include <QTemporaryDir>
|
||||
#include <QDebug>
|
||||
#include <QTemporaryFile>
|
||||
#include <QJsonDocument>
|
||||
|
||||
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "core/server_defs.h"
|
||||
#include "core/scripts_registry.h"
|
||||
#include "utilities.h"
|
||||
#include "core/server_defs.h"
|
||||
#include "core/servercontroller.h"
|
||||
#include "settings.h"
|
||||
#include "utilities.h"
|
||||
|
||||
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
|
||||
ConfiguratorBase(settings, parent)
|
||||
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: ConfiguratorBase(settings, parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
|
||||
@@ -36,37 +33,40 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
|
||||
|
||||
unsigned char buff[EDDSA_KEY_LENGTH];
|
||||
int ret = RAND_priv_bytes(buff, EDDSA_KEY_LENGTH);
|
||||
if (ret <=0) return connData;
|
||||
if (ret <= 0)
|
||||
return connData;
|
||||
|
||||
EVP_PKEY * pKey = EVP_PKEY_new();
|
||||
EVP_PKEY *pKey = EVP_PKEY_new();
|
||||
q_check_ptr(pKey);
|
||||
pKey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, &buff[0], EDDSA_KEY_LENGTH);
|
||||
|
||||
|
||||
size_t keySize = EDDSA_KEY_LENGTH;
|
||||
|
||||
// save private key
|
||||
unsigned char priv[EDDSA_KEY_LENGTH];
|
||||
EVP_PKEY_get_raw_private_key(pKey, priv, &keySize);
|
||||
connData.clientPrivKey = QByteArray::fromRawData((char*)priv, keySize).toBase64();
|
||||
connData.clientPrivKey = QByteArray::fromRawData((char *)priv, keySize).toBase64();
|
||||
|
||||
// save public key
|
||||
unsigned char pub[EDDSA_KEY_LENGTH];
|
||||
EVP_PKEY_get_raw_public_key(pKey, pub, &keySize);
|
||||
connData.clientPubKey = QByteArray::fromRawData((char*)pub, keySize).toBase64();
|
||||
connData.clientPubKey = QByteArray::fromRawData((char *)pub, keySize).toBase64();
|
||||
|
||||
return connData;
|
||||
}
|
||||
|
||||
WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
|
||||
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
|
||||
DockerContainer container,
|
||||
const QJsonObject &containerConfig,
|
||||
ErrorCode *errorCode)
|
||||
{
|
||||
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
|
||||
connData.host = credentials.hostName;
|
||||
connData.port = containerConfig.value(config_key::port).toString(protocols::wireguard::defaultPort);
|
||||
|
||||
if (connData.clientPrivKey.isEmpty() || connData.clientPubKey.isEmpty()) {
|
||||
if (errorCode) *errorCode = ErrorCode::InternalError;
|
||||
if (errorCode)
|
||||
*errorCode = ErrorCode::InternalError;
|
||||
return connData;
|
||||
}
|
||||
|
||||
@@ -96,22 +96,24 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
||||
// Calc next IP address
|
||||
if (ips.isEmpty()) {
|
||||
nextIpNumber = "2";
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
int next = ips.last().split(".").last().toInt() + 1;
|
||||
if (next > 254) {
|
||||
if (errorCode) *errorCode = ErrorCode::AddressPoolError;
|
||||
if (errorCode)
|
||||
*errorCode = ErrorCode::AddressPoolError;
|
||||
return connData;
|
||||
}
|
||||
nextIpNumber = QString::number(next);
|
||||
}
|
||||
}
|
||||
|
||||
QString subnetIp = containerConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
|
||||
QString subnetIp =
|
||||
containerConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
|
||||
{
|
||||
QStringList l = subnetIp.split(".", Qt::SkipEmptyParts);
|
||||
if (l.isEmpty()) {
|
||||
if (errorCode) *errorCode = ErrorCode::AddressPoolError;
|
||||
if (errorCode)
|
||||
*errorCode = ErrorCode::AddressPoolError;
|
||||
return connData;
|
||||
}
|
||||
l.removeLast();
|
||||
@@ -121,52 +123,60 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
||||
}
|
||||
|
||||
// Get keys
|
||||
connData.serverPubKey = serverController.getTextFileFromContainer(container, credentials, amnezia::protocols::wireguard::serverPublicKeyPath, &e);
|
||||
connData.serverPubKey = serverController.getTextFileFromContainer(
|
||||
container, credentials, amnezia::protocols::wireguard::serverPublicKeyPath, &e);
|
||||
connData.serverPubKey.replace("\n", "");
|
||||
if (e) {
|
||||
if (errorCode) *errorCode = e;
|
||||
if (errorCode)
|
||||
*errorCode = e;
|
||||
return connData;
|
||||
}
|
||||
|
||||
connData.pskKey = serverController.getTextFileFromContainer(container, credentials, amnezia::protocols::wireguard::serverPskKeyPath, &e);
|
||||
connData.pskKey = serverController.getTextFileFromContainer(container, credentials,
|
||||
amnezia::protocols::wireguard::serverPskKeyPath, &e);
|
||||
connData.pskKey.replace("\n", "");
|
||||
|
||||
if (e) {
|
||||
if (errorCode) *errorCode = e;
|
||||
if (errorCode)
|
||||
*errorCode = e;
|
||||
return connData;
|
||||
}
|
||||
|
||||
// Add client to config
|
||||
QString configPart = QString(
|
||||
"[Peer]\n"
|
||||
"PublicKey = %1\n"
|
||||
"PresharedKey = %2\n"
|
||||
"AllowedIPs = %3/32\n\n").
|
||||
arg(connData.clientPubKey).
|
||||
arg(connData.pskKey).
|
||||
arg(connData.clientIP);
|
||||
QString configPart = QString("[Peer]\n"
|
||||
"PublicKey = %1\n"
|
||||
"PresharedKey = %2\n"
|
||||
"AllowedIPs = %3/32\n\n")
|
||||
.arg(connData.clientPubKey)
|
||||
.arg(connData.pskKey)
|
||||
.arg(connData.clientIP);
|
||||
|
||||
e = serverController.uploadTextFileToContainer(container, credentials, configPart,
|
||||
protocols::wireguard::serverConfigPath, libssh::SftpOverwriteMode::SftpAppendToExisting);
|
||||
protocols::wireguard::serverConfigPath,
|
||||
libssh::SftpOverwriteMode::SftpAppendToExisting);
|
||||
|
||||
if (e) {
|
||||
if (errorCode) *errorCode = e;
|
||||
if (errorCode)
|
||||
*errorCode = e;
|
||||
return connData;
|
||||
}
|
||||
|
||||
e = serverController.runScript(credentials,
|
||||
serverController.replaceVars("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip /opt/amnezia/wireguard/wg0.conf)'",
|
||||
serverController.genVarsForScript(credentials, container)));
|
||||
e = serverController.runScript(
|
||||
credentials,
|
||||
serverController.replaceVars("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick "
|
||||
"strip /opt/amnezia/wireguard/wg0.conf)'",
|
||||
serverController.genVarsForScript(credentials, container)));
|
||||
|
||||
return connData;
|
||||
}
|
||||
|
||||
QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &credentials,
|
||||
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
|
||||
QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode *errorCode)
|
||||
{
|
||||
ServerController serverController(m_settings);
|
||||
QString config = serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::wireguard_template, container),
|
||||
serverController.genVarsForScript(credentials, container, containerConfig));
|
||||
QString config =
|
||||
serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::wireguard_template, container),
|
||||
serverController.genVarsForScript(credentials, container, containerConfig));
|
||||
|
||||
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
|
||||
if (errorCode && *errorCode) {
|
||||
|
||||
@@ -8,18 +8,23 @@ QDebug operator<<(QDebug debug, const amnezia::DockerContainer &c)
|
||||
return debug;
|
||||
}
|
||||
|
||||
amnezia::DockerContainer ContainerProps::containerFromString(const QString &container){
|
||||
amnezia::DockerContainer ContainerProps::containerFromString(const QString &container)
|
||||
{
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<DockerContainer>();
|
||||
for (int i = 0; i < metaEnum.keyCount(); ++i) {
|
||||
DockerContainer c = static_cast<DockerContainer>(i);
|
||||
if (container == containerToString(c)) return c;
|
||||
if (container == containerToString(c))
|
||||
return c;
|
||||
}
|
||||
return DockerContainer::None;
|
||||
}
|
||||
|
||||
QString ContainerProps::containerToString(amnezia::DockerContainer c){
|
||||
if (c == DockerContainer::None) return "none";
|
||||
if (c == DockerContainer::Cloak) return "amnezia-openvpn-cloak";
|
||||
QString ContainerProps::containerToString(amnezia::DockerContainer c)
|
||||
{
|
||||
if (c == DockerContainer::None)
|
||||
return "none";
|
||||
if (c == DockerContainer::Cloak)
|
||||
return "amnezia-openvpn-cloak";
|
||||
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<DockerContainer>();
|
||||
QString containerKey = metaEnum.valueToKey(static_cast<int>(c));
|
||||
@@ -27,9 +32,12 @@ QString ContainerProps::containerToString(amnezia::DockerContainer c){
|
||||
return "amnezia-" + containerKey.toLower();
|
||||
}
|
||||
|
||||
QString ContainerProps::containerTypeToString(amnezia::DockerContainer c){
|
||||
if (c == DockerContainer::None) return "none";
|
||||
if (c == DockerContainer::Ipsec) return "ikev2";
|
||||
QString ContainerProps::containerTypeToString(amnezia::DockerContainer c)
|
||||
{
|
||||
if (c == DockerContainer::None)
|
||||
return "none";
|
||||
if (c == DockerContainer::Ipsec)
|
||||
return "ikev2";
|
||||
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<DockerContainer>();
|
||||
QString containerKey = metaEnum.valueToKey(static_cast<int>(c));
|
||||
@@ -40,29 +48,21 @@ QString ContainerProps::containerTypeToString(amnezia::DockerContainer c){
|
||||
QVector<amnezia::Proto> ContainerProps::protocolsForContainer(amnezia::DockerContainer container)
|
||||
{
|
||||
switch (container) {
|
||||
case DockerContainer::None:
|
||||
return { };
|
||||
case DockerContainer::None: return {};
|
||||
|
||||
case DockerContainer::OpenVpn:
|
||||
return { Proto::OpenVpn };
|
||||
case DockerContainer::OpenVpn: return { Proto::OpenVpn };
|
||||
|
||||
case DockerContainer::ShadowSocks:
|
||||
return { Proto::OpenVpn, Proto::ShadowSocks };
|
||||
case DockerContainer::ShadowSocks: return { Proto::OpenVpn, Proto::ShadowSocks };
|
||||
|
||||
case DockerContainer::Cloak:
|
||||
return { Proto::OpenVpn, Proto::ShadowSocks, Proto::Cloak };
|
||||
case DockerContainer::Cloak: return { Proto::OpenVpn, Proto::ShadowSocks, Proto::Cloak };
|
||||
|
||||
case DockerContainer::Ipsec:
|
||||
return { Proto::Ikev2 /*, Protocol::L2tp */};
|
||||
case DockerContainer::Ipsec: return { Proto::Ikev2 /*, Protocol::L2tp */ };
|
||||
|
||||
case DockerContainer::Dns:
|
||||
return { };
|
||||
case DockerContainer::Dns: return {};
|
||||
|
||||
case DockerContainer::Sftp:
|
||||
return { Proto::Sftp};
|
||||
case DockerContainer::Sftp: return { Proto::Sftp };
|
||||
|
||||
default:
|
||||
return { defaultProtocol(container) };
|
||||
default: return { defaultProtocol(container) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,70 +79,94 @@ QList<DockerContainer> ContainerProps::allContainers()
|
||||
|
||||
QMap<DockerContainer, QString> ContainerProps::containerHumanNames()
|
||||
{
|
||||
return {
|
||||
{DockerContainer::None, "Not installed"},
|
||||
{DockerContainer::OpenVpn, "OpenVPN"},
|
||||
{DockerContainer::ShadowSocks, "OpenVpn over ShadowSocks"},
|
||||
{DockerContainer::Cloak, "OpenVpn over Cloak"},
|
||||
{DockerContainer::WireGuard, "WireGuard"},
|
||||
{DockerContainer::Ipsec, QObject::tr("IPsec")},
|
||||
return { { DockerContainer::None, "Not installed" },
|
||||
{ DockerContainer::OpenVpn, "OpenVPN" },
|
||||
{ DockerContainer::ShadowSocks, "ShadowSocks" },
|
||||
{ DockerContainer::Cloak, "OpenVPN over Cloak" },
|
||||
{ DockerContainer::WireGuard, "WireGuard" },
|
||||
{ DockerContainer::Ipsec, QObject::tr("IPsec") },
|
||||
|
||||
{DockerContainer::TorWebSite, QObject::tr("Web site in Tor network")},
|
||||
{DockerContainer::Dns, QObject::tr("DNS Service")},
|
||||
//{DockerContainer::FileShare, QObject::tr("SMB file sharing service")},
|
||||
{DockerContainer::Sftp, QObject::tr("Sftp file sharing service")}
|
||||
};
|
||||
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
|
||||
{ DockerContainer::Dns, QObject::tr("Amnezia DNS") },
|
||||
//{DockerContainer::FileShare, QObject::tr("SMB file sharing service")},
|
||||
{ DockerContainer::Sftp, QObject::tr("Sftp file sharing service") } };
|
||||
}
|
||||
|
||||
QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
|
||||
{
|
||||
return {
|
||||
{DockerContainer::OpenVpn, QObject::tr("OpenVPN container")},
|
||||
{DockerContainer::ShadowSocks, QObject::tr("Container with OpenVpn and ShadowSocks")},
|
||||
{DockerContainer::Cloak, QObject::tr("Container with OpenVpn and ShadowSocks protocols "
|
||||
"configured with traffic masking by Cloak plugin")},
|
||||
{DockerContainer::WireGuard, QObject::tr("WireGuard container")},
|
||||
{DockerContainer::Ipsec, QObject::tr("IPsec container")},
|
||||
return { { DockerContainer::OpenVpn,
|
||||
QObject::tr("OpenVPN is the most popular VPN protocol, with flexible configuration options. It uses its "
|
||||
"own security protocol with SSL/TLS for key exchange.") },
|
||||
{ DockerContainer::ShadowSocks,
|
||||
QObject::tr("ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but is "
|
||||
"recognised by analysis systems in some highly censored regions.") },
|
||||
{ DockerContainer::Cloak,
|
||||
QObject::tr("OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against "
|
||||
"active-probbing detection. Ideal for bypassing blocking in regions with the highest levels "
|
||||
"of censorship.") },
|
||||
{ DockerContainer::WireGuard,
|
||||
QObject::tr("WireGuard - New popular VPN protocol with high performance, high speed and low power "
|
||||
"consumption. Recommended for regions with low levels of censorship.") },
|
||||
{ DockerContainer::Ipsec,
|
||||
QObject::tr("IKEv2 - Modern stable protocol, a bit faster than others, restores connection after "
|
||||
"signal loss. It has native support on the latest versions of Android and iOS.") },
|
||||
|
||||
{DockerContainer::TorWebSite, QObject::tr("Web site in Tor network")},
|
||||
{DockerContainer::Dns, QObject::tr("DNS Service")},
|
||||
//{DockerContainer::FileShare, QObject::tr("SMB file sharing service - is Window file sharing protocol")},
|
||||
{DockerContainer::Sftp, QObject::tr("Sftp file sharing service - is secure FTP service")}
|
||||
};
|
||||
{ DockerContainer::TorWebSite, QObject::tr("Deploy a WordPress site on the Tor network in two clicks.") },
|
||||
{ DockerContainer::Dns,
|
||||
QObject::tr("Replace the current DNS server with your own. This will increase your privacy level.") },
|
||||
//{DockerContainer::FileShare, QObject::tr("SMB file sharing service - is Window file sharing protocol")},
|
||||
{ DockerContainer::Sftp,
|
||||
QObject::tr("Creates a file vault on your server to securely store and transfer files.") } };
|
||||
}
|
||||
|
||||
QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
|
||||
{
|
||||
return { { DockerContainer::OpenVpn, QObject::tr("OpenVPN container") },
|
||||
{ DockerContainer::ShadowSocks, QObject::tr("Container with OpenVpn and ShadowSocks") },
|
||||
{ DockerContainer::Cloak,
|
||||
QObject::tr("Container with OpenVpn and ShadowSocks protocols "
|
||||
"configured with traffic masking by Cloak plugin") },
|
||||
{ DockerContainer::WireGuard, QObject::tr("WireGuard container") },
|
||||
{ DockerContainer::Ipsec, QObject::tr("IPsec container") },
|
||||
|
||||
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
|
||||
{ DockerContainer::Dns, QObject::tr("DNS Service") },
|
||||
//{DockerContainer::FileShare, QObject::tr("SMB file sharing service - is Window file sharing protocol")},
|
||||
{ DockerContainer::Sftp, QObject::tr("Sftp file sharing service - is secure FTP service") } };
|
||||
}
|
||||
|
||||
amnezia::ServiceType ContainerProps::containerService(DockerContainer c)
|
||||
{
|
||||
switch (c) {
|
||||
case DockerContainer::None : return ServiceType::None;
|
||||
case DockerContainer::OpenVpn : return ServiceType::Vpn;
|
||||
case DockerContainer::Cloak : return ServiceType::Vpn;
|
||||
case DockerContainer::ShadowSocks : return ServiceType::Vpn;
|
||||
case DockerContainer::WireGuard : return ServiceType::Vpn;
|
||||
case DockerContainer::Ipsec : return ServiceType::Vpn;
|
||||
case DockerContainer::TorWebSite : return ServiceType::Other;
|
||||
case DockerContainer::Dns : return ServiceType::Other;
|
||||
//case DockerContainer::FileShare : return ServiceType::Other;
|
||||
case DockerContainer::Sftp : return ServiceType::Other;
|
||||
default: return ServiceType::Other;
|
||||
case DockerContainer::None: return ServiceType::None;
|
||||
case DockerContainer::OpenVpn: return ServiceType::Vpn;
|
||||
case DockerContainer::Cloak: return ServiceType::Vpn;
|
||||
case DockerContainer::ShadowSocks: return ServiceType::Vpn;
|
||||
case DockerContainer::WireGuard: return ServiceType::Vpn;
|
||||
case DockerContainer::Ipsec: return ServiceType::Vpn;
|
||||
case DockerContainer::TorWebSite: return ServiceType::Other;
|
||||
case DockerContainer::Dns: return ServiceType::Other;
|
||||
// case DockerContainer::FileShare : return ServiceType::Other;
|
||||
case DockerContainer::Sftp: return ServiceType::Other;
|
||||
default: return ServiceType::Other;
|
||||
}
|
||||
}
|
||||
|
||||
Proto ContainerProps::defaultProtocol(DockerContainer c)
|
||||
{
|
||||
switch (c) {
|
||||
case DockerContainer::None : return Proto::Any;
|
||||
case DockerContainer::OpenVpn : return Proto::OpenVpn;
|
||||
case DockerContainer::Cloak : return Proto::Cloak;
|
||||
case DockerContainer::ShadowSocks : return Proto::ShadowSocks;
|
||||
case DockerContainer::WireGuard : return Proto::WireGuard;
|
||||
case DockerContainer::Ipsec : return Proto::Ikev2;
|
||||
case DockerContainer::None: return Proto::Any;
|
||||
case DockerContainer::OpenVpn: return Proto::OpenVpn;
|
||||
case DockerContainer::Cloak: return Proto::Cloak;
|
||||
case DockerContainer::ShadowSocks: return Proto::ShadowSocks;
|
||||
case DockerContainer::WireGuard: return Proto::WireGuard;
|
||||
case DockerContainer::Ipsec: return Proto::Ikev2;
|
||||
|
||||
case DockerContainer::TorWebSite : return Proto::TorWebSite;
|
||||
case DockerContainer::Dns : return Proto::Dns;
|
||||
//case DockerContainer::FileShare : return Protocol::FileShare;
|
||||
case DockerContainer::Sftp : return Proto::Sftp;
|
||||
default: return Proto::Any;
|
||||
case DockerContainer::TorWebSite: return Proto::TorWebSite;
|
||||
case DockerContainer::Dns: return Proto::Dns;
|
||||
// case DockerContainer::FileShare : return Protocol::FileShare;
|
||||
case DockerContainer::Sftp: return Proto::Sftp;
|
||||
default: return Proto::Any;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,22 +175,23 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
||||
#ifdef Q_OS_WINDOWS
|
||||
return true;
|
||||
|
||||
#elif defined (Q_OS_IOS)
|
||||
#elif defined(Q_OS_IOS)
|
||||
switch (c) {
|
||||
case DockerContainer::WireGuard: return true;
|
||||
case DockerContainer::OpenVpn: return true;
|
||||
case DockerContainer::Cloak: return true;
|
||||
// case DockerContainer::ShadowSocks: return true;
|
||||
case DockerContainer::Cloak:
|
||||
return true;
|
||||
// case DockerContainer::ShadowSocks: return true;
|
||||
default: return false;
|
||||
}
|
||||
#elif defined (Q_OS_MAC)
|
||||
#elif defined(Q_OS_MAC)
|
||||
switch (c) {
|
||||
case DockerContainer::WireGuard: return true;
|
||||
case DockerContainer::Ipsec: return false;
|
||||
default: return true;
|
||||
}
|
||||
|
||||
#elif defined (Q_OS_ANDROID)
|
||||
#elif defined(Q_OS_ANDROID)
|
||||
switch (c) {
|
||||
case DockerContainer::WireGuard: return true;
|
||||
case DockerContainer::OpenVpn: return true;
|
||||
@@ -175,7 +200,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
||||
default: return false;
|
||||
}
|
||||
|
||||
#elif defined (Q_OS_LINUX)
|
||||
#elif defined(Q_OS_LINUX)
|
||||
switch (c) {
|
||||
case DockerContainer::WireGuard: return true;
|
||||
case DockerContainer::Ipsec: return false;
|
||||
@@ -183,14 +208,64 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
||||
}
|
||||
|
||||
#else
|
||||
return false;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
QStringList ContainerProps::fixedPortsForContainer(DockerContainer c)
|
||||
{
|
||||
switch (c) {
|
||||
case DockerContainer::Ipsec : return QStringList{"500", "4500"};
|
||||
default: return {};
|
||||
case DockerContainer::Ipsec: return QStringList { "500", "4500" };
|
||||
default: return {};
|
||||
}
|
||||
}
|
||||
|
||||
bool ContainerProps::isEasySetupContainer(DockerContainer container)
|
||||
{
|
||||
switch (container) {
|
||||
case DockerContainer::WireGuard: return true;
|
||||
case DockerContainer::Cloak: return true;
|
||||
case DockerContainer::OpenVpn: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
QString ContainerProps::easySetupHeader(DockerContainer container)
|
||||
{
|
||||
switch (container) {
|
||||
case DockerContainer::WireGuard: return tr("Low");
|
||||
case DockerContainer::Cloak: return tr("High");
|
||||
case DockerContainer::OpenVpn: return tr("Medium");
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
QString ContainerProps::easySetupDescription(DockerContainer container)
|
||||
{
|
||||
switch (container) {
|
||||
case DockerContainer::WireGuard: return tr("I just want to increase the level of privacy");
|
||||
case DockerContainer::Cloak: return tr("Many foreign websites and VPN providers are blocked");
|
||||
case DockerContainer::OpenVpn: return tr("Some foreign sites are blocked, but VPN providers are not blocked");
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
int ContainerProps::easySetupOrder(DockerContainer container)
|
||||
{
|
||||
switch (container) {
|
||||
case DockerContainer::WireGuard: return 1;
|
||||
case DockerContainer::Cloak: return 3;
|
||||
case DockerContainer::OpenVpn: return 2;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool ContainerProps::isShareable(DockerContainer container)
|
||||
{
|
||||
switch (container) {
|
||||
case DockerContainer::TorWebSite: return false;
|
||||
case DockerContainer::Dns: return false;
|
||||
case DockerContainer::Sftp: return false;
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,68 +8,72 @@
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
namespace amnezia {
|
||||
|
||||
namespace ContainerEnumNS {
|
||||
Q_NAMESPACE
|
||||
enum DockerContainer {
|
||||
None = 0,
|
||||
OpenVpn,
|
||||
ShadowSocks,
|
||||
Cloak,
|
||||
WireGuard,
|
||||
Ipsec,
|
||||
|
||||
//non-vpn
|
||||
TorWebSite,
|
||||
Dns,
|
||||
//FileShare,
|
||||
Sftp
|
||||
};
|
||||
Q_ENUM_NS(DockerContainer)
|
||||
} // namespace ContainerEnumNS
|
||||
|
||||
using namespace ContainerEnumNS;
|
||||
using namespace ProtocolEnumNS;
|
||||
|
||||
class ContainerProps : public QObject
|
||||
namespace amnezia
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_INVOKABLE static amnezia::DockerContainer containerFromString(const QString &container);
|
||||
Q_INVOKABLE static QString containerToString(amnezia::DockerContainer container);
|
||||
Q_INVOKABLE static QString containerTypeToString(amnezia::DockerContainer c);
|
||||
namespace ContainerEnumNS
|
||||
{
|
||||
Q_NAMESPACE
|
||||
enum DockerContainer {
|
||||
None = 0,
|
||||
OpenVpn,
|
||||
ShadowSocks,
|
||||
Cloak,
|
||||
WireGuard,
|
||||
Ipsec,
|
||||
|
||||
Q_INVOKABLE static QList<amnezia::DockerContainer> allContainers();
|
||||
// non-vpn
|
||||
TorWebSite,
|
||||
Dns,
|
||||
// FileShare,
|
||||
Sftp
|
||||
};
|
||||
Q_ENUM_NS(DockerContainer)
|
||||
} // namespace ContainerEnumNS
|
||||
|
||||
Q_INVOKABLE static QMap<amnezia::DockerContainer, QString> containerHumanNames();
|
||||
Q_INVOKABLE static QMap<amnezia::DockerContainer, QString> containerDescriptions();
|
||||
using namespace ContainerEnumNS;
|
||||
using namespace ProtocolEnumNS;
|
||||
|
||||
// these protocols will be displayed in container settings
|
||||
Q_INVOKABLE static QVector<amnezia::Proto> protocolsForContainer(amnezia::DockerContainer container);
|
||||
class ContainerProps : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_INVOKABLE static amnezia::ServiceType containerService(amnezia::DockerContainer c);
|
||||
public:
|
||||
Q_INVOKABLE static amnezia::DockerContainer containerFromString(const QString &container);
|
||||
Q_INVOKABLE static QString containerToString(amnezia::DockerContainer container);
|
||||
Q_INVOKABLE static QString containerTypeToString(amnezia::DockerContainer c);
|
||||
|
||||
// binding between Docker container and main protocol of given container
|
||||
// it may be changed fot future containers :)
|
||||
Q_INVOKABLE static amnezia::Proto defaultProtocol(amnezia::DockerContainer c);
|
||||
Q_INVOKABLE static QList<amnezia::DockerContainer> allContainers();
|
||||
|
||||
Q_INVOKABLE static bool isSupportedByCurrentPlatform(amnezia::DockerContainer c);
|
||||
Q_INVOKABLE static QStringList fixedPortsForContainer(amnezia::DockerContainer c);
|
||||
};
|
||||
Q_INVOKABLE static QMap<amnezia::DockerContainer, QString> containerHumanNames();
|
||||
Q_INVOKABLE static QMap<amnezia::DockerContainer, QString> containerDescriptions();
|
||||
Q_INVOKABLE static QMap<amnezia::DockerContainer, QString> containerDetailedDescriptions();
|
||||
|
||||
// these protocols will be displayed in container settings
|
||||
Q_INVOKABLE static QVector<amnezia::Proto> protocolsForContainer(amnezia::DockerContainer container);
|
||||
|
||||
Q_INVOKABLE static amnezia::ServiceType containerService(amnezia::DockerContainer c);
|
||||
|
||||
static void declareQmlContainerEnum() {
|
||||
qmlRegisterUncreatableMetaObject(
|
||||
ContainerEnumNS::staticMetaObject,
|
||||
"ContainerEnum",
|
||||
1, 0,
|
||||
"ContainerEnum",
|
||||
"Error: only enums"
|
||||
);
|
||||
}
|
||||
// binding between Docker container and main protocol of given container
|
||||
// it may be changed fot future containers :)
|
||||
Q_INVOKABLE static amnezia::Proto defaultProtocol(amnezia::DockerContainer c);
|
||||
|
||||
Q_INVOKABLE static bool isSupportedByCurrentPlatform(amnezia::DockerContainer c);
|
||||
Q_INVOKABLE static QStringList fixedPortsForContainer(amnezia::DockerContainer c);
|
||||
|
||||
static bool isEasySetupContainer(amnezia::DockerContainer container);
|
||||
static QString easySetupHeader(amnezia::DockerContainer container);
|
||||
static QString easySetupDescription(amnezia::DockerContainer container);
|
||||
static int easySetupOrder(amnezia::DockerContainer container);
|
||||
|
||||
static bool isShareable(amnezia::DockerContainer container);
|
||||
};
|
||||
|
||||
static void declareQmlContainerEnum()
|
||||
{
|
||||
qmlRegisterUncreatableMetaObject(ContainerEnumNS::staticMetaObject, "ContainerEnum", 1, 0, "ContainerEnum",
|
||||
"Error: only enums");
|
||||
}
|
||||
|
||||
} // namespace amnezia
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@ struct ServerCredentials
|
||||
{
|
||||
QString hostName;
|
||||
QString userName;
|
||||
QString password;
|
||||
QString secretData;
|
||||
int port = 22;
|
||||
|
||||
bool isValid() const { return !hostName.isEmpty() && !userName.isEmpty() && !password.isEmpty() && port > 0; }
|
||||
bool isValid() const { return !hostName.isEmpty() && !userName.isEmpty() && !secretData.isEmpty() && port > 0; }
|
||||
};
|
||||
|
||||
enum ErrorCode
|
||||
@@ -37,7 +37,7 @@ enum ErrorCode
|
||||
|
||||
// Ssh connection errors
|
||||
SshRequsetDeniedError, SshInterruptedError, SshInternalError,
|
||||
SshPrivateKeyError, SshPrivateKeyFormatError,
|
||||
SshPrivateKeyError, SshPrivateKeyFormatError, SshTimeoutError,
|
||||
|
||||
// Ssh sftp errors
|
||||
SshSftpEofError, SshSftpNoSuchFileError, SshSftpPermissionDeniedError,
|
||||
@@ -69,7 +69,10 @@ enum ErrorCode
|
||||
OpenSslFailed,
|
||||
OpenVpnExecutableCrashed,
|
||||
ShadowSocksExecutableCrashed,
|
||||
CloakExecutableCrashed
|
||||
CloakExecutableCrashed,
|
||||
|
||||
// import and install errors
|
||||
ImportInvalidConfigError
|
||||
};
|
||||
|
||||
} // namespace amnezia
|
||||
|
||||
@@ -24,6 +24,7 @@ QString errorString(ErrorCode code){
|
||||
case(SshInternalError): return QObject::tr("Ssh internal error");
|
||||
case(SshPrivateKeyError): return QObject::tr("Invalid private key or invalid passphrase entered");
|
||||
case(SshPrivateKeyFormatError): return QObject::tr("The selected private key format is not supported, use openssh ED25519 key types or PEM key types");
|
||||
case(SshTimeoutError): return QObject::tr("Timeout connecting to server");
|
||||
|
||||
// Libssh sftp errors
|
||||
case(SshSftpEofError): return QObject::tr("Sftp error: End-of-file encountered");
|
||||
@@ -57,6 +58,8 @@ QString errorString(ErrorCode code){
|
||||
case (OpenVpnTapAdapterError): return QObject::tr("Can't setup OpenVPN TAP network adapter");
|
||||
case (AddressPoolError): return QObject::tr("VPN pool error: no available addresses");
|
||||
|
||||
case (ImportInvalidConfigError): return QObject::tr("The config does not contain any containers and credentiaks for connecting to the server");
|
||||
|
||||
case(InternalError):
|
||||
default:
|
||||
return QObject::tr("Internal error");
|
||||
|
||||
@@ -2,22 +2,21 @@
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QEventLoop>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QLoggingCategory>
|
||||
#include <QPointer>
|
||||
#include <QTimer>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
#include <QApplication>
|
||||
#include <QTemporaryFile>
|
||||
#include <QFileInfo>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <chrono>
|
||||
@@ -25,15 +24,14 @@
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
#include "logger.h"
|
||||
#include "scripts_registry.h"
|
||||
#include "server_defs.h"
|
||||
#include "settings.h"
|
||||
#include "scripts_registry.h"
|
||||
#include "utilities.h"
|
||||
|
||||
#include <configurators/vpn_configurator.h>
|
||||
|
||||
ServerController::ServerController(std::shared_ptr<Settings> settings, QObject *parent) :
|
||||
m_settings(settings)
|
||||
ServerController::ServerController(std::shared_ptr<Settings> settings, QObject *parent) : m_settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -42,10 +40,10 @@ ServerController::~ServerController()
|
||||
m_sshClient.disconnectFromHost();
|
||||
}
|
||||
|
||||
|
||||
ErrorCode ServerController::runScript(const ServerCredentials &credentials, QString script,
|
||||
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdOut,
|
||||
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdErr) {
|
||||
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut,
|
||||
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr)
|
||||
{
|
||||
|
||||
auto error = m_sshClient.connectToHost(credentials);
|
||||
if (error != ErrorCode::NoError) {
|
||||
@@ -92,36 +90,36 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::runContainerScript(const ServerCredentials &credentials,
|
||||
DockerContainer container, QString script,
|
||||
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdOut,
|
||||
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdErr)
|
||||
ErrorCode
|
||||
ServerController::runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script,
|
||||
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut,
|
||||
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr)
|
||||
{
|
||||
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
|
||||
Logger::appendSshLog("Run container script for " + ContainerProps::containerToString(container) + ":\n" + script);
|
||||
|
||||
ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName);
|
||||
if (e) return e;
|
||||
if (e)
|
||||
return e;
|
||||
|
||||
QString runner = QString("sudo docker exec -i $CONTAINER_NAME bash %1 ").arg(fileName);
|
||||
e = runScript(credentials,
|
||||
replaceVars(runner, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||
e = runScript(credentials, replaceVars(runner, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||
|
||||
QString remover = QString("sudo docker exec -i $CONTAINER_NAME rm %1 ").arg(fileName);
|
||||
runScript(credentials,
|
||||
replaceVars(remover, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||
runScript(credentials, replaceVars(remover, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||
const ServerCredentials &credentials, const QString &file, const QString &path,
|
||||
libssh::SftpOverwriteMode overwriteMode)
|
||||
ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||
const QString &file, const QString &path,
|
||||
libssh::SftpOverwriteMode overwriteMode)
|
||||
{
|
||||
ErrorCode e = ErrorCode::NoError;
|
||||
QString tmpFileName = QString("/tmp/%1.tmp").arg(Utils::getRandomString(16));
|
||||
e = uploadFileToHost(credentials, file.toUtf8(), tmpFileName);
|
||||
if (e) return e;
|
||||
if (e)
|
||||
return e;
|
||||
|
||||
QString stdOut;
|
||||
auto cbReadStd = [&](const QString &data, libssh::Client &) {
|
||||
@@ -130,61 +128,63 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||
};
|
||||
|
||||
// mkdir
|
||||
QString mkdir = QString("sudo docker exec -i $CONTAINER_NAME mkdir -p \"$(dirname %1)\"")
|
||||
.arg(path);
|
||||
|
||||
e = runScript(credentials,
|
||||
replaceVars(mkdir, genVarsForScript(credentials, container)));
|
||||
if (e) return e;
|
||||
QString mkdir = QString("sudo docker exec -i $CONTAINER_NAME mkdir -p \"$(dirname %1)\"").arg(path);
|
||||
|
||||
e = runScript(credentials, replaceVars(mkdir, genVarsForScript(credentials, container)));
|
||||
if (e)
|
||||
return e;
|
||||
|
||||
if (overwriteMode == libssh::SftpOverwriteMode::SftpOverwriteExisting) {
|
||||
e = runScript(credentials,
|
||||
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path),
|
||||
genVarsForScript(credentials, container)), cbReadStd, cbReadStd);
|
||||
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path),
|
||||
genVarsForScript(credentials, container)),
|
||||
cbReadStd, cbReadStd);
|
||||
|
||||
if (e) return e;
|
||||
}
|
||||
else if (overwriteMode == libssh::SftpOverwriteMode::SftpAppendToExisting) {
|
||||
if (e)
|
||||
return e;
|
||||
} else if (overwriteMode == libssh::SftpOverwriteMode::SftpAppendToExisting) {
|
||||
e = runScript(credentials,
|
||||
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName),
|
||||
genVarsForScript(credentials, container)), cbReadStd, cbReadStd);
|
||||
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName),
|
||||
genVarsForScript(credentials, container)),
|
||||
cbReadStd, cbReadStd);
|
||||
|
||||
if (e) return e;
|
||||
if (e)
|
||||
return e;
|
||||
|
||||
e = runScript(credentials,
|
||||
replaceVars(QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path),
|
||||
genVarsForScript(credentials, container)), cbReadStd, cbReadStd);
|
||||
|
||||
if (e) return e;
|
||||
}
|
||||
else return ErrorCode::NotImplementedError;
|
||||
e = runScript(
|
||||
credentials,
|
||||
replaceVars(
|
||||
QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path),
|
||||
genVarsForScript(credentials, container)),
|
||||
cbReadStd, cbReadStd);
|
||||
|
||||
if (e)
|
||||
return e;
|
||||
} else
|
||||
return ErrorCode::NotImplementedError;
|
||||
|
||||
if (stdOut.contains("Error: No such container:")) {
|
||||
return ErrorCode::ServerContainerMissingError;
|
||||
}
|
||||
|
||||
runScript(credentials,
|
||||
replaceVars(QString("sudo shred %1").arg(tmpFileName),
|
||||
genVarsForScript(credentials, container)));
|
||||
replaceVars(QString("sudo shred %1").arg(tmpFileName), genVarsForScript(credentials, container)));
|
||||
|
||||
runScript(credentials,
|
||||
replaceVars(QString("sudo rm %1").arg(tmpFileName),
|
||||
genVarsForScript(credentials, container)));
|
||||
runScript(credentials, replaceVars(QString("sudo rm %1").arg(tmpFileName), genVarsForScript(credentials, container)));
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
|
||||
const ServerCredentials &credentials, const QString &path, ErrorCode *errorCode)
|
||||
QByteArray ServerController::getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||
const QString &path, ErrorCode *errorCode)
|
||||
{
|
||||
|
||||
if (errorCode) *errorCode = ErrorCode::NoError;
|
||||
|
||||
QString script = QString("sudo docker exec -i %1 sh -c \"xxd -p \'%2\'\"").
|
||||
arg(ContainerProps::containerToString(container)).arg(path);
|
||||
if (errorCode)
|
||||
*errorCode = ErrorCode::NoError;
|
||||
|
||||
QString script = QString("sudo docker exec -i %1 sh -c \"xxd -p \'%2\'\"")
|
||||
.arg(ContainerProps::containerToString(container))
|
||||
.arg(path);
|
||||
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
@@ -196,8 +196,8 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
|
||||
return QByteArray::fromHex(stdOut.toUtf8());
|
||||
}
|
||||
|
||||
ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath,
|
||||
libssh::SftpOverwriteMode overwriteMode)
|
||||
ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data,
|
||||
const QString &remotePath, libssh::SftpOverwriteMode overwriteMode)
|
||||
{
|
||||
auto error = m_sshClient.connectToHost(credentials);
|
||||
if (error != ErrorCode::NoError) {
|
||||
@@ -209,7 +209,8 @@ ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credential
|
||||
localFile.write(data);
|
||||
localFile.close();
|
||||
|
||||
error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName().toStdString(), remotePath.toStdString(), "non_desc");
|
||||
error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName().toStdString(), remotePath.toStdString(),
|
||||
"non_desc");
|
||||
if (error != ErrorCode::NoError) {
|
||||
return error;
|
||||
}
|
||||
@@ -218,15 +219,14 @@ ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credential
|
||||
|
||||
ErrorCode ServerController::removeAllContainers(const ServerCredentials &credentials)
|
||||
{
|
||||
return runScript(credentials,
|
||||
amnezia::scriptData(SharedScriptType::remove_all_containers));
|
||||
return runScript(credentials, amnezia::scriptData(SharedScriptType::remove_all_containers));
|
||||
}
|
||||
|
||||
ErrorCode ServerController::removeContainer(const ServerCredentials &credentials, DockerContainer container)
|
||||
{
|
||||
return runScript(credentials,
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::remove_container),
|
||||
genVarsForScript(credentials, container)));
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::remove_container),
|
||||
genVarsForScript(credentials, container)));
|
||||
}
|
||||
|
||||
ErrorCode ServerController::setupContainer(const ServerCredentials &credentials, DockerContainer container,
|
||||
@@ -236,22 +236,33 @@ ErrorCode ServerController::setupContainer(const ServerCredentials &credentials,
|
||||
ErrorCode e = ErrorCode::NoError;
|
||||
|
||||
e = isUserInSudo(credentials, container);
|
||||
if (e) return e;
|
||||
if (e)
|
||||
return e;
|
||||
|
||||
e = isServerDpkgBusy(credentials, container);
|
||||
if (e) return e;
|
||||
if (e)
|
||||
return e;
|
||||
|
||||
e = installDockerWorker(credentials, container);
|
||||
if (e) return e;
|
||||
if (e)
|
||||
return e;
|
||||
qDebug().noquote() << "ServerController::setupContainer installDockerWorker finished";
|
||||
|
||||
if (!isUpdate) {
|
||||
e = isServerPortBusy(credentials, container, config);
|
||||
if (e) return e;
|
||||
if (e)
|
||||
return e;
|
||||
}
|
||||
|
||||
if (!isUpdate) {
|
||||
e = isServerPortBusy(credentials, container, config);
|
||||
if (e)
|
||||
return e;
|
||||
}
|
||||
|
||||
e = prepareHostWorker(credentials, container, config);
|
||||
if (e) return e;
|
||||
if (e)
|
||||
return e;
|
||||
qDebug().noquote() << "ServerController::setupContainer prepareHostWorker finished";
|
||||
|
||||
removeContainer(credentials, container);
|
||||
@@ -259,15 +270,18 @@ ErrorCode ServerController::setupContainer(const ServerCredentials &credentials,
|
||||
|
||||
qDebug().noquote() << "buildContainerWorker start";
|
||||
e = buildContainerWorker(credentials, container, config);
|
||||
if (e) return e;
|
||||
if (e)
|
||||
return e;
|
||||
qDebug().noquote() << "ServerController::setupContainer buildContainerWorker finished";
|
||||
|
||||
e = runContainerWorker(credentials, container, config);
|
||||
if (e) return e;
|
||||
if (e)
|
||||
return e;
|
||||
qDebug().noquote() << "ServerController::setupContainer runContainerWorker finished";
|
||||
|
||||
e = configureContainerWorker(credentials, container, config);
|
||||
if (e) return e;
|
||||
if (e)
|
||||
return e;
|
||||
qDebug().noquote() << "ServerController::setupContainer configureContainerWorker finished";
|
||||
|
||||
setupServerFirewall(credentials);
|
||||
@@ -277,46 +291,25 @@ ErrorCode ServerController::setupContainer(const ServerCredentials &credentials,
|
||||
}
|
||||
|
||||
ErrorCode ServerController::updateContainer(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &oldConfig, QJsonObject &newConfig)
|
||||
const QJsonObject &oldConfig, QJsonObject &newConfig)
|
||||
{
|
||||
bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig);
|
||||
qDebug() << "ServerController::updateContainer for container" << container << "reinstall required is" << reinstallRequired;
|
||||
qDebug() << "ServerController::updateContainer for container" << container << "reinstall required is"
|
||||
<< reinstallRequired;
|
||||
|
||||
if (reinstallRequired) {
|
||||
return setupContainer(credentials, container, newConfig, true);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
ErrorCode e = configureContainerWorker(credentials, container, newConfig);
|
||||
if (e) return e;
|
||||
if (e)
|
||||
return e;
|
||||
|
||||
return startupContainerWorker(credentials, container, newConfig);
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject ServerController::createContainerInitialConfig(DockerContainer container, int port, TransportProto tp)
|
||||
{
|
||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
||||
|
||||
QJsonObject config {
|
||||
{ config_key::container, ContainerProps::containerToString(container) }
|
||||
};
|
||||
|
||||
QJsonObject protoConfig;
|
||||
protoConfig.insert(config_key::port, QString::number(port));
|
||||
protoConfig.insert(config_key::transport_proto, ProtocolProps::transportProtoToString(tp, mainProto));
|
||||
|
||||
|
||||
if (container == DockerContainer::Sftp) {
|
||||
protoConfig.insert(config_key::userName, protocols::sftp::defaultUserName);
|
||||
protoConfig.insert(config_key::password, Utils::getRandomString(10));
|
||||
}
|
||||
|
||||
config.insert(ProtocolProps::protoToString(mainProto), protoConfig);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
bool ServerController::isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig)
|
||||
bool ServerController::isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig,
|
||||
const QJsonObject &newConfig)
|
||||
{
|
||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
||||
|
||||
@@ -324,25 +317,25 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
|
||||
const QJsonObject &newProtoConfig = newConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
|
||||
|
||||
if (container == DockerContainer::OpenVpn) {
|
||||
if (oldProtoConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto) !=
|
||||
newProtoConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto))
|
||||
return true;
|
||||
if (oldProtoConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto)
|
||||
!= newProtoConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto))
|
||||
return true;
|
||||
|
||||
if (oldProtoConfig.value(config_key::port).toString(protocols::openvpn::defaultPort) !=
|
||||
newProtoConfig.value(config_key::port).toString(protocols::openvpn::defaultPort))
|
||||
return true;
|
||||
if (oldProtoConfig.value(config_key::port).toString(protocols::openvpn::defaultPort)
|
||||
!= newProtoConfig.value(config_key::port).toString(protocols::openvpn::defaultPort))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (container == DockerContainer::Cloak) {
|
||||
if (oldProtoConfig.value(config_key::port).toString(protocols::cloak::defaultPort) !=
|
||||
newProtoConfig.value(config_key::port).toString(protocols::cloak::defaultPort))
|
||||
return true;
|
||||
if (oldProtoConfig.value(config_key::port).toString(protocols::cloak::defaultPort)
|
||||
!= newProtoConfig.value(config_key::port).toString(protocols::cloak::defaultPort))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (container == DockerContainer::ShadowSocks) {
|
||||
if (oldProtoConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort) !=
|
||||
newProtoConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort))
|
||||
return true;
|
||||
if (oldProtoConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort)
|
||||
!= newProtoConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -364,75 +357,86 @@ ErrorCode ServerController::installDockerWorker(const ServerCredentials &credent
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
|
||||
ErrorCode error = runScript(credentials,
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::install_docker),
|
||||
genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
||||
ErrorCode error =
|
||||
runScript(credentials,
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::install_docker), genVarsForScript(credentials)),
|
||||
cbReadStdOut, cbReadStdErr);
|
||||
|
||||
qDebug().noquote() << "ServerController::installDockerWorker" << stdOut;
|
||||
if (stdOut.contains("lock")) return ErrorCode::ServerPacketManagerError;
|
||||
if (stdOut.contains("command not found")) return ErrorCode::ServerDockerFailedError;
|
||||
if (stdOut.contains("lock"))
|
||||
return ErrorCode::ServerPacketManagerError;
|
||||
if (stdOut.contains("command not found"))
|
||||
return ErrorCode::ServerDockerFailedError;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
||||
ErrorCode ServerController::prepareHostWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &config)
|
||||
{
|
||||
// create folder on host
|
||||
return runScript(credentials,
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::prepare_host),
|
||||
genVarsForScript(credentials, container)));
|
||||
return runScript(
|
||||
credentials,
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::prepare_host), genVarsForScript(credentials, container)));
|
||||
}
|
||||
|
||||
ErrorCode ServerController::buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
||||
ErrorCode ServerController::buildContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &config)
|
||||
{
|
||||
ErrorCode e = uploadFileToHost(credentials, amnezia::scriptData(ProtocolScriptType::dockerfile, container).toUtf8(),
|
||||
amnezia::server::getDockerfileFolder(container) + "/Dockerfile");
|
||||
amnezia::server::getDockerfileFolder(container) + "/Dockerfile");
|
||||
|
||||
if (e) return e;
|
||||
if (e)
|
||||
return e;
|
||||
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
stdOut += data + "\n";
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
||||
// stdOut += data + "\n";
|
||||
// };
|
||||
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
||||
// stdOut += data + "\n";
|
||||
// };
|
||||
|
||||
e = runScript(credentials,
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::build_container),
|
||||
genVarsForScript(credentials, container, config)), cbReadStdOut);
|
||||
if (e) return e;
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::build_container),
|
||||
genVarsForScript(credentials, container, config)),
|
||||
cbReadStdOut);
|
||||
if (e)
|
||||
return e;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
|
||||
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||
QJsonObject &config)
|
||||
{
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
stdOut += data + "\n";
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
||||
// stdOut += data + "\n";
|
||||
// };
|
||||
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
||||
// stdOut += data + "\n";
|
||||
// };
|
||||
|
||||
ErrorCode e = runScript(credentials,
|
||||
replaceVars(amnezia::scriptData(ProtocolScriptType::run_container, container),
|
||||
genVarsForScript(credentials, container, config)), cbReadStdOut);
|
||||
replaceVars(amnezia::scriptData(ProtocolScriptType::run_container, container),
|
||||
genVarsForScript(credentials, container, config)),
|
||||
cbReadStdOut);
|
||||
|
||||
|
||||
if (stdOut.contains("docker: Error response from daemon")) return ErrorCode::ServerDockerFailedError;
|
||||
|
||||
if (stdOut.contains("address already in use")) return ErrorCode::ServerPortAlreadyAllocatedError;
|
||||
if (stdOut.contains("is already in use by container")) return ErrorCode::ServerPortAlreadyAllocatedError;
|
||||
if (stdOut.contains("invalid publish")) return ErrorCode::ServerDockerFailedError;
|
||||
if (stdOut.contains("address already in use"))
|
||||
return ErrorCode::ServerPortAlreadyAllocatedError;
|
||||
if (stdOut.contains("is already in use by container"))
|
||||
return ErrorCode::ServerPortAlreadyAllocatedError;
|
||||
if (stdOut.contains("invalid publish"))
|
||||
return ErrorCode::ServerDockerFailedError;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
|
||||
ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||
QJsonObject &config)
|
||||
{
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
@@ -444,19 +448,18 @@ ErrorCode ServerController::configureContainerWorker(const ServerCredentials &cr
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
|
||||
|
||||
ErrorCode e = runContainerScript(credentials, container,
|
||||
replaceVars(amnezia::scriptData(ProtocolScriptType::configure_container, container),
|
||||
genVarsForScript(credentials, container, config)),
|
||||
cbReadStdOut, cbReadStdErr);
|
||||
|
||||
replaceVars(amnezia::scriptData(ProtocolScriptType::configure_container, container),
|
||||
genVarsForScript(credentials, container, config)),
|
||||
cbReadStdOut, cbReadStdErr);
|
||||
|
||||
m_configurator->updateContainerConfigAfterInstallation(container, config, stdOut);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
||||
ErrorCode ServerController::startupContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &config)
|
||||
{
|
||||
QString script = amnezia::scriptData(ProtocolScriptType::container_startup, container);
|
||||
|
||||
@@ -465,16 +468,19 @@ ErrorCode ServerController::startupContainerWorker(const ServerCredentials &cred
|
||||
}
|
||||
|
||||
ErrorCode e = uploadTextFileToContainer(container, credentials,
|
||||
replaceVars(script, genVarsForScript(credentials, container, config)),
|
||||
"/opt/amnezia/start.sh");
|
||||
if (e) return e;
|
||||
replaceVars(script, genVarsForScript(credentials, container, config)),
|
||||
"/opt/amnezia/start.sh");
|
||||
if (e)
|
||||
return e;
|
||||
|
||||
return runScript(credentials,
|
||||
replaceVars("sudo docker exec -d $CONTAINER_NAME sh -c \"chmod a+x /opt/amnezia/start.sh && /opt/amnezia/start.sh\"",
|
||||
genVarsForScript(credentials, container, config)));
|
||||
replaceVars("sudo docker exec -d $CONTAINER_NAME sh -c \"chmod a+x /opt/amnezia/start.sh && "
|
||||
"/opt/amnezia/start.sh\"",
|
||||
genVarsForScript(credentials, container, config)));
|
||||
}
|
||||
|
||||
ServerController::Vars ServerController::genVarsForScript(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
||||
ServerController::Vars ServerController::genVarsForScript(const ServerCredentials &credentials,
|
||||
DockerContainer container, const QJsonObject &config)
|
||||
{
|
||||
const QJsonObject &openvpnConfig = config.value(ProtocolProps::protoToString(Proto::OpenVpn)).toObject();
|
||||
const QJsonObject &cloakConfig = config.value(ProtocolProps::protoToString(Proto::Cloak)).toObject();
|
||||
@@ -484,85 +490,102 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
||||
|
||||
Vars vars;
|
||||
|
||||
vars.append({{"$REMOTE_HOST", credentials.hostName}});
|
||||
vars.append({ { "$REMOTE_HOST", credentials.hostName } });
|
||||
|
||||
// OpenVPN vars
|
||||
vars.append({{"$OPENVPN_SUBNET_IP", openvpnConfig.value(config_key::subnet_address).toString(protocols::openvpn::defaultSubnetAddress) }});
|
||||
vars.append({{"$OPENVPN_SUBNET_CIDR", openvpnConfig.value(config_key::subnet_cidr).toString(protocols::openvpn::defaultSubnetCidr) }});
|
||||
vars.append({{"$OPENVPN_SUBNET_MASK", openvpnConfig.value(config_key::subnet_mask).toString(protocols::openvpn::defaultSubnetMask) }});
|
||||
vars.append(
|
||||
{ { "$OPENVPN_SUBNET_IP",
|
||||
openvpnConfig.value(config_key::subnet_address).toString(protocols::openvpn::defaultSubnetAddress) } });
|
||||
vars.append({ { "$OPENVPN_SUBNET_CIDR",
|
||||
openvpnConfig.value(config_key::subnet_cidr).toString(protocols::openvpn::defaultSubnetCidr) } });
|
||||
vars.append({ { "$OPENVPN_SUBNET_MASK",
|
||||
openvpnConfig.value(config_key::subnet_mask).toString(protocols::openvpn::defaultSubnetMask) } });
|
||||
|
||||
vars.append({{"$OPENVPN_PORT", openvpnConfig.value(config_key::port).toString(protocols::openvpn::defaultPort) }});
|
||||
vars.append({{"$OPENVPN_TRANSPORT_PROTO", openvpnConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto) }});
|
||||
vars.append({ { "$OPENVPN_PORT", openvpnConfig.value(config_key::port).toString(protocols::openvpn::defaultPort) } });
|
||||
vars.append(
|
||||
{ { "$OPENVPN_TRANSPORT_PROTO",
|
||||
openvpnConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto) } });
|
||||
|
||||
bool isNcpDisabled = openvpnConfig.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable);
|
||||
vars.append({{"$OPENVPN_NCP_DISABLE", isNcpDisabled ? protocols::openvpn::ncpDisableString : "" }});
|
||||
vars.append({ { "$OPENVPN_NCP_DISABLE", isNcpDisabled ? protocols::openvpn::ncpDisableString : "" } });
|
||||
|
||||
vars.append({{"$OPENVPN_CIPHER", openvpnConfig.value(config_key::cipher).toString(protocols::openvpn::defaultCipher) }});
|
||||
vars.append({{"$OPENVPN_HASH", openvpnConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash) }});
|
||||
vars.append({ { "$OPENVPN_CIPHER",
|
||||
openvpnConfig.value(config_key::cipher).toString(protocols::openvpn::defaultCipher) } });
|
||||
vars.append({ { "$OPENVPN_HASH", openvpnConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash) } });
|
||||
|
||||
bool isTlsAuth = openvpnConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth);
|
||||
vars.append({{"$OPENVPN_TLS_AUTH", isTlsAuth ? protocols::openvpn::tlsAuthString : "" }});
|
||||
vars.append({ { "$OPENVPN_TLS_AUTH", isTlsAuth ? protocols::openvpn::tlsAuthString : "" } });
|
||||
if (!isTlsAuth) {
|
||||
// erase $OPENVPN_TA_KEY, so it will not set in OpenVpnConfigurator::genOpenVpnConfig
|
||||
vars.append({{"$OPENVPN_TA_KEY", "" }});
|
||||
vars.append({ { "$OPENVPN_TA_KEY", "" } });
|
||||
}
|
||||
|
||||
vars.append({{"$OPENVPN_ADDITIONAL_CLIENT_CONFIG", openvpnConfig.value(config_key::additional_client_config).
|
||||
toString(protocols::openvpn::defaultAdditionalClientConfig) }});
|
||||
vars.append({{"$OPENVPN_ADDITIONAL_SERVER_CONFIG", openvpnConfig.value(config_key::additional_server_config).
|
||||
toString(protocols::openvpn::defaultAdditionalServerConfig) }});
|
||||
vars.append({ { "$OPENVPN_ADDITIONAL_CLIENT_CONFIG",
|
||||
openvpnConfig.value(config_key::additional_client_config)
|
||||
.toString(protocols::openvpn::defaultAdditionalClientConfig) } });
|
||||
vars.append({ { "$OPENVPN_ADDITIONAL_SERVER_CONFIG",
|
||||
openvpnConfig.value(config_key::additional_server_config)
|
||||
.toString(protocols::openvpn::defaultAdditionalServerConfig) } });
|
||||
|
||||
// ShadowSocks vars
|
||||
vars.append({{"$SHADOWSOCKS_SERVER_PORT", ssConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort) }});
|
||||
vars.append({{"$SHADOWSOCKS_LOCAL_PORT", ssConfig.value(config_key::local_port).toString(protocols::shadowsocks::defaultLocalProxyPort) }});
|
||||
vars.append({{"$SHADOWSOCKS_CIPHER", ssConfig.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher) }});
|
||||
vars.append({ { "$SHADOWSOCKS_SERVER_PORT",
|
||||
ssConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort) } });
|
||||
vars.append({ { "$SHADOWSOCKS_LOCAL_PORT",
|
||||
ssConfig.value(config_key::local_port).toString(protocols::shadowsocks::defaultLocalProxyPort) } });
|
||||
vars.append({ { "$SHADOWSOCKS_CIPHER",
|
||||
ssConfig.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher) } });
|
||||
|
||||
vars.append({{"$CONTAINER_NAME", ContainerProps::containerToString(container)}});
|
||||
vars.append({{"$DOCKERFILE_FOLDER", "/opt/amnezia/" + ContainerProps::containerToString(container)}});
|
||||
vars.append({ { "$CONTAINER_NAME", ContainerProps::containerToString(container) } });
|
||||
vars.append({ { "$DOCKERFILE_FOLDER", "/opt/amnezia/" + ContainerProps::containerToString(container) } });
|
||||
|
||||
// Cloak vars
|
||||
vars.append({{"$CLOAK_SERVER_PORT", cloakConfig.value(config_key::port).toString(protocols::cloak::defaultPort) }});
|
||||
vars.append({{"$FAKE_WEB_SITE_ADDRESS", cloakConfig.value(config_key::site).toString(protocols::cloak::defaultRedirSite) }});
|
||||
vars.append({ { "$CLOAK_SERVER_PORT", cloakConfig.value(config_key::port).toString(protocols::cloak::defaultPort) } });
|
||||
vars.append({ { "$FAKE_WEB_SITE_ADDRESS",
|
||||
cloakConfig.value(config_key::site).toString(protocols::cloak::defaultRedirSite) } });
|
||||
|
||||
// Wireguard vars
|
||||
vars.append({{"$WIREGUARD_SUBNET_IP", wireguarConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) }});
|
||||
vars.append({{"$WIREGUARD_SUBNET_CIDR", wireguarConfig.value(config_key::subnet_cidr).toString(protocols::wireguard::defaultSubnetCidr) }});
|
||||
vars.append({{"$WIREGUARD_SUBNET_MASK", wireguarConfig.value(config_key::subnet_mask).toString(protocols::wireguard::defaultSubnetMask) }});
|
||||
vars.append(
|
||||
{ { "$WIREGUARD_SUBNET_IP",
|
||||
wireguarConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) } });
|
||||
vars.append({ { "$WIREGUARD_SUBNET_CIDR",
|
||||
wireguarConfig.value(config_key::subnet_cidr).toString(protocols::wireguard::defaultSubnetCidr) } });
|
||||
vars.append({ { "$WIREGUARD_SUBNET_MASK",
|
||||
wireguarConfig.value(config_key::subnet_mask).toString(protocols::wireguard::defaultSubnetMask) } });
|
||||
|
||||
vars.append({{"$WIREGUARD_SERVER_PORT", wireguarConfig.value(config_key::port).toString(protocols::wireguard::defaultPort) }});
|
||||
vars.append({ { "$WIREGUARD_SERVER_PORT",
|
||||
wireguarConfig.value(config_key::port).toString(protocols::wireguard::defaultPort) } });
|
||||
|
||||
// IPsec vars
|
||||
vars.append({{"$IPSEC_VPN_L2TP_NET", "192.168.42.0/24"}});
|
||||
vars.append({{"$IPSEC_VPN_L2TP_POOL", "192.168.42.10-192.168.42.250"}});
|
||||
vars.append({{"$IPSEC_VPN_L2TP_LOCAL", "192.168.42.1"}});
|
||||
vars.append({ { "$IPSEC_VPN_L2TP_NET", "192.168.42.0/24" } });
|
||||
vars.append({ { "$IPSEC_VPN_L2TP_POOL", "192.168.42.10-192.168.42.250" } });
|
||||
vars.append({ { "$IPSEC_VPN_L2TP_LOCAL", "192.168.42.1" } });
|
||||
|
||||
vars.append({{"$IPSEC_VPN_XAUTH_NET", "192.168.43.0/24"}});
|
||||
vars.append({{"$IPSEC_VPN_XAUTH_POOL", "192.168.43.10-192.168.43.250"}});
|
||||
vars.append({ { "$IPSEC_VPN_XAUTH_NET", "192.168.43.0/24" } });
|
||||
vars.append({ { "$IPSEC_VPN_XAUTH_POOL", "192.168.43.10-192.168.43.250" } });
|
||||
|
||||
vars.append({{"$IPSEC_VPN_SHA2_TRUNCBUG", "yes"}});
|
||||
vars.append({ { "$IPSEC_VPN_SHA2_TRUNCBUG", "yes" } });
|
||||
|
||||
vars.append({{"$IPSEC_VPN_VPN_ANDROID_MTU_FIX", "yes"}});
|
||||
vars.append({{"$IPSEC_VPN_DISABLE_IKEV2", "no"}});
|
||||
vars.append({{"$IPSEC_VPN_DISABLE_L2TP", "no"}});
|
||||
vars.append({{"$IPSEC_VPN_DISABLE_XAUTH", "no"}});
|
||||
vars.append({ { "$IPSEC_VPN_VPN_ANDROID_MTU_FIX", "yes" } });
|
||||
vars.append({ { "$IPSEC_VPN_DISABLE_IKEV2", "no" } });
|
||||
vars.append({ { "$IPSEC_VPN_DISABLE_L2TP", "no" } });
|
||||
vars.append({ { "$IPSEC_VPN_DISABLE_XAUTH", "no" } });
|
||||
|
||||
vars.append({{"$IPSEC_VPN_C2C_TRAFFIC", "no"}});
|
||||
|
||||
vars.append({{"$PRIMARY_SERVER_DNS", m_settings->primaryDns()}});
|
||||
vars.append({{"$SECONDARY_SERVER_DNS", m_settings->secondaryDns()}});
|
||||
vars.append({ { "$IPSEC_VPN_C2C_TRAFFIC", "no" } });
|
||||
|
||||
vars.append({ { "$PRIMARY_SERVER_DNS", m_settings->primaryDns() } });
|
||||
vars.append({ { "$SECONDARY_SERVER_DNS", m_settings->secondaryDns() } });
|
||||
|
||||
// Sftp vars
|
||||
vars.append({{"$SFTP_PORT", sftpConfig.value(config_key::port).toString(QString::number(ProtocolProps::defaultPort(Proto::Sftp))) }});
|
||||
vars.append({{"$SFTP_USER", sftpConfig.value(config_key::userName).toString() }});
|
||||
vars.append({{"$SFTP_PASSWORD", sftpConfig.value(config_key::password).toString() }});
|
||||
|
||||
vars.append(
|
||||
{ { "$SFTP_PORT",
|
||||
sftpConfig.value(config_key::port).toString(QString::number(ProtocolProps::defaultPort(Proto::Sftp))) } });
|
||||
vars.append({ { "$SFTP_USER", sftpConfig.value(config_key::userName).toString() } });
|
||||
vars.append({ { "$SFTP_PASSWORD", sftpConfig.value(config_key::password).toString() } });
|
||||
|
||||
QString serverIp = Utils::getIPAddress(credentials.hostName);
|
||||
if (!serverIp.isEmpty()) {
|
||||
vars.append({{"$SERVER_IP_ADDRESS", serverIp}});
|
||||
}
|
||||
else {
|
||||
vars.append({ { "$SERVER_IP_ADDRESS", serverIp } });
|
||||
} else {
|
||||
qWarning() << "ServerController::genVarsForScript unable to resolve address for credentials.hostName";
|
||||
}
|
||||
|
||||
@@ -581,10 +604,11 @@ QString ServerController::checkSshConnection(const ServerCredentials &credential
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
|
||||
ErrorCode e = runScript(credentials,
|
||||
amnezia::scriptData(SharedScriptType::check_connection), cbReadStdOut, cbReadStdErr);
|
||||
ErrorCode e =
|
||||
runScript(credentials, amnezia::scriptData(SharedScriptType::check_connection), cbReadStdOut, cbReadStdErr);
|
||||
|
||||
if (errorCode) *errorCode = e;
|
||||
if (errorCode)
|
||||
*errorCode = e;
|
||||
|
||||
return stdOut;
|
||||
}
|
||||
@@ -596,9 +620,9 @@ void ServerController::setCancelInstallation(const bool cancel)
|
||||
|
||||
ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials)
|
||||
{
|
||||
return runScript(credentials,
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::setup_host_firewall),
|
||||
genVarsForScript(credentials)));
|
||||
return runScript(
|
||||
credentials,
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::setup_host_firewall), genVarsForScript(credentials)));
|
||||
}
|
||||
|
||||
QString ServerController::replaceVars(const QString &script, const Vars &vars)
|
||||
@@ -610,7 +634,8 @@ QString ServerController::replaceVars(const QString &script, const Vars &vars)
|
||||
return s;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
||||
ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &config)
|
||||
{
|
||||
if (container == DockerContainer::Dns) {
|
||||
return ErrorCode::NoError;
|
||||
@@ -633,11 +658,15 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
||||
QStringList fixedPorts = ContainerProps::fixedPortsForContainer(container);
|
||||
|
||||
QString defaultPort("%1");
|
||||
QString port = containerConfig.value(config_key::port).toString(defaultPort.arg(ProtocolProps::defaultPort(protocol)));
|
||||
QString defaultTransportProto = ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(protocol), protocol);
|
||||
QString port =
|
||||
containerConfig.value(config_key::port).toString(defaultPort.arg(ProtocolProps::defaultPort(protocol)));
|
||||
QString defaultTransportProto =
|
||||
ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(protocol), protocol);
|
||||
QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto);
|
||||
|
||||
QString script = QString("which lsof &>/dev/null || true && sudo lsof -i -P -n | grep -E ':%1 ").arg(port);
|
||||
// TODO reimplement with netstat
|
||||
QString script =
|
||||
QString("which lsof &>/dev/null || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
|
||||
for (auto &port : fixedPorts) {
|
||||
script = script.append("|:%1").arg(port);
|
||||
}
|
||||
@@ -647,8 +676,8 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
||||
script = script.append(" | grep LISTEN");
|
||||
}
|
||||
|
||||
ErrorCode errorCode = runScript(credentials,
|
||||
replaceVars(script, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||
ErrorCode errorCode = runScript(credentials, replaceVars(script, genVarsForScript(credentials, container)),
|
||||
cbReadStdOut, cbReadStdErr);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
@@ -676,9 +705,11 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
|
||||
};
|
||||
|
||||
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo);
|
||||
ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
||||
ErrorCode error =
|
||||
runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
||||
|
||||
if (!stdOut.contains("sudo")) return ErrorCode::ServerUserNotInSudo;
|
||||
if (!stdOut.contains("sudo"))
|
||||
return ErrorCode::ServerUserNotInSudo;
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -699,27 +730,29 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential
|
||||
|
||||
QFuture<ErrorCode> future = QtConcurrent::run([this, &stdOut, &cbReadStdOut, &cbReadStdErr, &credentials]() {
|
||||
// max 100 attempts
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
for (int i = 0; i < 30; ++i) {
|
||||
if (m_cancelInstallation) {
|
||||
return ErrorCode::ServerCancelInstallation;
|
||||
}
|
||||
stdOut.clear();
|
||||
runScript(credentials,
|
||||
replaceVars(amnezia::scriptData(SharedScriptType::check_server_is_busy),
|
||||
genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
||||
genVarsForScript(credentials)),
|
||||
cbReadStdOut, cbReadStdErr);
|
||||
|
||||
// if 'fuser' is not installed, skip check
|
||||
if (stdOut.contains("Not installed")) return ErrorCode::NoError;
|
||||
if (stdOut.contains("Packet manager not found"))
|
||||
return ErrorCode::ServerPacketManagerError;
|
||||
if (stdOut.contains("fuser not installed"))
|
||||
return ErrorCode::NoError;
|
||||
|
||||
if (stdOut.isEmpty()) {
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
else {
|
||||
#ifdef MZ_DEBUG
|
||||
} else {
|
||||
#ifdef MZ_DEBUG
|
||||
qDebug().noquote() << stdOut;
|
||||
#endif
|
||||
#endif
|
||||
emit serverIsBusy(true);
|
||||
QThread::msleep(5000);
|
||||
QThread::msleep(10000);
|
||||
}
|
||||
}
|
||||
return ErrorCode::ServerPacketManagerError;
|
||||
@@ -736,7 +769,8 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential
|
||||
return future.result();
|
||||
}
|
||||
|
||||
ErrorCode ServerController::getAlreadyInstalledContainers(const ServerCredentials &credentials, QMap<DockerContainer, QJsonObject> &installedContainers)
|
||||
ErrorCode ServerController::getAlreadyInstalledContainers(const ServerCredentials &credentials,
|
||||
QMap<DockerContainer, QJsonObject> &installedContainers)
|
||||
{
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
@@ -767,14 +801,19 @@ ErrorCode ServerController::getAlreadyInstalledContainers(const ServerCredential
|
||||
QString port = containerAndPortMatch.captured(2);
|
||||
QString transportProto = containerAndPortMatch.captured(3);
|
||||
DockerContainer container = ContainerProps::containerFromString(name);
|
||||
|
||||
QJsonObject config;
|
||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
||||
QJsonObject config {
|
||||
{ config_key::container, name },
|
||||
{ ProtocolProps::protoToString(mainProto), QJsonObject {
|
||||
{ config_key::port, port },
|
||||
{ config_key::transport_proto, transportProto }}
|
||||
for (auto protocol : ContainerProps::protocolsForContainer(container)) {
|
||||
QJsonObject containerConfig;
|
||||
if (protocol == mainProto) {
|
||||
containerConfig.insert(config_key::port, port);
|
||||
containerConfig.insert(config_key::transport_proto, transportProto);
|
||||
|
||||
config.insert(config_key::container, ContainerProps::containerToString(container));
|
||||
}
|
||||
};
|
||||
config.insert(ProtocolProps::protoToString(protocol), containerConfig);
|
||||
}
|
||||
installedContainers.insert(container, config);
|
||||
}
|
||||
}
|
||||
@@ -782,7 +821,8 @@ ErrorCode ServerController::getAlreadyInstalledContainers(const ServerCredential
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
ErrorCode ServerController::getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey, const std::function<QString()> &callback)
|
||||
ErrorCode ServerController::getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey,
|
||||
const std::function<QString()> &callback)
|
||||
{
|
||||
auto error = m_sshClient.getDecryptedPrivateKey(credentials, decryptedPrivateKey, callback);
|
||||
return error;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
|
||||
#include "defs.h"
|
||||
#include "containers/containers_defs.h"
|
||||
#include "defs.h"
|
||||
#include "sshclient.h"
|
||||
|
||||
class Settings;
|
||||
@@ -24,52 +24,62 @@ public:
|
||||
|
||||
ErrorCode removeAllContainers(const ServerCredentials &credentials);
|
||||
ErrorCode removeContainer(const ServerCredentials &credentials, DockerContainer container);
|
||||
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container,
|
||||
QJsonObject &config, bool isUpdate = false);
|
||||
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config,
|
||||
bool isUpdate = false);
|
||||
ErrorCode updateContainer(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &oldConfig, QJsonObject &newConfig);
|
||||
ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials, QMap<DockerContainer, QJsonObject> &installedContainers);
|
||||
|
||||
// create initial config - generate passwords, etc
|
||||
QJsonObject createContainerInitialConfig(DockerContainer container, int port, TransportProto tp);
|
||||
ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject());
|
||||
ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials,
|
||||
QMap<DockerContainer, QJsonObject> &installedContainers);
|
||||
|
||||
ErrorCode uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||
const QString &file, const QString &path,
|
||||
libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
|
||||
ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &config = QJsonObject());
|
||||
|
||||
ErrorCode uploadTextFileToContainer(
|
||||
DockerContainer container, const ServerCredentials &credentials, const QString &file, const QString &path,
|
||||
libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
|
||||
QByteArray getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||
const QString &path, ErrorCode *errorCode = nullptr);
|
||||
|
||||
QString replaceVars(const QString &script, const Vars &vars);
|
||||
Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None, const QJsonObject &config = QJsonObject());
|
||||
Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None,
|
||||
const QJsonObject &config = QJsonObject());
|
||||
|
||||
ErrorCode runScript(const ServerCredentials &credentials, QString script,
|
||||
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
|
||||
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdErr = nullptr);
|
||||
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
|
||||
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr = nullptr);
|
||||
|
||||
ErrorCode runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script,
|
||||
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
|
||||
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdErr = nullptr);
|
||||
ErrorCode
|
||||
runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script,
|
||||
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
|
||||
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr = nullptr);
|
||||
|
||||
QString checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode = nullptr);
|
||||
|
||||
void setCancelInstallation(const bool cancel);
|
||||
|
||||
ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey, const std::function<QString()> &callback);
|
||||
ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey,
|
||||
const std::function<QString()> &callback);
|
||||
|
||||
private:
|
||||
ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container);
|
||||
ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject());
|
||||
ErrorCode buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject());
|
||||
ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &config = QJsonObject());
|
||||
ErrorCode buildContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &config = QJsonObject());
|
||||
ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config);
|
||||
ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config);
|
||||
ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||
QJsonObject &config);
|
||||
|
||||
ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config);
|
||||
bool isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig);
|
||||
ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &config);
|
||||
bool isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig,
|
||||
const QJsonObject &newConfig);
|
||||
ErrorCode isUserInSudo(const ServerCredentials &credentials, DockerContainer container);
|
||||
ErrorCode isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container);
|
||||
|
||||
ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data,
|
||||
const QString &remotePath, libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
|
||||
|
||||
ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath,
|
||||
libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
|
||||
|
||||
ErrorCode setupServerFirewall(const ServerCredentials &credentials);
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ const uint32_t S_IRWXU = 0644;
|
||||
#endif
|
||||
|
||||
namespace libssh {
|
||||
const QString libsshTimeoutError = "Timeout connecting to";
|
||||
|
||||
std::function<QString()> Client::m_passphraseCallback;
|
||||
|
||||
Client::Client(QObject *parent) : QObject(parent)
|
||||
@@ -45,20 +47,29 @@ namespace libssh {
|
||||
ssh_options_set(m_session, SSH_OPTIONS_USER, hostUsername.c_str());
|
||||
ssh_options_set(m_session, SSH_OPTIONS_LOG_VERBOSITY, &logVerbosity);
|
||||
|
||||
int connectionResult = ssh_connect(m_session);
|
||||
QFutureWatcher<int> watcher;
|
||||
QFuture<int> future = QtConcurrent::run([this]() {
|
||||
return ssh_connect(m_session);
|
||||
});
|
||||
|
||||
QEventLoop wait;
|
||||
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, &wait, &QEventLoop::quit);
|
||||
watcher.setFuture(future);
|
||||
wait.exec();
|
||||
|
||||
int connectionResult = watcher.result();
|
||||
|
||||
if (connectionResult != SSH_OK) {
|
||||
qDebug() << ssh_get_error(m_session);
|
||||
return fromLibsshErrorCode(ssh_get_error_code(m_session));
|
||||
return fromLibsshErrorCode();
|
||||
}
|
||||
|
||||
std::string authUsername = credentials.userName.toStdString();
|
||||
|
||||
int authResult = SSH_ERROR;
|
||||
if (credentials.password.contains("BEGIN") && credentials.password.contains("PRIVATE KEY")) {
|
||||
if (credentials.secretData.contains("BEGIN") && credentials.secretData.contains("PRIVATE KEY")) {
|
||||
ssh_key privateKey = nullptr;
|
||||
ssh_key publicKey = nullptr;
|
||||
authResult = ssh_pki_import_privkey_base64(credentials.password.toStdString().c_str(), nullptr, callback, nullptr, &privateKey);
|
||||
authResult = ssh_pki_import_privkey_base64(credentials.secretData.toStdString().c_str(), nullptr, callback, nullptr, &privateKey);
|
||||
if (authResult == SSH_OK) {
|
||||
authResult = ssh_pki_export_privkey_to_pubkey(privateKey, &publicKey);
|
||||
}
|
||||
@@ -78,18 +89,17 @@ namespace libssh {
|
||||
ssh_key_free(privateKey);
|
||||
}
|
||||
if (authResult != SSH_OK) {
|
||||
qDebug() << ssh_get_error(m_session);
|
||||
ErrorCode errorCode = fromLibsshErrorCode(ssh_get_error_code(m_session));
|
||||
qCritical() << ssh_get_error(m_session);
|
||||
ErrorCode errorCode = fromLibsshErrorCode();
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
errorCode = ErrorCode::SshPrivateKeyFormatError;
|
||||
}
|
||||
return errorCode;
|
||||
}
|
||||
} else {
|
||||
authResult = ssh_userauth_password(m_session, authUsername.c_str(), credentials.password.toStdString().c_str());
|
||||
authResult = ssh_userauth_password(m_session, authUsername.c_str(), credentials.secretData.toStdString().c_str());
|
||||
if (authResult != SSH_OK) {
|
||||
qDebug() << ssh_get_error(m_session);
|
||||
return fromLibsshErrorCode(ssh_get_error_code(m_session));
|
||||
return fromLibsshErrorCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,16 +196,15 @@ namespace libssh {
|
||||
ErrorCode Client::writeResponse(const QString &data)
|
||||
{
|
||||
if (m_channel == nullptr) {
|
||||
qDebug() << "ssh channel not initialized";
|
||||
return fromLibsshErrorCode(ssh_get_error_code(m_session));
|
||||
qCritical() << "ssh channel not initialized";
|
||||
return fromLibsshErrorCode();
|
||||
}
|
||||
|
||||
int bytesWritten = ssh_channel_write(m_channel, data.toUtf8(), (uint32_t)data.size());
|
||||
if (bytesWritten == data.size() && ssh_channel_write(m_channel, "\n", 1)) {
|
||||
return fromLibsshErrorCode(ssh_get_error_code(m_session));
|
||||
return fromLibsshErrorCode();
|
||||
}
|
||||
qDebug() << ssh_get_error(m_session);
|
||||
return fromLibsshErrorCode(ssh_get_error_code(m_session));
|
||||
return fromLibsshErrorCode();
|
||||
}
|
||||
|
||||
ErrorCode Client::closeChannel()
|
||||
@@ -210,8 +219,7 @@ namespace libssh {
|
||||
ssh_channel_free(m_channel);
|
||||
m_channel = nullptr;
|
||||
}
|
||||
qDebug() << ssh_get_error(m_session);
|
||||
return fromLibsshErrorCode(ssh_get_error_code(m_session));
|
||||
return fromLibsshErrorCode();
|
||||
}
|
||||
|
||||
ErrorCode Client::sftpFileCopy(const SftpOverwriteMode overwriteMode, const std::string& localPath, const std::string& remotePath, const std::string& fileDesc)
|
||||
@@ -308,12 +316,21 @@ namespace libssh {
|
||||
sftp_free(m_sftpSession);
|
||||
m_sftpSession = nullptr;
|
||||
}
|
||||
qDebug() << ssh_get_error(m_session);
|
||||
qCritical() << ssh_get_error(m_session);
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
ErrorCode Client::fromLibsshErrorCode(int errorCode)
|
||||
ErrorCode Client::fromLibsshErrorCode()
|
||||
{
|
||||
int errorCode = ssh_get_error_code(m_session);
|
||||
if (errorCode != SSH_NO_ERROR) {
|
||||
QString errorMessage = ssh_get_error(m_session);
|
||||
qCritical() << errorMessage;
|
||||
if (errorMessage.contains(libsshTimeoutError)) {
|
||||
return ErrorCode::SshTimeoutError;
|
||||
}
|
||||
}
|
||||
|
||||
switch (errorCode) {
|
||||
case(SSH_NO_ERROR): return ErrorCode::NoError;
|
||||
case(SSH_REQUEST_DENIED): return ErrorCode::SshRequsetDeniedError;
|
||||
@@ -350,7 +367,7 @@ namespace libssh {
|
||||
|
||||
ssh_key privateKey = nullptr;
|
||||
m_passphraseCallback = passphraseCallback;
|
||||
authResult = ssh_pki_import_privkey_base64(credentials.password.toStdString().c_str(), nullptr, callback, nullptr, &privateKey);
|
||||
authResult = ssh_pki_import_privkey_base64(credentials.secretData.toStdString().c_str(), nullptr, callback, nullptr, &privateKey);
|
||||
if (authResult == SSH_OK) {
|
||||
char *b64 = nullptr;
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace libssh {
|
||||
private:
|
||||
ErrorCode closeChannel();
|
||||
ErrorCode closeSftpSession();
|
||||
ErrorCode fromLibsshErrorCode(int errorCode);
|
||||
ErrorCode fromLibsshErrorCode();
|
||||
ErrorCode fromLibsshSftpErrorCode(int errorCode);
|
||||
static int callback(const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QMetaEnum>
|
||||
#include <QTimer>
|
||||
|
||||
#include "leakdetector.h"
|
||||
@@ -64,9 +65,12 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
||||
// method calls switchServer().
|
||||
//
|
||||
// At the end, if the activation succeds, the `connected` signal is emitted.
|
||||
// If the activation abort's for any reason `the `activationFailure` signal is
|
||||
// emitted.
|
||||
logger.debug() << "Activating interface";
|
||||
auto emit_failure_guard = qScopeGuard([this] { emit activationFailure(); });
|
||||
|
||||
if (m_connections.contains(config.m_hopindex)) {
|
||||
if (m_connections.contains(config.m_hopType)) {
|
||||
if (supportServerSwitching(config)) {
|
||||
logger.debug() << "Already connected. Server switching supported.";
|
||||
|
||||
@@ -85,10 +89,12 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
||||
bool status = run(Switch, config);
|
||||
logger.debug() << "Connection status:" << status;
|
||||
if (status) {
|
||||
m_connections[config.m_hopindex] = ConnectionState(config);
|
||||
m_connections[config.m_hopType] = ConnectionState(config);
|
||||
m_handshakeTimer.start(HANDSHAKE_POLL_MSEC);
|
||||
emit_failure_guard.dismiss();
|
||||
return true;
|
||||
}
|
||||
return status;
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.warning() << "Already connected. Server switching not supported.";
|
||||
@@ -96,8 +102,12 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_ASSERT(!m_connections.contains(config.m_hopindex));
|
||||
return activate(config);
|
||||
Q_ASSERT(!m_connections.contains(config.m_hopType));
|
||||
if (activate(config)) {
|
||||
emit_failure_guard.dismiss();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
prepareActivation(config);
|
||||
@@ -112,13 +122,7 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
||||
|
||||
// Configure routing for excluded addresses.
|
||||
for (const QString& i : config.m_excludedAddresses) {
|
||||
QHostAddress address(i);
|
||||
if (m_excludedAddrSet.contains(address)) {
|
||||
m_excludedAddrSet[address]++;
|
||||
continue;
|
||||
}
|
||||
wgutils()->addExclusionRoute(address);
|
||||
m_excludedAddrSet[address] = 1;
|
||||
addExclusionRoute(IPAddress(i));
|
||||
}
|
||||
|
||||
// Add the peer to this interface.
|
||||
@@ -142,7 +146,7 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
||||
|
||||
// set routing
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
if (!wgutils()->updateRoutePrefix(ip, config.m_hopindex)) {
|
||||
if (!wgutils()->updateRoutePrefix(ip)) {
|
||||
logger.debug() << "Routing configuration failed for"
|
||||
<< logger.sensitive(ip.toString());
|
||||
return false;
|
||||
@@ -152,15 +156,21 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
||||
bool status = run(Up, config);
|
||||
logger.debug() << "Connection status:" << status;
|
||||
if (status) {
|
||||
m_connections[config.m_hopindex] = ConnectionState(config);
|
||||
m_connections[config.m_hopType] = ConnectionState(config);
|
||||
m_handshakeTimer.start(HANDSHAKE_POLL_MSEC);
|
||||
emit_failure_guard.dismiss();
|
||||
return true;
|
||||
}
|
||||
|
||||
return status;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) {
|
||||
if ((config.m_hopindex == 0) && supportDnsUtils()) {
|
||||
if (!supportDnsUtils()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((config.m_hopType == InterfaceConfig::MultiHopExit) ||
|
||||
(config.m_hopType == InterfaceConfig::SingleHop)) {
|
||||
QList<QHostAddress> resolvers;
|
||||
resolvers.append(QHostAddress(config.m_dnsServer));
|
||||
|
||||
@@ -199,6 +209,28 @@ bool Daemon::parseStringList(const QJsonObject& obj, const QString& name,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Daemon::addExclusionRoute(const IPAddress& prefix) {
|
||||
if (m_excludedAddrSet.contains(prefix)) {
|
||||
m_excludedAddrSet[prefix]++;
|
||||
return true;
|
||||
}
|
||||
if (!wgutils()->addExclusionRoute(prefix)) {
|
||||
return false;
|
||||
}
|
||||
m_excludedAddrSet[prefix] = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Daemon::delExclusionRoute(const IPAddress& prefix) {
|
||||
Q_ASSERT(m_excludedAddrSet.contains(prefix));
|
||||
if (m_excludedAddrSet[prefix] > 1) {
|
||||
m_excludedAddrSet[prefix]--;
|
||||
return true;
|
||||
}
|
||||
m_excludedAddrSet.remove(prefix);
|
||||
return wgutils()->deleteExclusionRoute(prefix);
|
||||
}
|
||||
|
||||
// static
|
||||
bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||
#define GETVALUE(name, where, jsontype) \
|
||||
@@ -216,8 +248,8 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||
|
||||
GETVALUE("privateKey", config.m_privateKey, String);
|
||||
GETVALUE("serverPublicKey", config.m_serverPublicKey, String);
|
||||
GETVALUE("serverPort", config.m_serverPort, Double);
|
||||
GETVALUE("serverPskKey", config.m_serverPskKey, String);
|
||||
GETVALUE("serverPort", config.m_serverPort, Double);
|
||||
|
||||
config.m_deviceIpv4Address = obj.value("deviceIpv4Address").toString();
|
||||
config.m_deviceIpv6Address = obj.value("deviceIpv6Address").toString();
|
||||
@@ -247,15 +279,24 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||
config.m_dnsServer = value.toString();
|
||||
}
|
||||
|
||||
if (!obj.contains("hopindex")) {
|
||||
config.m_hopindex = 0;
|
||||
if (!obj.contains("hopType")) {
|
||||
config.m_hopType = InterfaceConfig::SingleHop;
|
||||
} else {
|
||||
QJsonValue value = obj.value("hopindex");
|
||||
if (!value.isDouble()) {
|
||||
logger.error() << "hopindex is not a number";
|
||||
QJsonValue value = obj.value("hopType");
|
||||
if (!value.isString()) {
|
||||
logger.error() << "hopType is not a string";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool okay;
|
||||
QByteArray vdata = value.toString().toUtf8();
|
||||
QMetaEnum meta = QMetaEnum::fromType<InterfaceConfig::HopType>();
|
||||
config.m_hopType =
|
||||
InterfaceConfig::HopType(meta.keyToValue(vdata.constData(), &okay));
|
||||
if (!okay) {
|
||||
logger.error() << "hopType" << value.toString() << "is not valid";
|
||||
return false;
|
||||
}
|
||||
config.m_hopindex = value.toInt();
|
||||
}
|
||||
|
||||
if (!obj.contains(JSON_ALLOWEDIPADDRESSRANGES)) {
|
||||
@@ -325,8 +366,8 @@ bool Daemon::deactivate(bool emitSignals) {
|
||||
Q_ASSERT(wgutils() != nullptr);
|
||||
|
||||
// Deactivate the main interface.
|
||||
if (m_connections.contains(0)) {
|
||||
const ConnectionState& state = m_connections.value(0);
|
||||
if (!m_connections.isEmpty()) {
|
||||
const ConnectionState& state = m_connections.first();
|
||||
if (!run(Down, state.m_config)) {
|
||||
return false;
|
||||
}
|
||||
@@ -349,9 +390,9 @@ bool Daemon::deactivate(bool emitSignals) {
|
||||
// Cleanup peers and routing
|
||||
for (const ConnectionState& state : m_connections) {
|
||||
const InterfaceConfig& config = state.m_config;
|
||||
logger.debug() << "Deleting routes for hop" << config.m_hopindex;
|
||||
logger.debug() << "Deleting routes for" << config.m_hopType;
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
wgutils()->deleteRoutePrefix(ip, config.m_hopindex);
|
||||
wgutils()->deleteRoutePrefix(ip);
|
||||
}
|
||||
wgutils()->deletePeer(config);
|
||||
}
|
||||
@@ -376,14 +417,14 @@ QString Daemon::logs() {
|
||||
return {};
|
||||
}
|
||||
|
||||
void Daemon::cleanLogs() { }
|
||||
void Daemon::cleanLogs() { }
|
||||
|
||||
bool Daemon::supportServerSwitching(const InterfaceConfig& config) const {
|
||||
if (!m_connections.contains(config.m_hopindex)) {
|
||||
if (!m_connections.contains(config.m_hopType)) {
|
||||
return false;
|
||||
}
|
||||
const InterfaceConfig& current =
|
||||
m_connections.value(config.m_hopindex).m_config;
|
||||
m_connections.value(config.m_hopType).m_config;
|
||||
|
||||
return current.m_privateKey == config.m_privateKey &&
|
||||
current.m_deviceIpv4Address == config.m_deviceIpv4Address &&
|
||||
@@ -395,21 +436,15 @@ bool Daemon::supportServerSwitching(const InterfaceConfig& config) const {
|
||||
bool Daemon::switchServer(const InterfaceConfig& config) {
|
||||
Q_ASSERT(wgutils() != nullptr);
|
||||
|
||||
logger.debug() << "Switching server for hop" << config.m_hopindex;
|
||||
logger.debug() << "Switching server for" << config.m_hopType;
|
||||
|
||||
Q_ASSERT(m_connections.contains(config.m_hopindex));
|
||||
Q_ASSERT(m_connections.contains(config.m_hopType));
|
||||
const InterfaceConfig& lastConfig =
|
||||
m_connections.value(config.m_hopindex).m_config;
|
||||
m_connections.value(config.m_hopType).m_config;
|
||||
|
||||
// Configure routing for new excluded addresses.
|
||||
for (const QString& i : config.m_excludedAddresses) {
|
||||
QHostAddress address(i);
|
||||
if (m_excludedAddrSet.contains(address)) {
|
||||
m_excludedAddrSet[address]++;
|
||||
continue;
|
||||
}
|
||||
wgutils()->addExclusionRoute(address);
|
||||
m_excludedAddrSet[address] = 1;
|
||||
addExclusionRoute(IPAddress(i));
|
||||
}
|
||||
|
||||
// Activate the new peer and its routes.
|
||||
@@ -418,7 +453,7 @@ bool Daemon::switchServer(const InterfaceConfig& config) {
|
||||
return false;
|
||||
}
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
if (!wgutils()->updateRoutePrefix(ip, config.m_hopindex)) {
|
||||
if (!wgutils()->updateRoutePrefix(ip)) {
|
||||
logger.error() << "Server switch failed to update the routing table";
|
||||
break;
|
||||
}
|
||||
@@ -426,18 +461,11 @@ bool Daemon::switchServer(const InterfaceConfig& config) {
|
||||
|
||||
// Remove routing entries for the old peer.
|
||||
for (const QString& i : lastConfig.m_excludedAddresses) {
|
||||
QHostAddress address(i);
|
||||
Q_ASSERT(m_excludedAddrSet.contains(address));
|
||||
if (m_excludedAddrSet[address] > 1) {
|
||||
m_excludedAddrSet[address]--;
|
||||
continue;
|
||||
}
|
||||
wgutils()->deleteExclusionRoute(address);
|
||||
m_excludedAddrSet.remove(address);
|
||||
delExclusionRoute(QHostAddress(i));
|
||||
}
|
||||
for (const IPAddress& ip : lastConfig.m_allowedIPAddressRanges) {
|
||||
if (!config.m_allowedIPAddressRanges.contains(ip)) {
|
||||
wgutils()->deleteRoutePrefix(ip, config.m_hopindex);
|
||||
wgutils()->deleteRoutePrefix(ip);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,7 +476,7 @@ bool Daemon::switchServer(const InterfaceConfig& config) {
|
||||
}
|
||||
}
|
||||
|
||||
m_connections[config.m_hopindex] = ConnectionState(config);
|
||||
m_connections[config.m_hopType] = ConnectionState(config);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -457,12 +485,12 @@ QJsonObject Daemon::getStatus() {
|
||||
QJsonObject json;
|
||||
logger.debug() << "Status request";
|
||||
|
||||
if (!m_connections.contains(0) || !wgutils()->interfaceExists()) {
|
||||
if (!wgutils()->interfaceExists() || m_connections.isEmpty()) {
|
||||
json.insert("connected", QJsonValue(false));
|
||||
return json;
|
||||
}
|
||||
|
||||
const ConnectionState& connection = m_connections.value(0);
|
||||
const ConnectionState& connection = m_connections.first();
|
||||
QList<WireguardUtils::PeerStatus> peers = wgutils()->getPeerStatus();
|
||||
for (const WireguardUtils::PeerStatus& status : peers) {
|
||||
if (status.m_pubkey != connection.m_config.m_serverPublicKey) {
|
||||
@@ -495,6 +523,7 @@ void Daemon::checkHandshake() {
|
||||
if (connection.m_date.isValid()) {
|
||||
continue;
|
||||
}
|
||||
logger.debug() << "awaiting" << config.m_serverPublicKey;
|
||||
|
||||
// Check if the handshake has completed.
|
||||
for (const WireguardUtils::PeerStatus& status : peers) {
|
||||
|
||||
@@ -43,11 +43,18 @@ class Daemon : public QObject {
|
||||
|
||||
signals:
|
||||
void connected(const QString& pubkey);
|
||||
/**
|
||||
* Can be fired if a call to activate() was unsucessfull
|
||||
* and connected systems should rollback
|
||||
*/
|
||||
void activationFailure();
|
||||
void disconnected();
|
||||
void backendFailure();
|
||||
|
||||
private:
|
||||
bool maybeUpdateResolvers(const InterfaceConfig& config);
|
||||
bool addExclusionRoute(const IPAddress& address);
|
||||
bool delExclusionRoute(const IPAddress& address);
|
||||
|
||||
protected:
|
||||
virtual bool run(Op op, const InterfaceConfig& config) {
|
||||
@@ -75,8 +82,8 @@ class Daemon : public QObject {
|
||||
QDateTime m_date;
|
||||
InterfaceConfig m_config;
|
||||
};
|
||||
QMap<int, ConnectionState> m_connections;
|
||||
QHash<QHostAddress, int> m_excludedAddrSet;
|
||||
QMap<InterfaceConfig::HopType, ConnectionState> m_connections;
|
||||
QHash<IPAddress, int> m_excludedAddrSet;
|
||||
QTimer m_handshakeTimer;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
#ifdef MZ_MACOS
|
||||
#if defined(MZ_MACOS) || defined(MZ_LINUX)
|
||||
# include <sys/stat.h>
|
||||
# include <sys/types.h>
|
||||
# include <unistd.h>
|
||||
@@ -68,7 +68,8 @@ bool DaemonLocalServer::initialize() {
|
||||
QString DaemonLocalServer::daemonPath() const {
|
||||
#if defined(MZ_WINDOWS)
|
||||
return "\\\\.\\pipe\\amneziavpn";
|
||||
#elif defined(MZ_MACOS)
|
||||
#endif
|
||||
#if defined(MZ_MACOS) || defined(MZ_LINUX)
|
||||
QDir dir("/var/run");
|
||||
if (!dir.exists()) {
|
||||
logger.warning() << "/var/run doesn't exist. Fallback /tmp.";
|
||||
@@ -92,7 +93,5 @@ QString DaemonLocalServer::daemonPath() const {
|
||||
}
|
||||
|
||||
return VAR_PATH;
|
||||
#else
|
||||
# error Unsupported platform
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
|
||||
}
|
||||
|
||||
if (type == "deactivate") {
|
||||
Daemon::instance()->deactivate();
|
||||
Daemon::instance()->deactivate(true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/* 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 "interfaceconfig.h"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QMetaEnum>
|
||||
|
||||
QJsonObject InterfaceConfig::toJson() const {
|
||||
QJsonObject json;
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<HopType>();
|
||||
|
||||
json.insert("hopType", QJsonValue(metaEnum.valueToKey(m_hopType)));
|
||||
json.insert("privateKey", QJsonValue(m_privateKey));
|
||||
json.insert("deviceIpv4Address", QJsonValue(m_deviceIpv4Address));
|
||||
json.insert("deviceIpv6Address", QJsonValue(m_deviceIpv6Address));
|
||||
json.insert("serverPublicKey", QJsonValue(m_serverPublicKey));
|
||||
json.insert("serverPskKey", QJsonValue(m_serverPskKey));
|
||||
json.insert("serverIpv4AddrIn", QJsonValue(m_serverIpv4AddrIn));
|
||||
json.insert("serverIpv6AddrIn", QJsonValue(m_serverIpv6AddrIn));
|
||||
json.insert("serverPort", QJsonValue((double)m_serverPort));
|
||||
if ((m_hopType == InterfaceConfig::MultiHopExit) ||
|
||||
(m_hopType == InterfaceConfig::SingleHop)) {
|
||||
json.insert("serverIpv4Gateway", QJsonValue(m_serverIpv4Gateway));
|
||||
json.insert("serverIpv6Gateway", QJsonValue(m_serverIpv6Gateway));
|
||||
json.insert("dnsServer", QJsonValue(m_dnsServer));
|
||||
}
|
||||
|
||||
QJsonArray allowedIPAddesses;
|
||||
for (const IPAddress& i : m_allowedIPAddressRanges) {
|
||||
QJsonObject range;
|
||||
range.insert("address", QJsonValue(i.address().toString()));
|
||||
range.insert("range", QJsonValue((double)i.prefixLength()));
|
||||
range.insert("isIpv6",
|
||||
QJsonValue(i.type() == QAbstractSocket::IPv6Protocol));
|
||||
allowedIPAddesses.append(range);
|
||||
};
|
||||
json.insert("allowedIPAddressRanges", allowedIPAddesses);
|
||||
|
||||
QJsonArray jsExcludedAddresses;
|
||||
for (const QString& i : m_excludedAddresses) {
|
||||
jsExcludedAddresses.append(QJsonValue(i));
|
||||
}
|
||||
json.insert("excludedAddresses", jsExcludedAddresses);
|
||||
|
||||
QJsonArray disabledApps;
|
||||
for (const QString& i : m_vpnDisabledApps) {
|
||||
disabledApps.append(QJsonValue(i));
|
||||
}
|
||||
json.insert("vpnDisabledApps", disabledApps);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
|
||||
#define VALIDATE(x) \
|
||||
if (x.contains("\n")) return "";
|
||||
|
||||
VALIDATE(m_privateKey);
|
||||
VALIDATE(m_deviceIpv4Address);
|
||||
VALIDATE(m_deviceIpv6Address);
|
||||
VALIDATE(m_serverIpv4Gateway);
|
||||
VALIDATE(m_serverIpv6Gateway);
|
||||
VALIDATE(m_serverPublicKey);
|
||||
VALIDATE(m_serverIpv4AddrIn);
|
||||
VALIDATE(m_serverIpv6AddrIn);
|
||||
#undef VALIDATE
|
||||
|
||||
QString content;
|
||||
QTextStream out(&content);
|
||||
out << "[Interface]\n";
|
||||
out << "PrivateKey = " << m_privateKey << "\n";
|
||||
|
||||
QStringList addresses;
|
||||
if (!m_deviceIpv4Address.isNull()) {
|
||||
addresses.append(m_deviceIpv4Address);
|
||||
}
|
||||
if (!m_deviceIpv6Address.isNull()) {
|
||||
addresses.append(m_deviceIpv6Address);
|
||||
}
|
||||
if (addresses.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
out << "Address = " << addresses.join(", ") << "\n";
|
||||
|
||||
if (!m_dnsServer.isNull()) {
|
||||
QStringList dnsServers(m_dnsServer);
|
||||
// If the DNS is not the Gateway, it's a user defined DNS
|
||||
// thus, not add any other :)
|
||||
if (m_dnsServer == m_serverIpv4Gateway) {
|
||||
dnsServers.append(m_serverIpv6Gateway);
|
||||
}
|
||||
out << "DNS = " << dnsServers.join(", ") << "\n";
|
||||
}
|
||||
|
||||
// If any extra config was provided, append it now.
|
||||
for (const QString& key : extra.keys()) {
|
||||
out << key << " = " << extra[key] << "\n";
|
||||
}
|
||||
|
||||
out << "\n[Peer]\n";
|
||||
out << "PublicKey = " << m_serverPublicKey << "\n";
|
||||
out << "Endpoint = " << m_serverIpv4AddrIn.toUtf8() << ":" << m_serverPort
|
||||
<< "\n";
|
||||
|
||||
/* In theory, we should use the ipv6 endpoint, but wireguard doesn't seem
|
||||
* to be happy if there are 2 endpoints.
|
||||
out << "Endpoint = [" << config.m_serverIpv6AddrIn << "]:"
|
||||
<< config.m_serverPort << "\n";
|
||||
*/
|
||||
QStringList ranges;
|
||||
for (const IPAddress& ip : m_allowedIPAddressRanges) {
|
||||
ranges.append(ip.toString());
|
||||
}
|
||||
out << "AllowedIPs = " << ranges.join(", ") << "\n";
|
||||
|
||||
return content;
|
||||
}
|
||||
@@ -10,22 +10,39 @@
|
||||
|
||||
#include "ipaddress.h"
|
||||
|
||||
struct InterfaceConfig {
|
||||
int m_hopindex = 0;
|
||||
class QJsonObject;
|
||||
|
||||
class InterfaceConfig {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
InterfaceConfig() {}
|
||||
|
||||
enum HopType { SingleHop, MultiHopEntry, MultiHopExit };
|
||||
Q_ENUM(HopType)
|
||||
|
||||
HopType m_hopType;
|
||||
QString m_privateKey;
|
||||
QString m_deviceIpv4Address;
|
||||
QString m_deviceIpv6Address;
|
||||
QString m_serverIpv4Gateway;
|
||||
QString m_serverIpv6Gateway;
|
||||
QString m_serverPublicKey;
|
||||
QString m_serverPskKey;
|
||||
QString m_serverIpv4AddrIn;
|
||||
QString m_serverPskKey;
|
||||
QString m_serverIpv6AddrIn;
|
||||
QString m_dnsServer;
|
||||
int m_serverPort = 0;
|
||||
QList<IPAddress> m_allowedIPAddressRanges;
|
||||
QStringList m_excludedAddresses;
|
||||
QStringList m_vpnDisabledApps;
|
||||
#if defined(MZ_ANDROID) || defined(MZ_IOS)
|
||||
QString m_installationId;
|
||||
#endif
|
||||
|
||||
QJsonObject toJson() const;
|
||||
QString toWgConf(
|
||||
const QMap<QString, QString>& extra = QMap<QString, QString>()) const;
|
||||
};
|
||||
|
||||
#endif // INTERFACECONFIG_H
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#ifndef WIREGUARDUTILS_H
|
||||
#define WIREGUARDUTILS_H
|
||||
|
||||
#define _WINSOCKAPI_
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QHostAddress>
|
||||
#include <QObject>
|
||||
@@ -12,7 +14,7 @@
|
||||
|
||||
#include "interfaceconfig.h"
|
||||
|
||||
constexpr const char* WG_INTERFACE = "moz0";
|
||||
constexpr const char* WG_INTERFACE = "amn0";
|
||||
|
||||
constexpr uint16_t WG_KEEPALIVE_PERIOD = 60;
|
||||
|
||||
@@ -41,11 +43,11 @@ class WireguardUtils : public QObject {
|
||||
virtual bool deletePeer(const InterfaceConfig& config) = 0;
|
||||
virtual QList<PeerStatus> getPeerStatus() = 0;
|
||||
|
||||
virtual bool updateRoutePrefix(const IPAddress& prefix, int hopindex) = 0;
|
||||
virtual bool deleteRoutePrefix(const IPAddress& prefix, int hopindex) = 0;
|
||||
virtual bool updateRoutePrefix(const IPAddress& prefix) = 0;
|
||||
virtual bool deleteRoutePrefix(const IPAddress& prefix) = 0;
|
||||
|
||||
virtual bool addExclusionRoute(const QHostAddress& address) = 0;
|
||||
virtual bool deleteExclusionRoute(const QHostAddress& address) = 0;
|
||||
virtual bool addExclusionRoute(const IPAddress& prefix) = 0;
|
||||
virtual bool deleteExclusionRoute(const IPAddress& prefix) = 0;
|
||||
};
|
||||
|
||||
#endif // WIREGUARDUTILS_H
|
||||
|
||||
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 236 KiB |
@@ -0,0 +1,18 @@
|
||||
<svg width="280" height="280" viewBox="0 0 280 280" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_f_1379_19114)">
|
||||
<circle cx="140" cy="140" r="107.5" stroke="#FBB36A"/>
|
||||
</g>
|
||||
<circle cx="140" cy="140" r="107" stroke="#FBB36A" stroke-width="2"/>
|
||||
<circle cx="140" cy="140" r="107" stroke="url(#paint0_linear_1379_19114)" stroke-width="2"/>
|
||||
<defs>
|
||||
<filter id="filter0_f_1379_19114" x="2" y="2" width="276" height="276" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="15" result="effect1_foregroundBlur_1379_19114"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_1379_19114" x1="-2.43527" y1="89.3291" x2="192.652" y2="11.9798" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E0AA84"/>
|
||||
<stop offset="1" stop-color="#DF7D37"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 969 B |
@@ -0,0 +1,17 @@
|
||||
<svg width="280" height="280" viewBox="0 0 280 280" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d_1379_19118)">
|
||||
<path d="M140 235C127.524 235 115.171 232.543 103.645 227.769C92.1191 222.994 81.6464 215.997 72.8248 207.175C64.0033 198.354 57.0056 187.881 52.2314 176.355C47.4572 164.829 45 152.476 45 140C45 127.524 47.4572 115.171 52.2314 103.645C57.0056 92.1191 64.0033 81.6464 72.8249 72.8248C81.6464 64.0033 92.1191 57.0056 103.645 52.2314C115.171 47.4572 127.524 45 140 45C152.476 45 164.829 47.4572 176.355 52.2314C187.881 57.0056 198.354 64.0033 207.175 72.8249C215.997 81.6464 222.994 92.1192 227.769 103.645C232.543 115.171 235 127.524 235 140C235 152.476 232.543 164.829 227.769 176.355C222.994 187.881 215.997 198.354 207.175 207.175C198.354 215.997 187.881 222.994 176.355 227.769C164.829 232.543 152.476 235 140 235L140 235Z" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_1379_19118" x="38" y="38" width="204" height="204" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="3"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.2 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1379_19118"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1379_19118" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,30 @@
|
||||
<svg width="280" height="280" viewBox="0 0 280 280" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.1" filter="url(#filter0_d_1379_19115)">
|
||||
<path d="M235 140C235 152.476 232.543 164.829 227.769 176.355C222.994 187.881 215.997 198.354 207.175 207.175C198.354 215.997 187.881 222.994 176.355 227.769C164.829 232.543 152.476 235 140 235C127.524 235 115.171 232.543 103.645 227.769C92.1191 222.994 81.6464 215.997 72.8249 207.175C64.0033 198.354 57.0056 187.881 52.2314 176.355C47.4572 164.829 45 152.476 45 140C45 127.524 47.4572 115.171 52.2314 103.645C57.0056 92.1191 64.0033 81.6464 72.8249 72.8248C81.6464 64.0033 92.1192 57.0056 103.645 52.2314C115.171 47.4572 127.524 45 140 45C152.476 45 164.829 47.4573 176.355 52.2314C187.881 57.0056 198.354 64.0033 207.175 72.8249C215.997 81.6464 222.994 92.1192 227.769 103.645C232.543 115.171 235 127.524 235 140L235 140Z" stroke="#FBB36A"/>
|
||||
</g>
|
||||
<g filter="url(#filter1_d_1379_19115)">
|
||||
<path d="M140 235C126.016 235 112.204 231.913 99.551 225.959C86.8977 220.004 75.7151 211.33 66.8012 200.555C57.8874 189.78 51.4623 177.17 47.9846 163.626C44.5069 150.081 44.0623 135.935 46.6827 122.199C49.3031 108.462 54.9237 95.4738 63.1434 84.1604C71.363 72.847 81.979 63.4878 94.2334 56.7509C106.488 50.014 120.078 46.0655 134.035 45.1875C147.991 44.3094 161.97 46.5233 174.972 51.6712" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_1379_19115" x="38.5" y="38.5" width="203" height="203" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="3"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.984314 0 0 0 0 0.717647 0 0 0 0 0.317647 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1379_19115"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1379_19115" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter1_d_1379_19115" x="38" y="38" width="143.973" height="204" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="3"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.988235 0 0 0 0 0.301961 0 0 0 0 0.0705883 0 0 0 0.49 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1379_19115"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1379_19115" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17 10C18.6569 10 20 8.65685 20 7C20 5.34315 18.6569 4 17 4C15.3431 4 14 5.34315 14 7C14 8.65685 15.3431 10 17 10Z" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<rect x="4" y="4" width="6" height="6" rx="1" stroke="#D7D8DB" stroke-width="2"/>
|
||||
<rect x="14" y="14" width="6" height="6" rx="1" stroke="#D7D8DB" stroke-width="2"/>
|
||||
<path d="M7.18963 13.8523L10.8078 20H2.91364L7.18963 13.8523Z" stroke="#D7D8DB" stroke-width="2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 576 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19 12H5" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 19L5 12L12 5" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 315 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5 12H19" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 5L19 12L12 19" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 316 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20 6L9 17L4 12" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 212 B |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 9L12 15L18 9" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 212 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9 18L15 12L9 6" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 212 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18 15L12 9L6 15" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 213 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20 9H11C9.89543 9 9 9.89543 9 11V20C9 21.1046 9.89543 22 11 22H20C21.1046 22 22 21.1046 22 20V11C22 9.89543 21.1046 9 20 9Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M5 15H4C3.46957 15 2.96086 14.7893 2.58579 14.4142C2.21071 14.0391 2 13.5304 2 13V4C2 3.46957 2.21071 2.96086 2.58579 2.58579C2.96086 2.21071 3.46957 2 4 2H13C13.5304 2 14.0391 2.21071 14.4142 2.58579C14.7893 2.96086 15 3.46957 15 4V5" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 649 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.332 5H9.33203L2.33203 12L9.33203 19H20.332C20.8625 19 21.3712 18.7893 21.7462 18.4142C22.1213 18.0391 22.332 17.5304 22.332 17V7C22.332 6.46957 22.1213 5.96086 21.7462 5.58579C21.3712 5.21071 20.8625 5 20.332 5V5Z" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M18.332 9L12.332 15" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12.332 9L18.332 15" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 640 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21 15V19C21 19.5304 20.7893 20.0391 20.4142 20.4142C20.0391 20.7893 19.5304 21 19 21H5C4.46957 21 3.96086 20.7893 3.58579 20.4142C3.21071 20.0391 3 19.5304 3 19V15" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7 10L12 15L17 10" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 15V3" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 574 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 20H21" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M16.5 3.49998C16.8978 3.10216 17.4374 2.87866 18 2.87866C18.2786 2.87866 18.5544 2.93353 18.8118 3.04014C19.0692 3.14674 19.303 3.303 19.5 3.49998C19.697 3.69697 19.8532 3.93082 19.9598 4.18819C20.0665 4.44556 20.1213 4.72141 20.1213 4.99998C20.1213 5.27856 20.0665 5.55441 19.9598 5.81178C19.8532 6.06915 19.697 6.303 19.5 6.49998L7 19L3 20L4 16L16.5 3.49998Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 660 B |
@@ -0,0 +1,6 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.88 9.87988C9.58526 10.1545 9.34885 10.4857 9.18488 10.8537C9.02091 11.2217 8.93274 11.619 8.92564 12.0218C8.91853 12.4246 8.99263 12.8247 9.14351 13.1983C9.2944 13.5718 9.51898 13.9112 9.80385 14.196C10.0887 14.4809 10.4281 14.7055 10.8016 14.8564C11.1752 15.0073 11.5753 15.0814 11.9781 15.0742C12.3809 15.0671 12.7782 14.979 13.1462 14.815C13.5142 14.651 13.8454 14.4146 14.12 14.1199" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.73 5.08C11.1513 5.02751 11.5754 5.00079 12 5C19 5 22 12 22 12C21.5529 12.9571 20.9922 13.8569 20.33 14.68" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.61 6.61011C4.62125 7.96473 3.02987 9.82537 2 12.0001C2 12.0001 5 19.0001 12 19.0001C13.9159 19.0052 15.7908 18.4452 17.39 17.3901" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M2 2L22 22" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2 12C2 12 5 5 12 5C19 5 22 12 22 12C22 12 19 19 12 19C5 19 2 12 2 12Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 474 B |
@@ -0,0 +1,11 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.5 2H6C5.46957 2 4.96086 2.21071 4.58579 2.58579C4.21071 2.96086 4 3.46957 4 4V20C4 20.5304 4.21071 21.0391 4.58579 21.4142C4.96086 21.7893 5.46957 22 6 22H18C18.5304 22 19.0391 21.7893 19.4142 21.4142C19.7893 21.0391 20 20.5304 20 20V7.5L14.5 2Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M14 2V8H20" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 17C13.1046 17 14 16.1046 14 15C14 13.8954 13.1046 13 12 13C10.8954 13 10 13.8954 10 15C10 16.1046 10.8954 17 12 17Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 12V13" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 17V18" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M14.6 13.5L13.73 14" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.27 16L9.40002 16.5" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M14.6 16.5L13.73 16" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.27 14L9.40002 13.5" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 14L7.45 11.1C7.61696 10.7687 7.87281 10.4903 8.18893 10.296C8.50504 10.1018 8.86897 9.99927 9.24 10H20C20.3055 9.99946 20.6071 10.0689 20.8816 10.2031C21.1561 10.3372 21.3963 10.5325 21.5836 10.7739C21.7709 11.0152 21.9004 11.2963 21.9622 11.5956C22.024 11.8948 22.0164 12.2042 21.94 12.5L20.39 18.5C20.279 18.9299 20.0281 19.3106 19.6769 19.5822C19.3256 19.8538 18.894 20.0008 18.45 20H4C3.46957 20 2.96086 19.7893 2.58579 19.4142C2.21071 19.0391 2 18.5304 2 18V5C2 3.9 2.9 3 4 3H7.93C8.25941 3.0017 8.58331 3.08475 8.8729 3.24176C9.1625 3.39877 9.40882 3.62488 9.59 3.9L10.41 5.1C10.5912 5.37512 10.8375 5.60123 11.1271 5.75824C11.4167 5.91525 11.7406 5.9983 12.07 6H18C18.5304 6 19.0391 6.21071 19.4142 6.58579C19.7893 6.96086 20 7.46957 20 8V10" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 948 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15 22V18C15.1392 16.7473 14.78 15.4901 14 14.5C17 14.5 20 12.5 20 9C20.08 7.75 19.73 6.52 19 5.5C19.28 4.35 19.28 3.15 19 2C19 2 18 2 16 3.5C13.36 3 10.64 3 8.00004 3.5C6.00004 2 5.00004 2 5.00004 2C4.70004 3.15 4.70004 4.35 5.00004 5.5C4.27191 6.51588 3.91851 7.75279 4.00004 9C4.00004 12.5 7.00004 14.5 10 14.5C9.61004 14.99 9.32004 15.55 9.15004 16.15C8.98004 16.75 8.93004 17.38 9.00004 18V22" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M9 18C4.49 20 4 16 2 16" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 711 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3 9L12 2L21 9V20C21 20.5304 20.7893 21.0391 20.4142 21.4142C20.0391 21.7893 19.5304 22 19 22H5C4.46957 22 3.96086 21.7893 3.58579 21.4142C3.21071 21.0391 3 20.5304 3 20V9Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M9 22V12H15V22" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 477 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20 4H4C2.89543 4 2 4.89543 2 6V18C2 19.1046 2.89543 20 4 20H20C21.1046 20 22 19.1046 22 18V6C22 4.89543 21.1046 4 20 4Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M22 7L13.03 12.7C12.7213 12.8934 12.3643 12.996 12 12.996C11.6357 12.996 11.2787 12.8934 10.97 12.7L2 7" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 514 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 6C12.5523 6 13 5.55228 13 5C13 4.44772 12.5523 4 12 4C11.4477 4 11 4.44772 11 5C11 5.55228 11.4477 6 12 6Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 20C12.5523 20 13 19.5523 13 19C13 18.4477 12.5523 18 12 18C11.4477 18 11 18.4477 11 19C11 19.5523 11.4477 20 12 20Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 733 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 5V19" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M5 12H19" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 307 B |
@@ -0,0 +1,14 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7 3H4C3.44772 3 3 3.44772 3 4V7C3 7.55228 3.44772 8 4 8H7C7.55228 8 8 7.55228 8 7V4C8 3.44772 7.55228 3 7 3Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M20 3H17C16.4477 3 16 3.44772 16 4V7C16 7.55228 16.4477 8 17 8H20C20.5523 8 21 7.55228 21 7V4C21 3.44772 20.5523 3 20 3Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7 16H4C3.44772 16 3 16.4477 3 17V20C3 20.5523 3.44772 21 4 21H7C7.55228 21 8 20.5523 8 20V17C8 16.4477 7.55228 16 7 16Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M21 16H18C17.4696 16 16.9609 16.2107 16.5858 16.5858C16.2107 16.9609 16 17.4696 16 18V21" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M21 21V21.01" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 7V10C12 10.5304 11.7893 11.0391 11.4142 11.4142C11.0391 11.7893 10.5304 12 10 12H7" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3 12H3.01" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 3H12.01" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 16V16.01" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M16 12H17" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M21 12V12.01" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 21V20" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.5" y="0.5" width="23" height="23" rx="11.5" stroke="#A85809"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 177 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.5" y="0.5" width="23" height="23" rx="11.5" stroke="#878B91"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 177 B |
@@ -0,0 +1,7 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 14C13.1046 14 14 13.1046 14 12C14 10.8954 13.1046 10 12 10C10.8954 10 10 10.8954 10 12C10 13.1046 10.8954 14 12 14Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4.93006 19.0702C3.05535 17.1949 2.0022 14.6518 2.0022 12.0002C2.0022 9.34853 3.05535 6.80545 4.93006 4.93018" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7.75993 16.24C7.20263 15.6818 6.76087 15.0191 6.45993 14.29C5.85157 12.8205 5.85157 11.1695 6.45993 9.7C6.76087 8.97087 7.20263 8.30823 7.75993 7.75" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M16.24 7.75977C16.8028 8.33271 17.2449 9.01281 17.54 9.75977C18.1483 11.2293 18.1483 12.8802 17.54 14.3498C17.2391 15.0789 16.7973 15.7415 16.24 16.2998" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M19.0701 4.93018C20.9448 6.80545 21.9979 9.34853 21.9979 12.0002C21.9979 14.6518 20.9448 17.1949 19.0701 19.0702" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 491 B |
|
Before Width: | Height: | Size: 624 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19 21H5C4.46957 21 3.96086 20.7893 3.58579 20.4142C3.21071 20.0391 3 19.5304 3 19V5C3 4.46957 3.21071 3.96086 3.58579 3.58579C3.96086 3.21071 4.46957 3 5 3H16L21 8V19C21 19.5304 20.7893 20.0391 20.4142 20.4142C20.0391 20.7893 19.5304 21 19 21Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M17 21V13H7V21" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7 3V8H15" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 652 B |
@@ -0,0 +1,6 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20 2H4C2.89543 2 2 2.89543 2 4V8C2 9.10457 2.89543 10 4 10H20C21.1046 10 22 9.10457 22 8V4C22 2.89543 21.1046 2 20 2Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M20 14H4C2.89543 14 2 14.8954 2 16V20C2 21.1046 2.89543 22 4 22H20C21.1046 22 22 21.1046 22 20V16C22 14.8954 21.1046 14 20 14Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6 6H6.01" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6 18H6.01" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 742 B |
@@ -0,0 +1,6 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20 7H11" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M14 17H5" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M17 20C18.6569 20 20 18.6569 20 17C20 15.3431 18.6569 14 17 14C15.3431 14 14 15.3431 14 17C14 18.6569 15.3431 20 17 20Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7 10C8.65685 10 10 8.65685 10 7C10 5.34315 8.65685 4 7 4C5.34315 4 4 5.34315 4 7C4 8.65685 5.34315 10 7 10Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 722 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.22 2H11.78C11.2496 2 10.7409 2.21071 10.3658 2.58579C9.99072 2.96086 9.78 3.46957 9.78 4V4.18C9.77964 4.53073 9.68706 4.87519 9.51154 5.17884C9.33602 5.48248 9.08374 5.73464 8.78 5.91L8.35 6.16C8.04596 6.33554 7.70108 6.42795 7.35 6.42795C6.99893 6.42795 6.65404 6.33554 6.35 6.16L6.2 6.08C5.74107 5.81526 5.19584 5.74344 4.684 5.88031C4.17217 6.01717 3.73555 6.35154 3.47 6.81L3.25 7.19C2.98526 7.64893 2.91345 8.19416 3.05031 8.706C3.18717 9.21783 3.52154 9.65445 3.98 9.92L4.13 10.02C4.43228 10.1945 4.68362 10.4451 4.85905 10.7468C5.03448 11.0486 5.1279 11.391 5.13 11.74V12.25C5.1314 12.6024 5.03965 12.949 4.86405 13.2545C4.68844 13.5601 4.43521 13.8138 4.13 13.99L3.98 14.08C3.52154 14.3456 3.18717 14.7822 3.05031 15.294C2.91345 15.8058 2.98526 16.3511 3.25 16.81L3.47 17.19C3.73555 17.6485 4.17217 17.9828 4.684 18.1197C5.19584 18.2566 5.74107 18.1847 6.2 17.92L6.35 17.84C6.65404 17.6645 6.99893 17.5721 7.35 17.5721C7.70108 17.5721 8.04596 17.6645 8.35 17.84L8.78 18.09C9.08374 18.2654 9.33602 18.5175 9.51154 18.8212C9.68706 19.1248 9.77964 19.4693 9.78 19.82V20C9.78 20.5304 9.99072 21.0391 10.3658 21.4142C10.7409 21.7893 11.2496 22 11.78 22H12.22C12.7504 22 13.2591 21.7893 13.6342 21.4142C14.0093 21.0391 14.22 20.5304 14.22 20V19.82C14.2204 19.4693 14.3129 19.1248 14.4885 18.8212C14.664 18.5175 14.9163 18.2654 15.22 18.09L15.65 17.84C15.954 17.6645 16.2989 17.5721 16.65 17.5721C17.0011 17.5721 17.346 17.6645 17.65 17.84L17.8 17.92C18.2589 18.1847 18.8042 18.2566 19.316 18.1197C19.8278 17.9828 20.2645 17.6485 20.53 17.19L20.75 16.8C21.0147 16.3411 21.0866 15.7958 20.9497 15.284C20.8128 14.7722 20.4785 14.3356 20.02 14.07L19.87 13.99C19.5648 13.8138 19.3116 13.5601 19.136 13.2545C18.9604 12.949 18.8686 12.6024 18.87 12.25V11.75C18.8686 11.3976 18.9604 11.051 19.136 10.7455C19.3116 10.4399 19.5648 10.1862 19.87 10.01L20.02 9.92C20.4785 9.65445 20.8128 9.21783 20.9497 8.706C21.0866 8.19416 21.0147 7.64893 20.75 7.19L20.53 6.81C20.2645 6.35154 19.8278 6.01717 19.316 5.88031C18.8042 5.74344 18.2589 5.81526 17.8 6.08L17.65 6.16C17.346 6.33554 17.0011 6.42795 16.65 6.42795C16.2989 6.42795 15.954 6.33554 15.65 6.16L15.22 5.91C14.9163 5.73464 14.664 5.48248 14.4885 5.17884C14.3129 4.87519 14.2204 4.53073 14.22 4.18V4C14.22 3.46957 14.0093 2.96086 13.6342 2.58579C13.2591 2.21071 12.7504 2 12.22 2V2Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1,7 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18 8C19.6569 8 21 6.65685 21 5C21 3.34315 19.6569 2 18 2C16.3431 2 15 3.34315 15 5C15 6.65685 16.3431 8 18 8Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6 15C7.65685 15 9 13.6569 9 12C9 10.3431 7.65685 9 6 9C4.34315 9 3 10.3431 3 12C3 13.6569 4.34315 15 6 15Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M18 22C19.6569 22 21 20.6569 21 19C21 17.3431 19.6569 16 18 16C16.3431 16 15 17.3431 15 19C15 20.6569 16.3431 22 18 22Z" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8.58997 13.5098L15.42 17.4898" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M15.41 6.50977L8.58997 10.4898" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 969 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.44009 10.8882C8.78796 8.6139 12.3627 7.1439 14.1358 6.42277C19.2263 4.37033 20.2844 4.00977 20.9708 4.00977C21.1138 4.00977 21.457 4.0375 21.6857 4.20392C21.8573 4.34259 21.9145 4.53674 21.9431 4.67542C21.9717 4.8141 22.0003 5.11919 21.9717 5.36882C21.6857 8.17012 20.5132 15.0208 19.8841 18.155C19.6267 19.4863 19.1119 19.9301 18.6257 19.9855C17.5676 20.0687 16.7383 19.2921 15.7087 18.6542C14.1072 17.628 13.408 17.1299 11.8351 16.1314C10.0334 14.9665 11.2059 14.3286 12.2355 13.3024C12.4929 13.025 16.9956 8.75257 17.0814 8.36427C17.0814 8.3088 17.11 8.14239 16.9956 8.05918C16.8812 7.97597 16.7383 8.00371 16.6239 8.03145C16.4523 8.05918 13.8784 9.72332 8.87375 12.9961C8.1302 13.4954 7.47244 13.7173 6.87188 13.7173C6.21412 13.7173 4.9558 13.3567 4.01206 13.0516C2.86813 12.691 1.95299 12.4969 2.03878 11.8867C2.12458 11.5539 2.58215 11.2211 3.44009 10.8882Z" stroke="#D7D8DB" stroke-width="2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17 22H16C14.9391 22 13.9217 21.5786 13.1716 20.8284C12.4214 20.0783 12 19.0609 12 18V6C12 4.93913 12.4214 3.92172 13.1716 3.17157C13.9217 2.42143 14.9391 2 16 2H17" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7 22H8C9.06087 22 10.0783 21.5786 10.8284 20.8284C11.5786 20.0783 12 19.0609 12 18V17" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7 2H8C9.06087 2 10.0783 2.42143 10.8284 3.17157C11.5786 3.92172 12 4.93913 12 6V7" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 717 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3 6H21" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M19 6V20C19 21 18 22 17 22H7C6 22 5 21 5 20V6" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8 6V4C8 3 9 2 10 2H14C15 2 16 3 16 4V6" stroke="#CBCBCB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 476 B |
@@ -2,20 +2,19 @@
|
||||
#include <QTimer>
|
||||
|
||||
#include "amnezia_application.h"
|
||||
#include "version.h"
|
||||
#include "migrations.h"
|
||||
#include "version.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "Windows.h"
|
||||
#include "Windows.h"
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_IOS)
|
||||
#include "platforms/ios/QtAppDelegate-C-Interface.h"
|
||||
#include "platforms/ios/QtAppDelegate-C-Interface.h"
|
||||
#endif
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Migrations migrationsManager;
|
||||
@@ -27,16 +26,14 @@ int main(int argc, char *argv[])
|
||||
AllowSetForegroundWindow(ASFW_ANY);
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
AmneziaApplication app(argc, argv);
|
||||
#else
|
||||
AmneziaApplication app(argc, argv, true, SingleApplication::Mode::User | SingleApplication::Mode::SecondaryNotification);
|
||||
AmneziaApplication app(argc, argv, true,
|
||||
SingleApplication::Mode::User | SingleApplication::Mode::SecondaryNotification);
|
||||
|
||||
if (!app.isPrimary()) {
|
||||
QTimer::singleShot(1000, &app, [&](){
|
||||
app.quit();
|
||||
});
|
||||
QTimer::singleShot(1000, &app, [&]() { app.quit(); });
|
||||
return app.exec();
|
||||
}
|
||||
#endif
|
||||
@@ -63,6 +60,10 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (doExec) {
|
||||
app.init();
|
||||
|
||||
qInfo().noquote() << QString("Started %1 version %2").arg(APPLICATION_NAME, APP_VERSION);
|
||||
qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture());
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#ifndef CONTROLLERIMPL_H
|
||||
#define CONTROLLERIMPL_H
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <functional>
|
||||
#include <QDateTime>
|
||||
|
||||
class Keys;
|
||||
class Device;
|
||||
|
||||
@@ -52,18 +52,51 @@ DnsPingSender::DnsPingSender(const QHostAddress& source, QObject* parent)
|
||||
: PingSender(parent) {
|
||||
MZ_COUNT_CTOR(DnsPingSender);
|
||||
|
||||
if (source.isNull()) {
|
||||
m_socket.bind();
|
||||
} else {
|
||||
m_socket.bind(source);
|
||||
}
|
||||
m_source = source;
|
||||
|
||||
connect(&m_socket, &QUdpSocket::readyRead, this, &DnsPingSender::readData);
|
||||
}
|
||||
|
||||
DnsPingSender::~DnsPingSender() { MZ_COUNT_DTOR(DnsPingSender); }
|
||||
|
||||
void DnsPingSender::start() {
|
||||
auto state = m_socket.state();
|
||||
if (state != QAbstractSocket::UnconnectedState) {
|
||||
logger.info()
|
||||
<< "Attempted to start UDP socket, but it's in an invalid state:"
|
||||
<< state;
|
||||
return;
|
||||
}
|
||||
|
||||
bool bindResult = false;
|
||||
if (m_source.isNull()) {
|
||||
bindResult = m_socket.bind();
|
||||
} else {
|
||||
bindResult = m_socket.bind(m_source);
|
||||
}
|
||||
|
||||
if (!bindResult) {
|
||||
logger.error() << "Unable to bind UDP socket. Socket state:" << state;
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug() << "UDP socket bound to:"
|
||||
<< m_socket.localAddress().toString();
|
||||
return;
|
||||
}
|
||||
|
||||
void DnsPingSender::sendPing(const QHostAddress& dest, quint16 sequence) {
|
||||
if (dest.isNull()) {
|
||||
logger.error() << "Attempted to send DNS ping to invalid destination:"
|
||||
<< dest.toString() << "Ignoring.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_socket.isValid()) {
|
||||
logger.error() << "Attempted to send DNS ping, but socket is invalid.";
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray packet;
|
||||
|
||||
// Assemble a DNS query header.
|
||||
@@ -82,7 +115,14 @@ void DnsPingSender::sendPing(const QHostAddress& dest, quint16 sequence) {
|
||||
packet.append(query, sizeof(query));
|
||||
|
||||
// Send the datagram.
|
||||
m_socket.writeDatagram(packet, dest, DNS_PORT);
|
||||
logger.debug() << "Sending" << packet.size() << "bytes to UDP socket.";
|
||||
auto bytesWritten = m_socket.writeDatagram(packet, dest, DNS_PORT);
|
||||
|
||||
if (bytesWritten >= 0) {
|
||||
logger.debug() << "Number of bytes written to UDP socket:" << bytesWritten;
|
||||
} else {
|
||||
logger.error() << "Error writing to UDP socket:" << m_socket.error();
|
||||
}
|
||||
}
|
||||
|
||||
void DnsPingSender::readData() {
|
||||
@@ -112,6 +152,7 @@ void DnsPingSender::readData() {
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.debug() << "Received valid DNS reply";
|
||||
emit recvPing(qFromBigEndian<quint16>(header.id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,15 @@ class DnsPingSender final : public PingSender {
|
||||
|
||||
void sendPing(const QHostAddress& dest, quint16 sequence) override;
|
||||
|
||||
void start();
|
||||
void stop() { m_socket.close(); }
|
||||
|
||||
private:
|
||||
void readData();
|
||||
|
||||
private:
|
||||
QUdpSocket m_socket;
|
||||
QHostAddress m_source;
|
||||
};
|
||||
|
||||
#endif // DNSPINGSENDER_H
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* 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 "protocols/protocols_defs.h"
|
||||
#include "localsocketcontroller.h"
|
||||
|
||||
@@ -14,10 +13,14 @@
|
||||
#include <QJsonValue>
|
||||
#include <QStandardPaths>
|
||||
|
||||
//#include "errorhandler.h"
|
||||
#include "ipaddress.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
//#include "models/device.h"
|
||||
//#include "models/keys.h"
|
||||
#include "models/server.h"
|
||||
//#include "settingsholder.h"
|
||||
|
||||
// How many times do we try to reconnect.
|
||||
constexpr int MAX_CONNECTION_RETRY = 10;
|
||||
@@ -112,24 +115,22 @@ void LocalSocketController::daemonConnected() {
|
||||
}
|
||||
|
||||
void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
|
||||
qDebug() << rawConfig;
|
||||
QJsonObject wgConfig = rawConfig.value("wireguard_config_data").toObject();
|
||||
|
||||
QJsonObject json;
|
||||
json.insert("type", "activate");
|
||||
// json.insert("hopindex", QJsonValue((double)hop.m_hopindex));
|
||||
// json.insert("hopindex", QJsonValue((double)hop.m_hopindex));
|
||||
json.insert("privateKey", wgConfig.value(amnezia::config_key::client_priv_key));
|
||||
json.insert("deviceIpv4Address", wgConfig.value(amnezia::config_key::client_ip));
|
||||
json.insert("deviceIpv6Address", "dead::1");
|
||||
json.insert("serverPublicKey", wgConfig.value(amnezia::config_key::server_pub_key));
|
||||
json.insert("serverPskKey", wgConfig.value(amnezia::config_key::psk_key));
|
||||
json.insert("serverIpv4AddrIn", wgConfig.value(amnezia::config_key::hostName));
|
||||
// json.insert("serverIpv6AddrIn", QJsonValue(hop.m_server.ipv6AddrIn()));
|
||||
// json.insert("serverIpv6AddrIn", QJsonValue(hop.m_server.ipv6AddrIn()));
|
||||
json.insert("serverPort", wgConfig.value(amnezia::config_key::port).toInt());
|
||||
|
||||
json.insert("serverIpv4Gateway", wgConfig.value(amnezia::config_key::hostName));
|
||||
// json.insert("serverIpv6Gateway", QJsonValue(hop.m_server.ipv6Gateway()));
|
||||
// json.insert("serverIpv6Gateway", QJsonValue(hop.m_server.ipv6Gateway()));
|
||||
json.insert("dnsServer", rawConfig.value(amnezia::config_key::dns1));
|
||||
|
||||
QJsonArray jsAllowedIPAddesses;
|
||||
@@ -149,17 +150,16 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
json.insert("allowedIPAddressRanges", jsAllowedIPAddesses);
|
||||
|
||||
|
||||
QJsonArray jsExcludedAddresses;
|
||||
jsExcludedAddresses.append(wgConfig.value(amnezia::config_key::hostName));
|
||||
json.insert("excludedAddresses", jsExcludedAddresses);
|
||||
QJsonArray jsExcludedAddresses;
|
||||
jsExcludedAddresses.append(wgConfig.value(amnezia::config_key::hostName));
|
||||
json.insert("excludedAddresses", jsExcludedAddresses);
|
||||
|
||||
|
||||
// QJsonArray splitTunnelApps;
|
||||
// for (const auto& uri : hop.m_vpnDisabledApps) {
|
||||
// splitTunnelApps.append(QJsonValue(uri));
|
||||
// }
|
||||
// json.insert("vpnDisabledApps", splitTunnelApps);
|
||||
|
||||
// QJsonArray splitTunnelApps;
|
||||
// for (const auto& uri : hop.m_vpnDisabledApps) {
|
||||
// splitTunnelApps.append(QJsonValue(uri));
|
||||
// }
|
||||
// json.insert("vpnDisabledApps", splitTunnelApps);
|
||||
write(json);
|
||||
}
|
||||
|
||||
@@ -175,6 +175,7 @@ void LocalSocketController::deactivate() {
|
||||
QJsonObject json;
|
||||
json.insert("type", "deactivate");
|
||||
write(json);
|
||||
emit disconnected();
|
||||
}
|
||||
|
||||
void LocalSocketController::checkStatus() {
|
||||
|
||||
@@ -6,17 +6,20 @@
|
||||
|
||||
#include <QMetaEnum>
|
||||
|
||||
//#include "controller.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
//#include "mozillavpn.h"
|
||||
#include "networkwatcherimpl.h"
|
||||
#include "platforms/dummy/dummynetworkwatcher.h"
|
||||
//#include "settingsholder.h"
|
||||
|
||||
#ifdef MZ_WINDOWS
|
||||
//# include "platforms/windows/windowsnetworkwatcher.h"
|
||||
# include "platforms/windows/windowsnetworkwatcher.h"
|
||||
#endif
|
||||
|
||||
#ifdef MZ_LINUX
|
||||
//# include "platforms/linux/linuxnetworkwatcher.h"
|
||||
# include "platforms/linux/linuxnetworkwatcher.h"
|
||||
#endif
|
||||
|
||||
#ifdef MZ_MACOS
|
||||
@@ -51,9 +54,9 @@ void NetworkWatcher::initialize() {
|
||||
logger.debug() << "Initialize";
|
||||
|
||||
#if defined(MZ_WINDOWS)
|
||||
//m_impl = new WindowsNetworkWatcher(this);
|
||||
m_impl = new WindowsNetworkWatcher(this);
|
||||
#elif defined(MZ_LINUX)
|
||||
//m_impl = new LinuxNetworkWatcher(this);
|
||||
m_impl = new LinuxNetworkWatcher(this);
|
||||
#elif defined(MZ_MACOS)
|
||||
m_impl = new MacOSNetworkWatcher(this);
|
||||
#elif defined(MZ_WASM)
|
||||
@@ -73,11 +76,34 @@ void NetworkWatcher::initialize() {
|
||||
|
||||
m_impl->initialize();
|
||||
|
||||
//TODO IMPL FOR AMNEZIA
|
||||
|
||||
// TODO: IMPL FOR AMNEZIA
|
||||
#if 0
|
||||
SettingsHolder* settingsHolder = SettingsHolder::instance();
|
||||
Q_ASSERT(settingsHolder);
|
||||
|
||||
m_active = settingsHolder->unsecuredNetworkAlert() ||
|
||||
settingsHolder->captivePortalAlert();
|
||||
m_reportUnsecuredNetwork = settingsHolder->unsecuredNetworkAlert();
|
||||
if (m_active) {
|
||||
m_impl->start();
|
||||
}
|
||||
|
||||
connect(settingsHolder, &SettingsHolder::unsecuredNetworkAlertChanged, this,
|
||||
&NetworkWatcher::settingsChanged);
|
||||
connect(settingsHolder, &SettingsHolder::captivePortalAlertChanged, this,
|
||||
&NetworkWatcher::settingsChanged);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void NetworkWatcher::settingsChanged() {
|
||||
//TODO IMPL FOR AMNEZIA
|
||||
// TODO: IMPL FOR AMNEZIA
|
||||
#if 0
|
||||
SettingsHolder* settingsHolder = SettingsHolder::instance();
|
||||
m_active = settingsHolder->unsecuredNetworkAlert() ||
|
||||
settingsHolder->captivePortalAlert();
|
||||
m_reportUnsecuredNetwork = settingsHolder->unsecuredNetworkAlert();
|
||||
|
||||
if (m_active) {
|
||||
logger.debug()
|
||||
@@ -88,6 +114,7 @@ void NetworkWatcher::settingsChanged() {
|
||||
logger.debug() << "Stopping Network Watcher";
|
||||
m_impl->stop();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void NetworkWatcher::unsecuredNetwork(const QString& networkName,
|
||||
@@ -95,9 +122,55 @@ void NetworkWatcher::unsecuredNetwork(const QString& networkName,
|
||||
logger.debug() << "Unsecured network:" << logger.sensitive(networkName)
|
||||
<< "id:" << logger.sensitive(networkId);
|
||||
|
||||
//TODO IMPL FOR AMNEZIA
|
||||
#ifndef UNIT_TEST
|
||||
if (!m_reportUnsecuredNetwork) {
|
||||
logger.debug() << "Disabled. Ignoring unsecured network";
|
||||
return;
|
||||
}
|
||||
// TODO: IMPL FOR AMNEZIA
|
||||
#if 0
|
||||
MozillaVPN* vpn = MozillaVPN::instance();
|
||||
|
||||
if (vpn->state() != App::StateMain) {
|
||||
logger.debug() << "VPN not ready. Ignoring unsecured network";
|
||||
return;
|
||||
}
|
||||
|
||||
Controller::State state = vpn->controller()->state();
|
||||
if (state == Controller::StateOn || state == Controller::StateConnecting ||
|
||||
state == Controller::StateCheckSubscription ||
|
||||
state == Controller::StateSwitching ||
|
||||
state == Controller::StateSilentSwitching) {
|
||||
logger.debug() << "VPN on. Ignoring unsecured network";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_networks.contains(networkId)) {
|
||||
m_networks.insert(networkId, QElapsedTimer());
|
||||
} else if (!m_networks[networkId].hasExpired(NETWORK_WATCHER_TIMER_MSEC)) {
|
||||
logger.debug() << "Notification already shown. Ignoring unsecured network";
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's activate the QElapsedTimer to avoid notification loops.
|
||||
m_networks[networkId].start();
|
||||
|
||||
// We don't connect the system tray handler in the CTOR because it can be too
|
||||
// early. Maybe the NotificationHandler has not been created yet. We do it at
|
||||
// the first detection of an unsecured network.
|
||||
if (m_firstNotification) {
|
||||
connect(NotificationHandler::instance(),
|
||||
&NotificationHandler::notificationClicked, this,
|
||||
&NetworkWatcher::notificationClicked);
|
||||
m_firstNotification = false;
|
||||
}
|
||||
|
||||
NotificationHandler::instance()->unsecuredNetworkNotification(networkName);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
QString NetworkWatcher::getCurrentTransport() {
|
||||
auto type = m_impl->getTransportType();
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<NetworkWatcherImpl::TransportType>();
|
||||
|
||||
@@ -33,6 +33,8 @@ class NetworkWatcher final : public QObject {
|
||||
private:
|
||||
void settingsChanged();
|
||||
|
||||
// void notificationClicked(NotificationHandler::Message message);
|
||||
|
||||
private:
|
||||
bool m_active = false;
|
||||
bool m_reportUnsecuredNetwork = false;
|
||||
|
||||
@@ -59,6 +59,10 @@ class PingHelper final : public QObject {
|
||||
|
||||
QTimer m_pingTimer;
|
||||
PingSender* m_pingSender = nullptr;
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
friend class TestConnectionHealth;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // PINGHELPER_H
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#elif defined(MZ_MACOS) || defined(MZ_IOS)
|
||||
# include "platforms/macos/macospingsender.h"
|
||||
#elif defined(MZ_WINDOWS)
|
||||
// #include "platforms/windows/windowspingsender.h"
|
||||
# include "platforms/windows/windowspingsender.h"
|
||||
#elif defined(MZ_DUMMY) || defined(UNIT_TEST)
|
||||
# include "platforms/dummy/dummypingsender.h"
|
||||
#else
|
||||
@@ -19,13 +19,12 @@
|
||||
PingSender* PingSenderFactory::create(const QHostAddress& source,
|
||||
QObject* parent) {
|
||||
#if defined(MZ_LINUX) || defined(MZ_ANDROID)
|
||||
return nullptr;
|
||||
//return new LinuxPingSender(source, parent);
|
||||
return nullptr;
|
||||
// return new LinuxPingSender(source, parent);
|
||||
#elif defined(MZ_MACOS) || defined(MZ_IOS)
|
||||
return new MacOSPingSender(source, parent);
|
||||
#elif defined(MZ_WINDOWS)
|
||||
return nullptr;
|
||||
//return new WindowsPingSender(source, parent);
|
||||
return new WindowsPingSender(source, parent);
|
||||
#else
|
||||
return new DummyPingSender(source, parent);
|
||||
#endif
|
||||
|
||||
@@ -29,8 +29,6 @@ IPAddress::IPAddress(const QString& ip) {
|
||||
if (m_prefixLength >= 128) {
|
||||
m_prefixLength = 128;
|
||||
}
|
||||
} else {
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,4 +50,9 @@ class IPAddress final {
|
||||
int m_prefixLength;
|
||||
};
|
||||
|
||||
inline size_t qHash(const IPAddress& key, size_t seed) {
|
||||
const QHostAddress& address = key.address();
|
||||
return qHash(address, seed) ^ key.prefixLength();
|
||||
}
|
||||
|
||||
#endif // IPADDRESS_H
|
||||
|
||||
@@ -13,17 +13,16 @@
|
||||
|
||||
#include "android_controller.h"
|
||||
#include "private/qandroidextras_p.h"
|
||||
#include "ui/pages_logic/StartPageLogic.h"
|
||||
|
||||
#include "androidvpnactivity.h"
|
||||
#include "androidutils.h"
|
||||
#include "androidvpnactivity.h"
|
||||
|
||||
namespace {
|
||||
AndroidController* s_instance = nullptr;
|
||||
namespace
|
||||
{
|
||||
AndroidController *s_instance = nullptr;
|
||||
|
||||
constexpr auto PERMISSIONHELPER_CLASS =
|
||||
"org/amnezia/vpn/qt/VPNPermissionHelper";
|
||||
} // namespace
|
||||
constexpr auto PERMISSIONHELPER_CLASS = "org/amnezia/vpn/qt/VPNPermissionHelper";
|
||||
} // namespace
|
||||
|
||||
AndroidController::AndroidController() : QObject()
|
||||
{
|
||||
@@ -33,109 +32,127 @@ AndroidController::AndroidController() : QObject()
|
||||
|
||||
auto activity = AndroidVPNActivity::instance();
|
||||
|
||||
connect(activity, &AndroidVPNActivity::serviceConnected, this, []() {
|
||||
qDebug() << "Transact: service connected";
|
||||
AndroidVPNActivity::sendToService(ServiceAction::ACTION_REQUEST_STATISTIC, "");
|
||||
}, Qt::QueuedConnection);
|
||||
connect(
|
||||
activity, &AndroidVPNActivity::serviceConnected, this,
|
||||
[]() {
|
||||
qDebug() << "Transact: service connected";
|
||||
AndroidVPNActivity::sendToService(ServiceAction::ACTION_REQUEST_STATISTIC, "");
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
connect(activity, &AndroidVPNActivity::eventInitialized, this,
|
||||
[this](const QString& parcelBody) {
|
||||
// We might get multiple Init events as widgets, or fragments
|
||||
// might query this.
|
||||
if (m_init) {
|
||||
return;
|
||||
}
|
||||
connect(
|
||||
activity, &AndroidVPNActivity::eventInitialized, this,
|
||||
[this](const QString &parcelBody) {
|
||||
// We might get multiple Init events as widgets, or fragments
|
||||
// might query this.
|
||||
if (m_init) {
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "Transact: init";
|
||||
qDebug() << "Transact: init";
|
||||
|
||||
m_init = true;
|
||||
m_init = true;
|
||||
|
||||
auto doc = QJsonDocument::fromJson(parcelBody.toUtf8());
|
||||
qlonglong time = doc.object()["time"].toVariant().toLongLong();
|
||||
auto doc = QJsonDocument::fromJson(parcelBody.toUtf8());
|
||||
qlonglong time = doc.object()["time"].toVariant().toLongLong();
|
||||
|
||||
isConnected = doc.object()["connected"].toBool();
|
||||
isConnected = doc.object()["connected"].toBool();
|
||||
|
||||
if (isConnected) {
|
||||
emit scheduleStatusCheckSignal();
|
||||
}
|
||||
if (isConnected) {
|
||||
emit scheduleStatusCheckSignal();
|
||||
}
|
||||
|
||||
emit initialized(
|
||||
true, isConnected,
|
||||
time > 0 ? QDateTime::fromMSecsSinceEpoch(time) : QDateTime());
|
||||
emit initialized(true, isConnected, time > 0 ? QDateTime::fromMSecsSinceEpoch(time) : QDateTime());
|
||||
|
||||
setFallbackConnectedNotification();
|
||||
}, Qt::QueuedConnection);
|
||||
setFallbackConnectedNotification();
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
connect(activity, &AndroidVPNActivity::eventConnected, this,
|
||||
[this](const QString& parcelBody) {
|
||||
Q_UNUSED(parcelBody);
|
||||
qDebug() << "Transact: connected";
|
||||
connect(
|
||||
activity, &AndroidVPNActivity::eventConnected, this,
|
||||
[this](const QString &parcelBody) {
|
||||
Q_UNUSED(parcelBody);
|
||||
qDebug() << "Transact: connected";
|
||||
|
||||
if (!isConnected) {
|
||||
emit scheduleStatusCheckSignal();
|
||||
}
|
||||
if (!isConnected) {
|
||||
emit scheduleStatusCheckSignal();
|
||||
}
|
||||
|
||||
isConnected = true;
|
||||
isConnected = true;
|
||||
|
||||
emit connectionStateChanged(VpnProtocol::Connected);
|
||||
}, Qt::QueuedConnection);
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Connected);
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
connect(activity, &AndroidVPNActivity::eventDisconnected, this,
|
||||
[this]() {
|
||||
qDebug() << "Transact: disconnected";
|
||||
connect(
|
||||
activity, &AndroidVPNActivity::eventDisconnected, this,
|
||||
[this]() {
|
||||
qDebug() << "Transact: disconnected";
|
||||
|
||||
isConnected = false;
|
||||
isConnected = false;
|
||||
|
||||
emit connectionStateChanged(VpnProtocol::Disconnected);
|
||||
}, Qt::QueuedConnection);
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Disconnected);
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
connect(activity, &AndroidVPNActivity::eventStatisticUpdate, this,
|
||||
[this](const QString& parcelBody) {
|
||||
auto doc = QJsonDocument::fromJson(parcelBody.toUtf8());
|
||||
connect(
|
||||
activity, &AndroidVPNActivity::eventStatisticUpdate, this,
|
||||
[this](const QString &parcelBody) {
|
||||
auto doc = QJsonDocument::fromJson(parcelBody.toUtf8());
|
||||
|
||||
QString rx = doc.object()["rx_bytes"].toString();
|
||||
QString tx = doc.object()["tx_bytes"].toString();
|
||||
QString endpoint = doc.object()["endpoint"].toString();
|
||||
QString deviceIPv4 = doc.object()["deviceIpv4"].toString();
|
||||
QString rx = doc.object()["rx_bytes"].toString();
|
||||
QString tx = doc.object()["tx_bytes"].toString();
|
||||
QString endpoint = doc.object()["endpoint"].toString();
|
||||
QString deviceIPv4 = doc.object()["deviceIpv4"].toString();
|
||||
|
||||
emit statusUpdated(rx, tx, endpoint, deviceIPv4);
|
||||
}, Qt::QueuedConnection);
|
||||
emit statusUpdated(rx, tx, endpoint, deviceIPv4);
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
connect(activity, &AndroidVPNActivity::eventBackendLogs, this,
|
||||
[this](const QString& parcelBody) {
|
||||
qDebug() << "Transact: backend logs";
|
||||
connect(
|
||||
activity, &AndroidVPNActivity::eventBackendLogs, this,
|
||||
[this](const QString &parcelBody) {
|
||||
qDebug() << "Transact: backend logs";
|
||||
|
||||
QString buffer = parcelBody.toUtf8();
|
||||
if (m_logCallback) {
|
||||
m_logCallback(buffer);
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
QString buffer = parcelBody.toUtf8();
|
||||
if (m_logCallback) {
|
||||
m_logCallback(buffer);
|
||||
}
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
connect(activity, &AndroidVPNActivity::eventActivationError, this,
|
||||
[this](const QString& parcelBody) {
|
||||
Q_UNUSED(parcelBody)
|
||||
qDebug() << "Transact: error";
|
||||
emit connectionStateChanged(VpnProtocol::Error);
|
||||
}, Qt::QueuedConnection);
|
||||
connect(
|
||||
activity, &AndroidVPNActivity::eventActivationError, this,
|
||||
[this](const QString &parcelBody) {
|
||||
Q_UNUSED(parcelBody)
|
||||
qDebug() << "Transact: error";
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Error);
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
connect(activity, &AndroidVPNActivity::eventConfigImport, this,
|
||||
[this](const QString& parcelBody) {
|
||||
qDebug() << "Transact: config import";
|
||||
auto doc = QJsonDocument::fromJson(parcelBody.toUtf8());
|
||||
connect(
|
||||
activity, &AndroidVPNActivity::eventConfigImport, this,
|
||||
[this](const QString &parcelBody) {
|
||||
qDebug() << "Transact: config import";
|
||||
auto doc = QJsonDocument::fromJson(parcelBody.toUtf8());
|
||||
|
||||
QString buffer = doc.object()["config"].toString();
|
||||
qDebug() << "Transact: config string" << buffer;
|
||||
importConfig(buffer);
|
||||
}, Qt::QueuedConnection);
|
||||
QString buffer = doc.object()["config"].toString();
|
||||
qDebug() << "Transact: config string" << buffer;
|
||||
importConfigFromOutside(buffer);
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
connect(activity, &AndroidVPNActivity::serviceDisconnected, this,
|
||||
[this]() {
|
||||
qDebug() << "Transact: service disconnected";
|
||||
m_serviceConnected = false;
|
||||
}, Qt::QueuedConnection);
|
||||
connect(
|
||||
activity, &AndroidVPNActivity::serviceDisconnected, this,
|
||||
[this]() {
|
||||
qDebug() << "Transact: service disconnected";
|
||||
m_serviceConnected = false;
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
AndroidController* AndroidController::instance() {
|
||||
AndroidController *AndroidController::instance()
|
||||
{
|
||||
if (!s_instance) {
|
||||
s_instance = new AndroidController();
|
||||
}
|
||||
@@ -143,16 +160,13 @@ AndroidController* AndroidController::instance() {
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
bool AndroidController::initialize(StartPageLogic *startPageLogic)
|
||||
bool AndroidController::initialize()
|
||||
{
|
||||
qDebug() << "Initializing";
|
||||
|
||||
m_startPageLogic = startPageLogic;
|
||||
|
||||
// Hook in the native implementation for startActivityForResult into the JNI
|
||||
JNINativeMethod methods[]{{"startActivityForResult",
|
||||
"(Landroid/content/Intent;)V",
|
||||
reinterpret_cast<void*>(startActivityForResult)}};
|
||||
JNINativeMethod methods[] { { "startActivityForResult", "(Landroid/content/Intent;)V",
|
||||
reinterpret_cast<void *>(startActivityForResult) } };
|
||||
QJniObject javaClass(PERMISSIONHELPER_CLASS);
|
||||
QJniEnvironment env;
|
||||
jclass objectClass = env->GetObjectClass(javaClass.object<jobject>());
|
||||
@@ -168,11 +182,9 @@ ErrorCode AndroidController::start()
|
||||
{
|
||||
qDebug() << "Prompting for VPN permission";
|
||||
QJniObject activity = AndroidUtils::getActivity();
|
||||
auto appContext = activity.callObjectMethod(
|
||||
"getApplicationContext", "()Landroid/content/Context;");
|
||||
QJniObject::callStaticMethod<void>(
|
||||
PERMISSIONHELPER_CLASS, "startService", "(Landroid/content/Context;)V",
|
||||
appContext.object());
|
||||
auto appContext = activity.callObjectMethod("getApplicationContext", "()Landroid/content/Context;");
|
||||
QJniObject::callStaticMethod<void>(PERMISSIONHELPER_CLASS, "startService", "(Landroid/content/Context;)V",
|
||||
appContext.object());
|
||||
|
||||
QJsonDocument doc(m_vpnConfig);
|
||||
AndroidVPNActivity::sendToService(ServiceAction::ACTION_ACTIVATE, doc.toJson());
|
||||
@@ -180,7 +192,8 @@ ErrorCode AndroidController::start()
|
||||
return NoError;
|
||||
}
|
||||
|
||||
void AndroidController::stop() {
|
||||
void AndroidController::stop()
|
||||
{
|
||||
qDebug() << "AndroidController::stop";
|
||||
|
||||
AndroidVPNActivity::sendToService(ServiceAction::ACTION_DEACTIVATE, QString());
|
||||
@@ -188,16 +201,16 @@ void AndroidController::stop() {
|
||||
|
||||
// Activates the tunnel that is currently set
|
||||
// in the VPN Service
|
||||
void AndroidController::resumeStart() {
|
||||
void AndroidController::resumeStart()
|
||||
{
|
||||
AndroidVPNActivity::sendToService(ServiceAction::ACTION_RESUME_ACTIVATE, QString());
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the current notification text that is shown
|
||||
*/
|
||||
void AndroidController::setNotificationText(const QString& title,
|
||||
const QString& message,
|
||||
int timerSec) {
|
||||
void AndroidController::setNotificationText(const QString &title, const QString &message, int timerSec)
|
||||
{
|
||||
QJsonObject args;
|
||||
args["title"] = title;
|
||||
args["message"] = message;
|
||||
@@ -207,7 +220,8 @@ void AndroidController::setNotificationText(const QString& title,
|
||||
AndroidVPNActivity::sendToService(ServiceAction::ACTION_SET_NOTIFICATION_TEXT, doc.toJson());
|
||||
}
|
||||
|
||||
void AndroidController::shareConfig(const QString& configContent, const QString& suggestedName) {
|
||||
void AndroidController::shareConfig(const QString &configContent, const QString &suggestedName)
|
||||
{
|
||||
AndroidVPNActivity::saveFileAs(configContent, suggestedName);
|
||||
}
|
||||
|
||||
@@ -216,7 +230,8 @@ void AndroidController::shareConfig(const QString& configContent, const QString&
|
||||
* switches into the Connected state without the app open
|
||||
* e.g via always-on vpn
|
||||
*/
|
||||
void AndroidController::setFallbackConnectedNotification() {
|
||||
void AndroidController::setFallbackConnectedNotification()
|
||||
{
|
||||
QJsonObject args;
|
||||
args["title"] = tr("AmneziaVPN");
|
||||
//% "Ready for you to connect"
|
||||
@@ -227,11 +242,13 @@ void AndroidController::setFallbackConnectedNotification() {
|
||||
AndroidVPNActivity::sendToService(ServiceAction::ACTION_SET_NOTIFICATION_FALLBACK, doc.toJson());
|
||||
}
|
||||
|
||||
void AndroidController::checkStatus() {
|
||||
void AndroidController::checkStatus()
|
||||
{
|
||||
AndroidVPNActivity::sendToService(ServiceAction::ACTION_REQUEST_STATISTIC, QString());
|
||||
}
|
||||
|
||||
void AndroidController::getBackendLogs(std::function<void(const QString&)>&& a_callback) {
|
||||
void AndroidController::getBackendLogs(std::function<void(const QString &)> &&a_callback)
|
||||
{
|
||||
qDebug() << "get logs";
|
||||
|
||||
m_logCallback = std::move(a_callback);
|
||||
@@ -239,16 +256,13 @@ void AndroidController::getBackendLogs(std::function<void(const QString&)>&& a_c
|
||||
AndroidVPNActivity::sendToService(ServiceAction::ACTION_REQUEST_GET_LOG, QString());
|
||||
}
|
||||
|
||||
void AndroidController::cleanupBackendLogs() {
|
||||
void AndroidController::cleanupBackendLogs()
|
||||
{
|
||||
qDebug() << "cleanup logs";
|
||||
|
||||
AndroidVPNActivity::sendToService(ServiceAction::ACTION_REQUEST_CLEANUP_LOG, QString());
|
||||
}
|
||||
|
||||
void AndroidController::importConfig(const QString& data){
|
||||
m_startPageLogic->importAnyFile(data);
|
||||
}
|
||||
|
||||
const QJsonObject &AndroidController::vpnConfig() const
|
||||
{
|
||||
return m_vpnConfig;
|
||||
@@ -285,31 +299,29 @@ void AndroidController::startActivityForResult(JNIEnv *env, jobject, jobject int
|
||||
qDebug() << "start vpnPermissionHelper";
|
||||
Q_UNUSED(env);
|
||||
|
||||
QtAndroidPrivate::startActivity(intent, 1337,
|
||||
[](int receiverRequestCode, int resultCode,
|
||||
const QJniObject& data) {
|
||||
// Currently this function just used in
|
||||
// VPNService.kt::checkPermissions. So the result
|
||||
// we're getting is if the User gave us the
|
||||
// Vpn.bind permission. In case of NO we should
|
||||
// abort.
|
||||
Q_UNUSED(receiverRequestCode);
|
||||
Q_UNUSED(data);
|
||||
QtAndroidPrivate::startActivity(intent, 1337, [](int receiverRequestCode, int resultCode, const QJniObject &data) {
|
||||
// Currently this function just used in
|
||||
// VPNService.kt::checkPermissions. So the result
|
||||
// we're getting is if the User gave us the
|
||||
// Vpn.bind permission. In case of NO we should
|
||||
// abort.
|
||||
Q_UNUSED(receiverRequestCode);
|
||||
Q_UNUSED(data);
|
||||
|
||||
AndroidController* controller = AndroidController::instance();
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
AndroidController *controller = AndroidController::instance();
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (resultCode == ACTIVITY_RESULT_OK) {
|
||||
qDebug() << "VPN PROMPT RESULT - Accepted";
|
||||
controller->resumeStart();
|
||||
return;
|
||||
}
|
||||
// If the request got rejected abort the current
|
||||
// connection.
|
||||
qWarning() << "VPN PROMPT RESULT - Rejected";
|
||||
emit controller->connectionStateChanged(VpnProtocol::Disconnected);
|
||||
});
|
||||
if (resultCode == ACTIVITY_RESULT_OK) {
|
||||
qDebug() << "VPN PROMPT RESULT - Accepted";
|
||||
controller->resumeStart();
|
||||
return;
|
||||
}
|
||||
// If the request got rejected abort the current
|
||||
// connection.
|
||||
qWarning() << "VPN PROMPT RESULT - Rejected";
|
||||
emit controller->connectionStateChanged(Vpn::ConnectionState::Disconnected);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -8,36 +8,32 @@
|
||||
#include <QJniEnvironment>
|
||||
#include <QJniObject>
|
||||
|
||||
#include "ui/pages_logic/StartPageLogic.h"
|
||||
|
||||
#include "protocols/vpnprotocol.h"
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
|
||||
class AndroidController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AndroidController();
|
||||
static AndroidController* instance();
|
||||
static AndroidController *instance();
|
||||
|
||||
virtual ~AndroidController() override = default;
|
||||
|
||||
bool initialize(StartPageLogic *startPageLogic);
|
||||
bool initialize();
|
||||
|
||||
ErrorCode start();
|
||||
void stop();
|
||||
void resumeStart();
|
||||
|
||||
void checkStatus();
|
||||
void setNotificationText(const QString& title, const QString& message, int timerSec);
|
||||
void shareConfig(const QString& data, const QString& suggestedName);
|
||||
void setNotificationText(const QString &title, const QString &message, int timerSec);
|
||||
void shareConfig(const QString &data, const QString &suggestedName);
|
||||
void setFallbackConnectedNotification();
|
||||
void getBackendLogs(std::function<void(const QString&)>&& callback);
|
||||
void getBackendLogs(std::function<void(const QString &)> &&callback);
|
||||
void cleanupBackendLogs();
|
||||
void importConfig(const QString& data);
|
||||
|
||||
const QJsonObject &vpnConfig() const;
|
||||
void setVpnConfig(const QJsonObject &newVpnConfig);
|
||||
@@ -45,18 +41,20 @@ public:
|
||||
void startQrReaderActivity();
|
||||
|
||||
signals:
|
||||
void connectionStateChanged(VpnProtocol::VpnConnectionState state);
|
||||
void connectionStateChanged(Vpn::ConnectionState state);
|
||||
|
||||
// This signal is emitted when the controller is initialized. Note that the
|
||||
// VPN tunnel can be already active. In this case, "connected" should be set
|
||||
// to true and the "connectionDate" should be set to the activation date if
|
||||
// known.
|
||||
// If "status" is set to false, the backend service is considered unavailable.
|
||||
void initialized(bool status, bool connected, const QDateTime& connectionDate);
|
||||
void initialized(bool status, bool connected, const QDateTime &connectionDate);
|
||||
|
||||
void statusUpdated(QString totalRx, QString totalTx, QString endpoint, QString deviceIPv4);
|
||||
void scheduleStatusCheckSignal();
|
||||
|
||||
void importConfigFromOutside(QString &data);
|
||||
|
||||
protected slots:
|
||||
void scheduleStatusCheckSlot();
|
||||
|
||||
@@ -65,12 +63,10 @@ private:
|
||||
|
||||
QJsonObject m_vpnConfig;
|
||||
|
||||
StartPageLogic *m_startPageLogic;
|
||||
|
||||
bool m_serviceConnected = false;
|
||||
std::function<void(const QString&)> m_logCallback;
|
||||
std::function<void(const QString &)> m_logCallback;
|
||||
|
||||
static void startActivityForResult(JNIEnv* env, jobject /*thiz*/, jobject intent);
|
||||
static void startActivityForResult(JNIEnv *env, jobject /*thiz*/, jobject intent);
|
||||
|
||||
bool isConnected = false;
|
||||
|
||||
|
||||
@@ -4,23 +4,25 @@
|
||||
|
||||
#include "androidutils.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QGuiApplication>
|
||||
#include <QJniEnvironment>
|
||||
#include <QJniObject>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QNetworkCookieJar>
|
||||
#include <QUrlQuery>
|
||||
#include <QTimer>
|
||||
#include <QUrlQuery>
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
namespace {
|
||||
AndroidUtils* s_instance = nullptr;
|
||||
} // namespace
|
||||
namespace
|
||||
{
|
||||
AndroidUtils *s_instance = nullptr;
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
QString AndroidUtils::GetDeviceName() {
|
||||
QString AndroidUtils::GetDeviceName()
|
||||
{
|
||||
QJniEnvironment env;
|
||||
jclass BUILD = env->FindClass("android/os/Build");
|
||||
jfieldID model = env->GetStaticFieldID(BUILD, "MODEL", "Ljava/lang/String;");
|
||||
@@ -30,7 +32,7 @@ QString AndroidUtils::GetDeviceName() {
|
||||
return QString("Android Device");
|
||||
}
|
||||
|
||||
const char* buffer = env->GetStringUTFChars(value, nullptr);
|
||||
const char *buffer = env->GetStringUTFChars(value, nullptr);
|
||||
if (!buffer) {
|
||||
return QString("Android Device");
|
||||
}
|
||||
@@ -42,7 +44,8 @@ QString AndroidUtils::GetDeviceName() {
|
||||
};
|
||||
|
||||
// static
|
||||
AndroidUtils* AndroidUtils::instance() {
|
||||
AndroidUtils *AndroidUtils::instance()
|
||||
{
|
||||
if (!s_instance) {
|
||||
Q_ASSERT(qApp);
|
||||
s_instance = new AndroidUtils(qApp);
|
||||
@@ -51,19 +54,22 @@ AndroidUtils* AndroidUtils::instance() {
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
AndroidUtils::AndroidUtils(QObject* parent) : QObject(parent) {
|
||||
AndroidUtils::AndroidUtils(QObject *parent) : QObject(parent)
|
||||
{
|
||||
Q_ASSERT(!s_instance);
|
||||
s_instance = this;
|
||||
}
|
||||
|
||||
AndroidUtils::~AndroidUtils() {
|
||||
AndroidUtils::~AndroidUtils()
|
||||
{
|
||||
Q_ASSERT(s_instance == this);
|
||||
s_instance = nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
void AndroidUtils::dispatchToMainThread(std::function<void()> callback) {
|
||||
QTimer* timer = new QTimer();
|
||||
void AndroidUtils::dispatchToMainThread(std::function<void()> callback)
|
||||
{
|
||||
QTimer *timer = new QTimer();
|
||||
timer->moveToThread(qApp->thread());
|
||||
timer->setSingleShot(true);
|
||||
QObject::connect(timer, &QTimer::timeout, [=]() {
|
||||
@@ -74,8 +80,9 @@ void AndroidUtils::dispatchToMainThread(std::function<void()> callback) {
|
||||
}
|
||||
|
||||
// static
|
||||
QByteArray AndroidUtils::getQByteArrayFromJString(JNIEnv* env, jstring data) {
|
||||
const char* buffer = env->GetStringUTFChars(data, nullptr);
|
||||
QByteArray AndroidUtils::getQByteArrayFromJString(JNIEnv *env, jstring data)
|
||||
{
|
||||
const char *buffer = env->GetStringUTFChars(data, nullptr);
|
||||
if (!buffer) {
|
||||
qDebug() << "getQByteArrayFromJString - failed to parse data.";
|
||||
return QByteArray();
|
||||
@@ -87,8 +94,9 @@ QByteArray AndroidUtils::getQByteArrayFromJString(JNIEnv* env, jstring data) {
|
||||
}
|
||||
|
||||
// static
|
||||
QString AndroidUtils::getQStringFromJString(JNIEnv* env, jstring data) {
|
||||
const char* buffer = env->GetStringUTFChars(data, nullptr);
|
||||
QString AndroidUtils::getQStringFromJString(JNIEnv *env, jstring data)
|
||||
{
|
||||
const char *buffer = env->GetStringUTFChars(data, nullptr);
|
||||
if (!buffer) {
|
||||
qDebug() << "getQStringFromJString - failed to parse data.";
|
||||
return QString();
|
||||
@@ -100,15 +108,14 @@ QString AndroidUtils::getQStringFromJString(JNIEnv* env, jstring data) {
|
||||
}
|
||||
|
||||
// static
|
||||
QJsonObject AndroidUtils::getQJsonObjectFromJString(JNIEnv* env, jstring data) {
|
||||
QJsonObject AndroidUtils::getQJsonObjectFromJString(JNIEnv *env, jstring data)
|
||||
{
|
||||
QByteArray raw(getQByteArrayFromJString(env, data));
|
||||
QJsonParseError jsonError;
|
||||
QJsonDocument json = QJsonDocument::fromJson(raw, &jsonError);
|
||||
if (QJsonParseError::NoError != jsonError.error) {
|
||||
qDebug() << "getQJsonObjectFromJstring - error parsing json. Code: "
|
||||
<< jsonError.error << "Offset: " << jsonError.offset
|
||||
<< "Message: " << jsonError.errorString()
|
||||
<< "Data: " << raw;
|
||||
qDebug() << "getQJsonObjectFromJstring - error parsing json. Code: " << jsonError.error
|
||||
<< "Offset: " << jsonError.offset << "Message: " << jsonError.errorString() << "Data: " << raw;
|
||||
return QJsonObject();
|
||||
}
|
||||
|
||||
@@ -120,11 +127,13 @@ QJsonObject AndroidUtils::getQJsonObjectFromJString(JNIEnv* env, jstring data) {
|
||||
return json.object();
|
||||
}
|
||||
|
||||
QJniObject AndroidUtils::getActivity() {
|
||||
QJniObject AndroidUtils::getActivity()
|
||||
{
|
||||
return QNativeInterface::QAndroidApplication::context();
|
||||
}
|
||||
|
||||
int AndroidUtils::GetSDKVersion() {
|
||||
int AndroidUtils::GetSDKVersion()
|
||||
{
|
||||
QJniEnvironment env;
|
||||
jclass versionClass = env->FindClass("android/os/Build$VERSION");
|
||||
jfieldID sdkIntFieldID = env->GetStaticFieldID(versionClass, "SDK_INT", "I");
|
||||
@@ -133,15 +142,14 @@ int AndroidUtils::GetSDKVersion() {
|
||||
return sdk;
|
||||
}
|
||||
|
||||
QString AndroidUtils::GetManufacturer() {
|
||||
QString AndroidUtils::GetManufacturer()
|
||||
{
|
||||
QJniEnvironment env;
|
||||
jclass buildClass = env->FindClass("android/os/Build");
|
||||
jfieldID manuFacturerField =
|
||||
env->GetStaticFieldID(buildClass, "MANUFACTURER", "Ljava/lang/String;");
|
||||
jstring value =
|
||||
(jstring)env->GetStaticObjectField(buildClass, manuFacturerField);
|
||||
jfieldID manuFacturerField = env->GetStaticFieldID(buildClass, "MANUFACTURER", "Ljava/lang/String;");
|
||||
jstring value = (jstring)env->GetStaticObjectField(buildClass, manuFacturerField);
|
||||
|
||||
const char* buffer = env->GetStringUTFChars(value, nullptr);
|
||||
const char *buffer = env->GetStringUTFChars(value, nullptr);
|
||||
|
||||
if (!buffer) {
|
||||
qDebug() << "Failed to fetch MANUFACTURER";
|
||||
@@ -154,21 +162,22 @@ QString AndroidUtils::GetManufacturer() {
|
||||
return res;
|
||||
}
|
||||
|
||||
void AndroidUtils::runOnAndroidThreadSync(const std::function<void()> runnable) {
|
||||
QNativeInterface::QAndroidApplication::runOnAndroidMainThread(runnable)
|
||||
.waitForFinished();
|
||||
void AndroidUtils::runOnAndroidThreadSync(const std::function<void()> runnable)
|
||||
{
|
||||
QNativeInterface::QAndroidApplication::runOnAndroidMainThread(runnable).waitForFinished();
|
||||
}
|
||||
|
||||
void AndroidUtils::runOnAndroidThreadAsync(const std::function<void()> runnable) {
|
||||
void AndroidUtils::runOnAndroidThreadAsync(const std::function<void()> runnable)
|
||||
{
|
||||
QNativeInterface::QAndroidApplication::runOnAndroidMainThread(runnable);
|
||||
}
|
||||
|
||||
// Static
|
||||
// Creates a copy of the passed QByteArray in the JVM and passes back a ref
|
||||
jbyteArray AndroidUtils::tojByteArray(const QByteArray& data) {
|
||||
jbyteArray AndroidUtils::tojByteArray(const QByteArray &data)
|
||||
{
|
||||
QJniEnvironment env;
|
||||
jbyteArray out = env->NewByteArray(data.size());
|
||||
env->SetByteArrayRegion(out, 0, data.size(),
|
||||
reinterpret_cast<const jbyte*>(data.constData()));
|
||||
env->SetByteArrayRegion(out, 0, data.size(), reinterpret_cast<const jbyte *>(data.constData()));
|
||||
return out;
|
||||
}
|
||||
|
||||