Merge pull request #209 from amnezia-vpn/feature/new-gui

feature/new-gui
This commit is contained in:
pokamest 2023-09-21 12:07:22 -07:00 committed by GitHub
commit af23d9fd14
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
352 changed files with 19935 additions and 15917 deletions

1
.gitignore vendored
View file

@ -8,6 +8,7 @@ deploy/build/*
deploy/build_32/*
deploy/build_64/*
winbuild*.bat
.cache/
# Qt-es

View file

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 3.1.0.1
project(${PROJECT} VERSION 4.0.7.1
DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/"
)

@ -1 +1 @@
Subproject commit c6f0b66318f8da6917fb4681103f7303b1836194
Subproject commit 8bbaa6d8302cf0747d9786ace4dd13c7fb746502

View file

@ -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)

View file

@ -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());
}

View file

@ -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

View file

@ -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
)

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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) {

View file

@ -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;
}
}

View file

@ -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

View file

@ -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

View file

@ -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");

View file

@ -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,12 +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);
// 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);
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);
}
@ -648,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;
}
@ -677,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;
}
@ -707,18 +737,20 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential
stdOut.clear();
runScript(credentials,
replaceVars(amnezia::scriptData(SharedScriptType::check_server_is_busy),
genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
genVarsForScript(credentials)),
cbReadStdOut, cbReadStdErr);
if (stdOut.contains("Packet manager not found")) return ErrorCode::ServerPacketManagerError;
if (stdOut.contains("fuser 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(10000);
}
@ -737,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 &) {
@ -768,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);
}
}
@ -783,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;

View file

@ -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);

View file

@ -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;

View file

@ -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);

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 236 KiB

View file

@ -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

View file

@ -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

View file

@ -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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.4 KiB

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View file

@ -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

View file

@ -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

View file

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 624 B

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 KiB

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -4,7 +4,6 @@
#include "androidvpnactivity.h"
#include <QApplication>
#include <QJniEnvironment>
#include <QJniObject>
#include <QJsonDocument>
@ -13,19 +12,21 @@
#include "androidutils.h"
#include "jni.h"
namespace {
AndroidVPNActivity* s_instance = nullptr;
namespace
{
AndroidVPNActivity *s_instance = nullptr;
constexpr auto CLASSNAME = "org.amnezia.vpn.qt.VPNActivity";
}
AndroidVPNActivity::AndroidVPNActivity() {
AndroidVPNActivity::AndroidVPNActivity()
{
AndroidUtils::runOnAndroidThreadAsync([]() {
JNINativeMethod methods[]{
{"handleBackButton", "()Z", reinterpret_cast<bool*>(handleBackButton)},
{"onServiceMessage", "(ILjava/lang/String;)V", reinterpret_cast<void*>(onServiceMessage)},
{"qtOnServiceConnected", "()V", reinterpret_cast<void*>(onServiceConnected)},
{"qtOnServiceDisconnected", "()V", reinterpret_cast<void*>(onServiceDisconnected)},
{"onActivityMessage", "(ILjava/lang/String;)V", reinterpret_cast<void*>(onAndroidVpnActivityMessage)}
JNINativeMethod methods[] {
{ "handleBackButton", "()Z", reinterpret_cast<bool *>(handleBackButton) },
{ "onServiceMessage", "(ILjava/lang/String;)V", reinterpret_cast<void *>(onServiceMessage) },
{ "qtOnServiceConnected", "()V", reinterpret_cast<void *>(onServiceConnected) },
{ "qtOnServiceDisconnected", "()V", reinterpret_cast<void *>(onServiceDisconnected) },
{ "onActivityMessage", "(ILjava/lang/String;)V", reinterpret_cast<void *>(onAndroidVpnActivityMessage) }
};
QJniObject javaClass(CLASSNAME);
@ -36,19 +37,22 @@ AndroidVPNActivity::AndroidVPNActivity() {
});
}
void AndroidVPNActivity::maybeInit() {
void AndroidVPNActivity::maybeInit()
{
if (s_instance == nullptr) {
s_instance = new AndroidVPNActivity();
}
}
// static
bool AndroidVPNActivity::handleBackButton(JNIEnv* env, jobject thiz) {
bool AndroidVPNActivity::handleBackButton(JNIEnv *env, jobject thiz)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
}
void AndroidVPNActivity::connectService() {
void AndroidVPNActivity::connectService()
{
QJniObject::callStaticMethod<void>(CLASSNAME, "connectService", "()V");
}
@ -57,16 +61,16 @@ void AndroidVPNActivity::startQrCodeReader()
QJniObject::callStaticMethod<void>(CLASSNAME, "startQrCodeReader", "()V");
}
void AndroidVPNActivity::saveFileAs(QString fileContent, QString suggestedFilename) {
QJniObject::callStaticMethod<void>(
CLASSNAME,
"saveFileAs", "(Ljava/lang/String;Ljava/lang/String;)V",
QJniObject::fromString(fileContent).object<jstring>(),
QJniObject::fromString(suggestedFilename).object<jstring>());
void AndroidVPNActivity::saveFileAs(QString fileContent, QString suggestedFilename)
{
QJniObject::callStaticMethod<void>(CLASSNAME, "saveFileAs", "(Ljava/lang/String;Ljava/lang/String;)V",
QJniObject::fromString(fileContent).object<jstring>(),
QJniObject::fromString(suggestedFilename).object<jstring>());
}
// static
AndroidVPNActivity* AndroidVPNActivity::instance() {
AndroidVPNActivity *AndroidVPNActivity::instance()
{
if (s_instance == nullptr) {
AndroidVPNActivity::maybeInit();
}
@ -75,21 +79,19 @@ AndroidVPNActivity* AndroidVPNActivity::instance() {
}
// static
void AndroidVPNActivity::sendToService(ServiceAction type, const QString& data) {
void AndroidVPNActivity::sendToService(ServiceAction type, const QString &data)
{
int messageType = (int)type;
QJniObject::callStaticMethod<void>(
CLASSNAME,
"sendToService", "(ILjava/lang/String;)V",
static_cast<int>(messageType),
QJniObject::fromString(data).object<jstring>());
QJniObject::callStaticMethod<void>(CLASSNAME, "sendToService", "(ILjava/lang/String;)V",
static_cast<int>(messageType), QJniObject::fromString(data).object<jstring>());
}
// static
void AndroidVPNActivity::onServiceMessage(JNIEnv* env, jobject thiz,
jint messageType, jstring body) {
void AndroidVPNActivity::onServiceMessage(JNIEnv *env, jobject thiz, jint messageType, jstring body)
{
Q_UNUSED(thiz);
const char* buffer = env->GetStringUTFChars(body, nullptr);
const char *buffer = env->GetStringUTFChars(body, nullptr);
if (!buffer) {
return;
}
@ -97,38 +99,23 @@ void AndroidVPNActivity::onServiceMessage(JNIEnv* env, jobject thiz,
QString parcelBody(buffer);
env->ReleaseStringUTFChars(body, buffer);
AndroidUtils::dispatchToMainThread([messageType, parcelBody] {
AndroidVPNActivity::instance()->handleServiceMessage(messageType,
parcelBody);
AndroidVPNActivity::instance()->handleServiceMessage(messageType, parcelBody);
});
}
void AndroidVPNActivity::handleServiceMessage(int code, const QString& data) {
void AndroidVPNActivity::handleServiceMessage(int code, const QString &data)
{
auto mode = (ServiceEvents)code;
switch (mode) {
case ServiceEvents::EVENT_INIT:
emit eventInitialized(data);
break;
case ServiceEvents::EVENT_CONNECTED:
emit eventConnected(data);
break;
case ServiceEvents::EVENT_DISCONNECTED:
emit eventDisconnected(data);
break;
case ServiceEvents::EVENT_STATISTIC_UPDATE:
emit eventStatisticUpdate(data);
break;
case ServiceEvents::EVENT_BACKEND_LOGS:
emit eventBackendLogs(data);
break;
case ServiceEvents::EVENT_ACTIVATION_ERROR:
emit eventActivationError(data);
break;
case ServiceEvents::EVENT_CONFIG_IMPORT:
emit eventConfigImport(data);
break;
default:
Q_ASSERT(false);
case ServiceEvents::EVENT_INIT: emit eventInitialized(data); break;
case ServiceEvents::EVENT_CONNECTED: emit eventConnected(data); break;
case ServiceEvents::EVENT_DISCONNECTED: emit eventDisconnected(data); break;
case ServiceEvents::EVENT_STATISTIC_UPDATE: emit eventStatisticUpdate(data); break;
case ServiceEvents::EVENT_BACKEND_LOGS: emit eventBackendLogs(data); break;
case ServiceEvents::EVENT_ACTIVATION_ERROR: emit eventActivationError(data); break;
case ServiceEvents::EVENT_CONFIG_IMPORT: emit eventConfigImport(data); break;
default: Q_ASSERT(false);
}
}
@ -137,22 +124,21 @@ void AndroidVPNActivity::handleActivityMessage(int code, const QString &data)
auto mode = (UIEvents)code;
switch (mode) {
case UIEvents::QR_CODED_DECODED:
emit eventQrCodeReceived(data);
break;
default:
Q_ASSERT(false);
case UIEvents::QR_CODED_DECODED: emit eventQrCodeReceived(data); break;
default: Q_ASSERT(false);
}
}
void AndroidVPNActivity::onServiceConnected(JNIEnv* env, jobject thiz) {
void AndroidVPNActivity::onServiceConnected(JNIEnv *env, jobject thiz)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
emit AndroidVPNActivity::instance()->serviceConnected();
}
void AndroidVPNActivity::onServiceDisconnected(JNIEnv* env, jobject thiz) {
void AndroidVPNActivity::onServiceDisconnected(JNIEnv *env, jobject thiz)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
@ -162,7 +148,7 @@ void AndroidVPNActivity::onServiceDisconnected(JNIEnv* env, jobject thiz) {
void AndroidVPNActivity::onAndroidVpnActivityMessage(JNIEnv *env, jobject thiz, jint messageType, jstring message)
{
Q_UNUSED(thiz);
const char* buffer = env->GetStringUTFChars(message, nullptr);
const char *buffer = env->GetStringUTFChars(message, nullptr);
if (!buffer) {
return;
}

View file

@ -0,0 +1,16 @@
#include "authResultReceiver.h"
AuthResultReceiver::AuthResultReceiver(QSharedPointer<AuthResultNotifier> &notifier) : m_notifier(notifier)
{
}
void AuthResultReceiver::handleActivityResult(int receiverRequestCode, int resultCode, const QJniObject &data)
{
qDebug() << "receiverRequestCode" << receiverRequestCode << "resultCode" << resultCode;
if (resultCode == -1) { // ResultOK
emit m_notifier->authSuccessful();
} else {
emit m_notifier->authFailed();
}
}

View file

@ -0,0 +1,32 @@
#ifndef AUTHRESULTRECEIVER_H
#define AUTHRESULTRECEIVER_H
#include <QJniObject>
#include <private/qandroidextras_p.h>
class AuthResultNotifier : public QObject
{
Q_OBJECT
public:
AuthResultNotifier(QObject *parent = nullptr) : QObject(parent) {};
signals:
void authFailed();
void authSuccessful();
};
/* Auth result handler for Android */
class AuthResultReceiver final : public QAndroidActivityResultReceiver
{
public:
AuthResultReceiver(QSharedPointer<AuthResultNotifier> &notifier);
void handleActivityResult(int receiverRequestCode, int resultCode, const QJniObject &data) override;
private:
QSharedPointer<AuthResultNotifier> m_notifier;
};
#endif // AUTHRESULTRECEIVER_H

View file

@ -1,5 +1,15 @@
#include "MobileUtils.h"
void MobileUtils::shareText(const QStringList&) {}
MobileUtils::MobileUtils(QObject *parent) : QObject(parent)
{
}
bool MobileUtils::shareText(const QStringList &)
{
return false;
}
QString MobileUtils::openFile()
{
return QString();
}

View file

@ -4,15 +4,19 @@
#include <QObject>
#include <QStringList>
class MobileUtils : public QObject {
class MobileUtils : public QObject
{
Q_OBJECT
public:
MobileUtils() = delete;
explicit MobileUtils(QObject *parent = nullptr);
public slots:
static void shareText(const QStringList& filesToSend);
bool shareText(const QStringList &filesToSend);
QString openFile();
signals:
void finished();
};
#endif // MOBILEUTILS_H

View file

@ -3,7 +3,7 @@
#include <UIKit/UIKit.h>
#include <Security/Security.h>
#include <QDebug>
#include <QEventLoop>
static UIViewController* getViewController() {
NSArray *windows = [[UIApplication sharedApplication]windows];
@ -15,7 +15,11 @@ static UIViewController* getViewController() {
return nil;
}
void MobileUtils::shareText(const QStringList& filesToSend) {
MobileUtils::MobileUtils(QObject *parent) : QObject(parent) {
}
bool MobileUtils::shareText(const QStringList& filesToSend) {
NSMutableArray *sharingItems = [NSMutableArray new];
for (int i = 0; i < filesToSend.size(); i++) {
@ -27,11 +31,78 @@ void MobileUtils::shareText(const QStringList& filesToSend) {
if (!qtController) return;
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
__block bool isAccepted = false;
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
isAccepted = completed;
emit finished();
}];
[qtController presentViewController:activityController animated:YES completion:nil];
UIPopoverPresentationController *popController = activityController.popoverPresentationController;
if (popController) {
popController.sourceView = qtController.view;
}
QEventLoop wait;
QObject::connect(this, &MobileUtils::finished, &wait, &QEventLoop::quit);
wait.exec();
return isAccepted;
}
typedef void (^DocumentPickerClosedCallback)(NSString *path);
@interface DocumentPickerDelegate : NSObject <UIDocumentPickerDelegate>
@property (nonatomic, copy) DocumentPickerClosedCallback documentPickerClosedCallback;
@end
@implementation DocumentPickerDelegate
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
for (NSURL *url in urls) {
if (self.documentPickerClosedCallback) {
self.documentPickerClosedCallback([url path]);
}
}
}
- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller {
if (self.documentPickerClosedCallback) {
self.documentPickerClosedCallback(nil);
}
}
@end
QString MobileUtils::openFile() {
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen];
DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init];
documentPicker.delegate = documentPickerDelegate;
UIViewController *qtController = getViewController();
if (!qtController) return;
[qtController presentViewController:documentPicker animated:YES completion:nil];
__block QString filePath;
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
if (path) {
filePath = QString::fromUtf8(path.UTF8String);
} else {
filePath = QString();
}
emit finished();
};
QEventLoop wait;
QObject::connect(this, &MobileUtils::finished, &wait, &QEventLoop::quit);
wait.exec();
return filePath;
}

View file

@ -49,14 +49,15 @@
_videoPreviewPlayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession: _captureSession];
CGFloat tabBarHeight = 20.0;
CGFloat statusBarHeight = [UIApplication sharedApplication].statusBarFrame.size.height;
QRect cameraRect = _qrCodeReader->cameraSize();
CGRect cameraCGRect = CGRectMake(cameraRect.x(),
cameraRect.y() + tabBarHeight,
cameraRect.y() + statusBarHeight,
cameraRect.width(),
cameraRect.height());
[_videoPreviewPlayer setVideoGravity: AVLayerVideoGravityResizeAspect];
[_videoPreviewPlayer setVideoGravity: AVLayerVideoGravityResizeAspectFill];
[_videoPreviewPlayer setFrame: cameraCGRect];
CALayer* layer = [UIApplication sharedApplication].keyWindow.layer;

View file

@ -1,9 +1,6 @@
#ifndef QTAPPDELEGATECINTERFACE_H
#define QTAPPDELEGATECINTERFACE_H
#include "ui/pages_logic/StartPageLogic.h"
void QtAppDelegateInitialize();
void setStartPageLogic(StartPageLogic*);
#endif // QTAPPDELEGATECINTERFACE_H

View file

@ -1,9 +1,4 @@
#import <UIKit/UIKit.h>
#import "QtAppDelegate-C-Interface.h"
#include "ui/pages_logic/StartPageLogic.h"
@interface QtAppDelegate : UIResponder <UIApplicationDelegate>
+(QtAppDelegate *)sharedQtAppDelegate;
@property (nonatomic) StartPageLogic* startPageLogic;
@end

View file

@ -1,4 +1,5 @@
#import "QtAppDelegate.h"
#import "ios_controller.h"
#include <QFile>
@ -75,11 +76,15 @@
QString filePath(url.path.UTF8String);
if (filePath.isEmpty()) return NO;
QFile file(filePath);
bool isOpenFile = file.open(QIODevice::ReadOnly);
QByteArray data = file.readAll();
[QtAppDelegate sharedQtAppDelegate].startPageLogic->importAnyFile(QString(data));
if (filePath.contains("backup")) {
IosController::Instance()->importBackupFromOutside(filePath);
} else {
QFile file(filePath);
bool isOpenFile = file.open(QIODevice::ReadOnly);
QByteArray data = file.readAll();
IosController::Instance()->importConfigFromOutside(QString(data));
}
return YES;
}
return NO;
@ -92,8 +97,4 @@ void QtAppDelegateInitialize()
NSLog(@"Created a new AppDelegate");
}
void setStartPageLogic(StartPageLogic* startPage) {
[QtAppDelegate sharedQtAppDelegate].startPageLogic = startPage;
}
@end

View file

@ -4,28 +4,30 @@
#include "protocols/vpnprotocol.h"
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#import <Foundation/Foundation.h>
@class NETunnelProviderManager;
#endif
using namespace amnezia;
struct Action {
static const char* start;
static const char* restart;
static const char* stop;
static const char* getTunnelId;
static const char* getStatus;
struct Action
{
static const char *start;
static const char *restart;
static const char *stop;
static const char *getTunnelId;
static const char *getStatus;
};
struct MessageKey {
static const char* action;
static const char* tunnelId;
static const char* config;
static const char* errorCode;
static const char* host;
static const char* port;
static const char* isOnDemand;
struct MessageKey
{
static const char *action;
static const char *tunnelId;
static const char *config;
static const char *errorCode;
static const char *host;
static const char *port;
static const char *isOnDemand;
};
class IosController : public QObject
@ -33,26 +35,27 @@ class IosController : public QObject
Q_OBJECT
public:
static IosController* Instance();
static IosController *Instance();
virtual ~IosController() override = default;
bool initialize();
bool connectVpn(amnezia::Proto proto, const QJsonObject& configuration);
bool connectVpn(amnezia::Proto proto, const QJsonObject &configuration);
void disconnectVpn();
void vpnStatusDidChange(void *pNotification);
void vpnConfigurationDidChange(void *pNotification);
void getBackendLogs(std::function<void (const QString &)> &&callback);
void getBackendLogs(std::function<void(const QString &)> &&callback);
void checkStatus();
signals:
void connectionStateChanged(VpnProtocol::VpnConnectionState state);
void connectionStateChanged(Vpn::ConnectionState state);
void bytesChanged(quint64 receivedBytes, quint64 sentBytes);
void importConfigFromOutside(const QString);
void importBackupFromOutside(const QString);
protected slots:
private:
explicit IosController();
@ -66,12 +69,12 @@ private:
void startTunnel();
private:
void *m_iosControllerWrapper{};
void *m_iosControllerWrapper {};
#ifdef __OBJC__
NETunnelProviderManager *m_currentTunnel{};
NSString *m_serverAddress{};
bool isOurManager(NETunnelProviderManager* manager);
void sendVpnExtensionMessage(NSDictionary* message, std::function<void(NSDictionary*)> callback = nullptr);
NETunnelProviderManager *m_currentTunnel {};
NSString *m_serverAddress {};
bool isOurManager(NETunnelProviderManager *manager);
void sendVpnExtensionMessage(NSDictionary *message, std::function<void(NSDictionary *)> callback = nullptr);
#endif
amnezia::Proto m_proto;

View file

@ -30,22 +30,22 @@ const char* MessageKey::host = "host";
const char* MessageKey::port = "port";
const char* MessageKey::isOnDemand = "is-on-demand";
VpnProtocol::VpnConnectionState iosStatusToState(NEVPNStatus status) {
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
switch (status) {
case NEVPNStatusInvalid:
return VpnProtocol::VpnConnectionState::Unknown;
return Vpn::ConnectionState::Unknown;
case NEVPNStatusDisconnected:
return VpnProtocol::VpnConnectionState::Disconnected;
return Vpn::ConnectionState::Disconnected;
case NEVPNStatusConnecting:
return VpnProtocol::VpnConnectionState::Connecting;
return Vpn::ConnectionState::Connecting;
case NEVPNStatusConnected:
return VpnProtocol::VpnConnectionState::Connected;
return Vpn::ConnectionState::Connected;
case NEVPNStatusReasserting:
return VpnProtocol::VpnConnectionState::Connecting;
return Vpn::ConnectionState::Connecting;
case NEVPNStatusDisconnecting:
return VpnProtocol::VpnConnectionState::Disconnecting;
return Vpn::ConnectionState::Disconnecting;
default:
return VpnProtocol::VpnConnectionState::Unknown;
return Vpn::ConnectionState::Unknown;
}
}
@ -82,7 +82,7 @@ bool IosController::initialize()
@try {
if (error) {
qDebug() << "IosController::initialize : Error:" << [error.localizedDescription UTF8String];
emit connectionStateChanged(VpnProtocol::VpnConnectionState::Error);
emit connectionStateChanged(Vpn::ConnectionState::Error);
ok = false;
return;
}
@ -95,6 +95,7 @@ bool IosController::initialize()
if (manager.connection.status == NEVPNStatusConnected) {
m_currentTunnel = manager;
qDebug() << "IosController::initialize : VPN already connected";
emit connectionStateChanged(Vpn::ConnectionState::Connected);
break;
// TODO: show connected state
@ -141,7 +142,7 @@ bool IosController::connectVpn(amnezia::Proto proto, const QJsonObject& configur
@try {
if (error) {
qDebug() << "IosController::connectVpn : Error:" << [error.localizedDescription UTF8String];
emit connectionStateChanged(VpnProtocol::VpnConnectionState::Error);
emit connectionStateChanged(Vpn::ConnectionState::Error);
ok = false;
return;
}
@ -155,7 +156,7 @@ bool IosController::connectVpn(amnezia::Proto proto, const QJsonObject& configur
m_currentTunnel = manager;
qDebug() << "IosController::connectVpn : Using existing tunnel";
if (manager.connection.status == NEVPNStatusConnected) {
emit connectionStateChanged(VpnProtocol::VpnConnectionState::Connected);
emit connectionStateChanged(Vpn::ConnectionState::Connected);
return;
}
@ -344,14 +345,14 @@ void IosController::startTunnel()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (saveError) {
emit connectionStateChanged(VpnProtocol::VpnConnectionState::Error);
emit connectionStateChanged(Vpn::ConnectionState::Error);
return;
}
[m_currentTunnel loadFromPreferencesWithCompletionHandler:^(NSError *loadError) {
if (loadError) {
qDebug() << "IosController::startOpenVPN : Connect OpenVPN Tunnel Load Error" << loadError.localizedDescription.UTF8String;
emit connectionStateChanged(VpnProtocol::VpnConnectionState::Error);
emit connectionStateChanged(Vpn::ConnectionState::Error);
return;
}
@ -373,7 +374,7 @@ void IosController::startTunnel()
if (!started || startError) {
qDebug() << "IosController::startOpenVPN : Connect OpenVPN Tunnel Start Error"
<< (startError ? startError.localizedDescription.UTF8String : "");
emit connectionStateChanged(VpnProtocol::VpnConnectionState::Error);
emit connectionStateChanged(Vpn::ConnectionState::Error);
} else {
qDebug() << "IosController::startOpenVPN : Starting the tunnel succeeded";
}

View file

@ -35,14 +35,14 @@ Ikev2Protocol::~Ikev2Protocol()
void Ikev2Protocol::stop()
{
setConnectionState(VpnProtocol::Disconnecting);
setConnectionState(Vpn::ConnectionState::Disconnecting);
{
if (! disconnect_vpn() ){
qDebug()<<"We don't disconnect";
setConnectionState(VpnProtocol::Error);
setConnectionState(Vpn::ConnectionState::Error);
}
else {
setConnectionState(VpnProtocol::Disconnected);
setConnectionState(Vpn::ConnectionState::Disconnected);
}
}
}
@ -55,40 +55,40 @@ void Ikev2Protocol::newConnectionStateEventReceived(UINT unMsg, tagRASCONNSTATE
{
case RASCS_OpenPort:
//qDebug()<<__FUNCTION__ << __LINE__;
setConnectionState(Preparing);
setConnectionState(Vpn::ConnectionState::Preparing);
break;
case RASCS_PortOpened:
//qDebug()<<__FUNCTION__ << __LINE__;
setConnectionState(Preparing);
setConnectionState(Vpn::ConnectionState::Preparing);
break;
case RASCS_ConnectDevice:
//qDebug()<<__FUNCTION__ << __LINE__;
setConnectionState(Preparing);
setConnectionState(Vpn::ConnectionState::Preparing);
break;
case RASCS_DeviceConnected:
//qDebug()<<__FUNCTION__ << __LINE__;
setConnectionState(Preparing);
setConnectionState(Vpn::ConnectionState::Preparing);
break;
case RASCS_AllDevicesConnected:
//qDebug()<<__FUNCTION__ << __LINE__;
setConnectionState(Preparing);
setConnectionState(Vpn::ConnectionState::Preparing);
break;
case RASCS_Authenticate:
//qDebug()<<__FUNCTION__ << __LINE__;
setConnectionState(Preparing);
setConnectionState(Vpn::ConnectionState::Preparing);
break;
case RASCS_AuthNotify:
//qDebug()<<__FUNCTION__ << __LINE__;
if (dwError != 0) {
//qDebug() << "have error" << dwError;
setConnectionState(Disconnected);
setConnectionState(Vpn::ConnectionState::Disconnected);
} else {
//qDebug() << "RASCS_AuthNotify but no error" << dwError;
}
break;
case RASCS_AuthRetry:
//qDebug()<<__FUNCTION__ << __LINE__;
setConnectionState(Preparing);
setConnectionState(Vpn::ConnectionState::Preparing);
break;
case RASCS_AuthCallback:
qDebug()<<__FUNCTION__ << __LINE__;
@ -151,16 +151,16 @@ void Ikev2Protocol::newConnectionStateEventReceived(UINT unMsg, tagRASCONNSTATE
qDebug()<<__FUNCTION__ << __LINE__;
break;
case RASCS_PasswordExpired:
setConnectionState(Error);
setConnectionState(Vpn::ConnectionState::Error);
qDebug()<<__FUNCTION__ << __LINE__;
break;
case RASCS_Connected: // = RASCS_DONE:
setConnectionState(Connected);
setConnectionState(Vpn::ConnectionState::Connected);
//qDebug()<<__FUNCTION__ << __LINE__;
break;
case RASCS_Disconnected:
setConnectionState(Disconnected);
setConnectionState(Vpn::ConnectionState::Disconnected);
//qDebug()<<__FUNCTION__ << __LINE__;
break;
default:
@ -177,7 +177,7 @@ void Ikev2Protocol::readIkev2Configuration(const QJsonObject &configuration)
ErrorCode Ikev2Protocol::start()
{
QByteArray cert = QByteArray::fromBase64(m_config[config_key::cert].toString().toUtf8());
setConnectionState(Connecting);
setConnectionState(Vpn::ConnectionState::Connecting);
QTemporaryFile certFile;
certFile.setAutoRemove(false);

View file

@ -61,7 +61,7 @@ ErrorCode OpenVpnOverCloakProtocol::start()
m_errorHandlerConnection = connect(&m_ckProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus){
qDebug().noquote() << "OpenVpnOverCloakProtocol finished, exitCode, exiStatus" << exitCode << exitStatus;
setConnectionState(VpnProtocol::Disconnected);
setConnectionState(Vpn::ConnectionState::Disconnected);
if (exitStatus != QProcess::NormalExit){
emit protocolError(amnezia::ErrorCode::CloakExecutableCrashed);
stop();
@ -76,7 +76,7 @@ ErrorCode OpenVpnOverCloakProtocol::start()
m_ckProcess.waitForStarted();
if (m_ckProcess.state() == QProcess::ProcessState::Running) {
setConnectionState(VpnConnectionState::Connecting);
setConnectionState(Vpn::ConnectionState::Connecting);
return OpenVpnProtocol::start();
}

View file

@ -1,21 +1,20 @@
#include <QCoreApplication>
#include <QFileInfo>
#include <QProcess>
#include <QTcpSocket>
#include <QTcpServer>
#include <QRandomGenerator>
#include <QTcpServer>
#include <QTcpSocket>
#include "logger.h"
#include "version.h"
#include "utilities.h"
#include "openvpnprotocol.h"
#include "utilities.h"
#include "version.h"
OpenVpnProtocol::OpenVpnProtocol(const QJsonObject &configuration, QObject* parent) :
VpnProtocol(configuration, parent)
OpenVpnProtocol::OpenVpnProtocol(const QJsonObject &configuration, QObject *parent) : VpnProtocol(configuration, parent)
{
readOpenVpnConfiguration(configuration);
connect(&m_managementServer, &ManagementServer::readyRead, this, &OpenVpnProtocol::onReadyReadDataFromManagementServer);
connect(&m_managementServer, &ManagementServer::readyRead, this,
&OpenVpnProtocol::onReadyReadDataFromManagementServer);
}
OpenVpnProtocol::~OpenVpnProtocol()
@ -26,7 +25,7 @@ OpenVpnProtocol::~OpenVpnProtocol()
QString OpenVpnProtocol::defaultConfigFileName()
{
//qDebug() << "OpenVpnProtocol::defaultConfigFileName" << defaultConfigPath() + QString("/%1.ovpn").arg(APPLICATION_NAME);
// qDebug() << "OpenVpnProtocol::defaultConfigFileName" << defaultConfigPath() + QString("/%1.ovpn").arg(APPLICATION_NAME);
return defaultConfigPath() + QString("/%1.ovpn").arg(APPLICATION_NAME);
}
@ -41,21 +40,20 @@ QString OpenVpnProtocol::defaultConfigPath()
void OpenVpnProtocol::stop()
{
qDebug() << "OpenVpnProtocol::stop()";
setConnectionState(VpnProtocol::Disconnecting);
setConnectionState(Vpn::ConnectionState::Disconnecting);
// TODO: need refactoring
// sendTermSignal() will even return true while server connected ???
if ((m_connectionState == VpnProtocol::Preparing) ||
(m_connectionState == VpnProtocol::Connecting) ||
(m_connectionState == VpnProtocol::Connected) ||
(m_connectionState == VpnProtocol::Reconnecting)) {
if ((m_connectionState == Vpn::ConnectionState::Preparing) || (m_connectionState == Vpn::ConnectionState::Connecting)
|| (m_connectionState == Vpn::ConnectionState::Connected)
|| (m_connectionState == Vpn::ConnectionState::Reconnecting)) {
if (!sendTermSignal()) {
killOpenVpnProcess();
}
QThread::msleep(10);
m_managementServer.stop();
}
setConnectionState(VpnProtocol::Disconnected);
setConnectionState(Vpn::ConnectionState::Disconnected);
}
ErrorCode OpenVpnProtocol::prepare()
@ -67,18 +65,19 @@ ErrorCode OpenVpnProtocol::prepare()
QRemoteObjectPendingReply<QStringList> resultCheck = IpcClient::Interface()->getTapList();
resultCheck.waitForFinished();
if (resultCheck.returnValue().isEmpty()){
if (resultCheck.returnValue().isEmpty()) {
QRemoteObjectPendingReply<bool> resultInstall = IpcClient::Interface()->checkAndInstallDriver();
resultInstall.waitForFinished();
if (!resultInstall.returnValue()) return ErrorCode::OpenVpnTapAdapterError;
if (!resultInstall.returnValue())
return ErrorCode::OpenVpnTapAdapterError;
}
return ErrorCode::NoError;
}
void OpenVpnProtocol::killOpenVpnProcess()
{
if (m_openVpnProcess){
if (m_openVpnProcess) {
m_openVpnProcess->close();
}
}
@ -112,9 +111,9 @@ QString OpenVpnProtocol::configPath() const
return m_configFileName;
}
void OpenVpnProtocol::sendManagementCommand(const QString& command)
void OpenVpnProtocol::sendManagementCommand(const QString &command)
{
QIODevice *device = dynamic_cast<QIODevice*>(m_managementServer.socket().data());
QIODevice *device = dynamic_cast<QIODevice *>(m_managementServer.socket().data());
if (device) {
QTextStream stream(device);
stream << command << Qt::endl;
@ -126,11 +125,12 @@ uint OpenVpnProtocol::selectMgmtPort()
for (int i = 0; i < 100; ++i) {
quint32 port = QRandomGenerator::global()->generate();
port = (double)(65000-15001) * port / UINT32_MAX + 15001;
port = (double)(65000 - 15001) * port / UINT32_MAX + 15001;
QTcpServer s;
bool ok = s.listen(QHostAddress::LocalHost, port);
if (ok) return port;
if (ok)
return port;
}
return m_managementPort;
@ -140,7 +140,8 @@ void OpenVpnProtocol::updateRouteGateway(QString line)
{
// TODO: fix for macos
line = line.split("ROUTE_GATEWAY", Qt::SkipEmptyParts).at(1);
if (!line.contains("/")) return;
if (!line.contains("/"))
return;
m_routeGateway = line.split("/", Qt::SkipEmptyParts).first();
m_routeGateway.replace(" ", "");
qDebug() << "Set VPN route gateway" << m_routeGateway;
@ -148,7 +149,7 @@ void OpenVpnProtocol::updateRouteGateway(QString line)
ErrorCode OpenVpnProtocol::start()
{
//qDebug() << "Start OpenVPN connection";
// qDebug() << "Start OpenVPN connection";
OpenVpnProtocol::stop();
if (!QFileInfo::exists(Utils::openVpnExecPath())) {
@ -166,24 +167,25 @@ ErrorCode OpenVpnProtocol::start()
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
p.start("route", QStringList() << "-n" << "get" << "default");
p.start("route",
QStringList() << "-n"
<< "get"
<< "default");
p.waitForFinished();
QString s = p.readAll();
QString s = p.readAll();
QRegularExpression rx(R"(gateway:\s*(\d+\.\d+\.\d+\.\d+))");
QRegularExpressionMatch match = rx.match(s);
if (match.hasMatch()) {
m_routeGateway = match.captured(1);
qDebug() << "Set VPN route gateway" << m_routeGateway;
}
else {
} else {
qWarning() << "Unable to set VPN route gateway, output:\n" << s;
}
#endif
// QString vpnLogFileNamePath = Utils::systemLogPath() + "/openvpn.log";
// Utils::createEmptyFile(vpnLogFileNamePath);
// QString vpnLogFileNamePath = Utils::systemLogPath() + "/openvpn.log";
// Utils::createEmptyFile(vpnLogFileNamePath);
uint mgmtPort = selectMgmtPort();
qDebug() << "OpenVpnProtocol::start mgmt port selected:" << mgmtPort;
@ -193,12 +195,12 @@ ErrorCode OpenVpnProtocol::start()
return lastError();
}
setConnectionState(VpnConnectionState::Connecting);
setConnectionState(Vpn::ConnectionState::Connecting);
m_openVpnProcess = IpcClient::CreatePrivilegedProcess();
if (!m_openVpnProcess) {
//qWarning() << "IpcProcess replica is not created!";
// qWarning() << "IpcProcess replica is not created!";
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return ErrorCode::AmneziaServiceConnectionFailed;
}
@ -210,28 +212,25 @@ ErrorCode OpenVpnProtocol::start()
return ErrorCode::AmneziaServiceConnectionFailed;
}
m_openVpnProcess->setProgram(PermittedProcess::OpenVPN);
QStringList arguments({"--config" , configPath(),
"--management", m_managementHost, QString::number(mgmtPort),
"--management-client"/*, "--log", vpnLogFileNamePath */
});
QStringList arguments({
"--config", configPath(), "--management", m_managementHost, QString::number(mgmtPort),
"--management-client" /*, "--log", vpnLogFileNamePath */
});
m_openVpnProcess->setArguments(arguments);
qDebug() << arguments.join(" ");
connect(m_openVpnProcess.data(), &PrivilegedProcess::errorOccurred, [&](QProcess::ProcessError error) {
qDebug() << "PrivilegedProcess errorOccurred" << error;
});
connect(m_openVpnProcess.data(), &PrivilegedProcess::errorOccurred,
[&](QProcess::ProcessError error) { qDebug() << "PrivilegedProcess errorOccurred" << error; });
connect(m_openVpnProcess.data(), &PrivilegedProcess::stateChanged, [&](QProcess::ProcessState newState) {
qDebug() << "PrivilegedProcess stateChanged" << newState;
});
connect(m_openVpnProcess.data(), &PrivilegedProcess::stateChanged,
[&](QProcess::ProcessState newState) { qDebug() << "PrivilegedProcess stateChanged" << newState; });
connect(m_openVpnProcess.data(), &PrivilegedProcess::finished, this, [&]() {
setConnectionState(VpnConnectionState::Disconnected);
});
connect(m_openVpnProcess.data(), &PrivilegedProcess::finished, this,
[&]() { setConnectionState(Vpn::ConnectionState::Disconnected); });
m_openVpnProcess->start();
//startTimeoutTimer();
// startTimeoutTimer();
return ErrorCode::NoError;
}
@ -254,7 +253,7 @@ void OpenVpnProtocol::sendInitialData()
void OpenVpnProtocol::onReadyReadDataFromManagementServer()
{
for (;;) {
for (;;) {
QString line = m_managementServer.readLine().simplified();
if (line.isEmpty()) {
@ -267,18 +266,18 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer()
if (line.contains(">INFO:OpenVPN Management Interface")) {
sendInitialData();
} else if (line.startsWith(">STATE")) {
} else if (line.startsWith(">STATE")) {
if (line.contains("CONNECTED,SUCCESS")) {
sendByteCount();
stopTimeoutTimer();
setConnectionState(VpnProtocol::Connected);
setConnectionState(Vpn::ConnectionState::Connected);
continue;
} else if (line.contains("EXITING,SIGTER")) {
//openVpnStateSigTermHandler();
setConnectionState(VpnProtocol::Disconnecting);
// openVpnStateSigTermHandler();
setConnectionState(Vpn::ConnectionState::Disconnecting);
continue;
} else if (line.contains("RECONNECTING")) {
setConnectionState(VpnProtocol::Reconnecting);
setConnectionState(Vpn::ConnectionState::Reconnecting);
continue;
}
}
@ -294,8 +293,7 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer()
if (line.contains("FATAL")) {
if (line.contains("tap-windows6 adapters on this system are currently in use or disabled")) {
emit protocolError(ErrorCode::OpenVpnAdaptersInUseError);
}
else {
} else {
emit protocolError(ErrorCode::OpenVpnUnknownError);
}
return;
@ -320,7 +318,8 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer()
void OpenVpnProtocol::updateVpnGateway(const QString &line)
{
// line looks like
// PUSH: Received control message: 'PUSH_REPLY,route 10.8.0.1,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.6 10.8.0.5,peer-id 0,cipher AES-256-GCM'
// PUSH: Received control message: 'PUSH_REPLY,route 10.8.0.1,topology net30,ping 10,ping-restart
// 120,ifconfig 10.8.0.6 10.8.0.5,peer-id 0,cipher AES-256-GCM'
QStringList params = line.split(",");
for (const QString &l : params) {

View file

@ -10,17 +10,21 @@ QDebug operator<<(QDebug debug, const amnezia::ProtocolEnumNS::Proto &p)
return debug;
}
amnezia::Proto ProtocolProps::protoFromString(QString proto){
amnezia::Proto ProtocolProps::protoFromString(QString proto)
{
QMetaEnum metaEnum = QMetaEnum::fromType<Proto>();
for (int i = 0; i < metaEnum.keyCount(); ++i) {
Proto p = static_cast<Proto>(i);
if (proto == protoToString(p)) return p;
if (proto == protoToString(p))
return p;
}
return Proto::Any;
}
QString ProtocolProps::protoToString(amnezia::Proto p){
if (p == Proto::Any) return "";
QString ProtocolProps::protoToString(amnezia::Proto p)
{
if (p == Proto::Any)
return "";
QMetaEnum metaEnum = QMetaEnum::fromType<Proto>();
QString protoKey = metaEnum.valueToKey(static_cast<int>(p));
@ -43,7 +47,8 @@ TransportProto ProtocolProps::transportProtoFromString(QString p)
QMetaEnum metaEnum = QMetaEnum::fromType<TransportProto>();
for (int i = 0; i < metaEnum.keyCount(); ++i) {
TransportProto tp = static_cast<TransportProto>(i);
if (p.toLower() == transportProtoToString(tp).toLower()) return tp;
if (p.toLower() == transportProtoToString(tp).toLower())
return tp;
}
return TransportProto::Udp;
}
@ -55,22 +60,19 @@ QString ProtocolProps::transportProtoToString(TransportProto proto, Proto p)
return protoKey.toLower();
}
QMap<amnezia::Proto, QString> ProtocolProps::protocolHumanNames()
{
return {
{Proto::OpenVpn, "OpenVPN"},
{Proto::ShadowSocks, "ShadowSocks"},
{Proto::Cloak, "Cloak"},
{Proto::WireGuard, "WireGuard"},
{Proto::Ikev2, "IKEv2"},
{Proto::L2tp, "L2TP"},
return { { Proto::OpenVpn, "OpenVPN" },
{ Proto::ShadowSocks, "ShadowSocks" },
{ Proto::Cloak, "Cloak" },
{ Proto::WireGuard, "WireGuard" },
{ Proto::Ikev2, "IKEv2" },
{ Proto::L2tp, "L2TP" },
{Proto::TorWebSite, "Web site in Tor network"},
{Proto::Dns, "DNS Service"},
{Proto::FileShare, "File Sharing Service"},
{Proto::Sftp, QObject::tr("Sftp service")}
};
{ Proto::TorWebSite, "Website in Tor network" },
{ Proto::Dns, "DNS Service" },
{ Proto::FileShare, "File Sharing Service" },
{ Proto::Sftp, QObject::tr("Sftp service") } };
}
QMap<amnezia::Proto, QString> ProtocolProps::protocolDescriptions()
@ -81,90 +83,89 @@ QMap<amnezia::Proto, QString> ProtocolProps::protocolDescriptions()
amnezia::ServiceType ProtocolProps::protocolService(Proto p)
{
switch (p) {
case Proto::Any : return ServiceType::None;
case Proto::OpenVpn : return ServiceType::Vpn;
case Proto::Cloak : return ServiceType::Vpn;
case Proto::ShadowSocks : return ServiceType::Vpn;
case Proto::WireGuard : return ServiceType::Vpn;
case Proto::TorWebSite : return ServiceType::Other;
case Proto::Dns : return ServiceType::Other;
case Proto::FileShare : return ServiceType::Other;
default: return ServiceType::Other;
case Proto::Any: return ServiceType::None;
case Proto::OpenVpn: return ServiceType::Vpn;
case Proto::Cloak: return ServiceType::Vpn;
case Proto::ShadowSocks: return ServiceType::Vpn;
case Proto::WireGuard: return ServiceType::Vpn;
case Proto::TorWebSite: return ServiceType::Other;
case Proto::Dns: return ServiceType::Other;
case Proto::FileShare: return ServiceType::Other;
default: return ServiceType::Other;
}
}
int ProtocolProps::defaultPort(Proto p)
{
switch (p) {
case Proto::Any : return -1;
case Proto::OpenVpn : return 1194;
case Proto::Cloak : return 443;
case Proto::ShadowSocks : return 6789;
case Proto::WireGuard : return 51820;
case Proto::Ikev2 : return -1;
case Proto::L2tp : return -1;
case Proto::Any: return -1;
case Proto::OpenVpn: return 1194;
case Proto::Cloak: return 443;
case Proto::ShadowSocks: return 6789;
case Proto::WireGuard: return 51820;
case Proto::Ikev2: return -1;
case Proto::L2tp: return -1;
case Proto::TorWebSite : return -1;
case Proto::Dns : return 53;
case Proto::FileShare : return 139;
case Proto::Sftp : return 222;
default: return -1;
case Proto::TorWebSite: return -1;
case Proto::Dns: return 53;
case Proto::FileShare: return 139;
case Proto::Sftp: return 222;
default: return -1;
}
}
bool ProtocolProps::defaultPortChangeable(Proto p)
{
switch (p) {
case Proto::Any : return false;
case Proto::OpenVpn : return true;
case Proto::Cloak : return true;
case Proto::ShadowSocks : return true;
case Proto::WireGuard : return true;
case Proto::Ikev2 : return false;
case Proto::L2tp : return false;
case Proto::Any: return false;
case Proto::OpenVpn: return true;
case Proto::Cloak: return true;
case Proto::ShadowSocks: return true;
case Proto::WireGuard: return true;
case Proto::Ikev2: return false;
case Proto::L2tp: return false;
case Proto::TorWebSite : return true;
case Proto::Dns : return false;
case Proto::FileShare : return false;
default: return -1;
case Proto::TorWebSite: return true;
case Proto::Dns: return false;
case Proto::FileShare: return false;
default: return -1;
}
}
TransportProto ProtocolProps::defaultTransportProto(Proto p)
{
switch (p) {
case Proto::Any : return TransportProto::Udp;
case Proto::OpenVpn : return TransportProto::Udp;
case Proto::Cloak : return TransportProto::Tcp;
case Proto::ShadowSocks : return TransportProto::Tcp;
case Proto::WireGuard : return TransportProto::Udp;
case Proto::Ikev2 : return TransportProto::Udp;
case Proto::L2tp : return TransportProto::Udp;
case Proto::Any: return TransportProto::Udp;
case Proto::OpenVpn: return TransportProto::Udp;
case Proto::Cloak: return TransportProto::Tcp;
case Proto::ShadowSocks: return TransportProto::Tcp;
case Proto::WireGuard: return TransportProto::Udp;
case Proto::Ikev2: return TransportProto::Udp;
case Proto::L2tp: return TransportProto::Udp;
// non-vpn
case Proto::TorWebSite : return TransportProto::Tcp;
case Proto::Dns : return TransportProto::Udp;
case Proto::FileShare : return TransportProto::Udp;
case Proto::Sftp : return TransportProto::Tcp;
case Proto::TorWebSite: return TransportProto::Tcp;
case Proto::Dns: return TransportProto::Udp;
case Proto::FileShare: return TransportProto::Udp;
case Proto::Sftp: return TransportProto::Tcp;
}
}
bool ProtocolProps::defaultTransportProtoChangeable(Proto p)
{
switch (p) {
case Proto::Any : return false;
case Proto::OpenVpn : return true;
case Proto::Cloak : return false;
case Proto::ShadowSocks : return false;
case Proto::WireGuard : return false;
case Proto::Ikev2 : return false;
case Proto::L2tp : return false;
case Proto::Any: return false;
case Proto::OpenVpn: return true;
case Proto::Cloak: return false;
case Proto::ShadowSocks: return false;
case Proto::WireGuard: return false;
case Proto::Ikev2: return false;
case Proto::L2tp: return false;
// non-vpn
case Proto::TorWebSite : return false;
case Proto::Dns : return false;
case Proto::FileShare : return false;
case Proto::Sftp : return false;
default: return false;
case Proto::TorWebSite: return false;
case Proto::Dns: return false;
case Proto::FileShare: return false;
case Proto::Sftp: return false;
default: return false;
}
}

View file

@ -1,206 +1,212 @@
#ifndef PROTOCOLS_DEFS_H
#define PROTOCOLS_DEFS_H
#include <QMetaEnum>
#include <QObject>
#include <QDebug>
#include <QObject>
#include <QMetaEnum>
namespace amnezia {
namespace config_key {
// Json config strings
constexpr char hostName[] = "hostName";
constexpr char userName[] = "userName";
constexpr char password[] = "password";
constexpr char port[] = "port";
constexpr char local_port[] = "local_port";
constexpr char dns1[] = "dns1";
constexpr char dns2[] = "dns2";
constexpr char description[] = "description";
constexpr char cert[] = "cert";
constexpr char config[] = "config";
constexpr char containers[] = "containers";
constexpr char container[] = "container";
constexpr char defaultContainer[] = "defaultContainer";
constexpr char vpnproto[] = "protocol";
constexpr char protocols[] = "protocols";
constexpr char remote[] = "remote";
constexpr char transport_proto[] = "transport_proto";
constexpr char cipher[] = "cipher";
constexpr char hash[] = "hash";
constexpr char ncp_disable[] = "ncp_disable";
constexpr char tls_auth[] = "tls_auth";
constexpr char client_priv_key[] = "client_priv_key";
constexpr char client_pub_key[] = "client_pub_key";
constexpr char server_priv_key[] = "server_priv_key";
constexpr char server_pub_key[] = "server_pub_key";
constexpr char psk_key[] = "psk_key";
constexpr char client_ip[] = "client_ip"; // internal ip address
constexpr char site[] = "site";
constexpr char block_outside_dns[] = "block_outside_dns";
constexpr char subnet_address[] = "subnet_address";
constexpr char subnet_mask[] = "subnet_mask";
constexpr char subnet_cidr[] = "subnet_cidr";
constexpr char additional_client_config[] = "additional_client_config";
constexpr char additional_server_config[] = "additional_server_config";
// proto config keys
constexpr char last_config[] = "last_config";
constexpr char isThirdPartyConfig[] = "isThirdPartyConfig";
constexpr char openvpn[] = "openvpn";
constexpr char cloak[] = "cloak";
constexpr char wireguard[] = "wireguard";
}
namespace protocols {
namespace dns {
constexpr char amneziaDnsIp[] = "172.29.172.254";
}
namespace openvpn {
constexpr char defaultSubnetAddress[] = "10.8.0.0";
constexpr char defaultSubnetMask[] = "255.255.255.0";
constexpr char defaultSubnetCidr[] = "24";
constexpr char serverConfigPath[] = "/opt/amnezia/openvpn/server.conf";
constexpr char caCertPath[] = "/opt/amnezia/openvpn/pki/ca.crt";
constexpr char clientCertPath[] = "/opt/amnezia/openvpn/pki/issued";
constexpr char taKeyPath[] = "/opt/amnezia/openvpn/ta.key";
constexpr char clientsDirPath[] = "/opt/amnezia/openvpn/clients";
constexpr char defaultPort[] = "1194";
constexpr char defaultTransportProto[] = "udp";
constexpr char defaultCipher[] = "AES-256-GCM";
constexpr char defaultHash[] = "SHA512";
constexpr bool defaultBlockOutsideDns = true;
constexpr bool defaultNcpDisable = false;
constexpr bool defaultTlsAuth = true;
constexpr char ncpDisableString[] = "ncp-disable";
constexpr char tlsAuthString[] = "tls-auth /opt/amnezia/openvpn/ta.key 0";
constexpr char defaultAdditionalClientConfig[] = "";
constexpr char defaultAdditionalServerConfig[] = "";
}
namespace shadowsocks {
constexpr char ssKeyPath[] = "/opt/amnezia/shadowsocks/shadowsocks.key";
constexpr char defaultPort[] = "6789";
constexpr char defaultLocalProxyPort[] = "8585";
constexpr char defaultCipher[] = "chacha20-ietf-poly1305";
}
namespace cloak {
constexpr char ckPublicKeyPath[] = "/opt/amnezia/cloak/cloak_public.key";
constexpr char ckBypassUidKeyPath[] = "/opt/amnezia/cloak/cloak_bypass_uid.key";
constexpr char ckAdminKeyPath[] = "/opt/amnezia/cloak/cloak_admin_uid.key";
constexpr char defaultPort[] = "443";
constexpr char defaultRedirSite[] = "tile.openstreetmap.org";
constexpr char defaultCipher[] = "chacha20-poly1305";
}
namespace wireguard {
constexpr char defaultSubnetAddress[] = "10.8.1.0";
constexpr char defaultSubnetMask[] = "255.255.255.0";
constexpr char defaultSubnetCidr[] = "24";
constexpr char defaultPort[] = "51820";
constexpr char serverConfigPath[] = "/opt/amnezia/wireguard/wg0.conf";
constexpr char serverPublicKeyPath[] = "/opt/amnezia/wireguard/wireguard_server_public_key.key";
constexpr char serverPskKeyPath[] = "/opt/amnezia/wireguard/wireguard_psk.key";
}
namespace sftp {
constexpr char defaultUserName[] = "sftp_user";
} // namespace sftp
} // namespace protocols
namespace ProtocolEnumNS {
Q_NAMESPACE
enum TransportProto {
Udp,
Tcp
};
Q_ENUM_NS(TransportProto)
enum Proto {
Any = 0,
OpenVpn,
ShadowSocks,
Cloak,
WireGuard,
Ikev2,
L2tp,
// non-vpn
TorWebSite,
Dns,
FileShare,
Sftp
};
Q_ENUM_NS(Proto)
enum ServiceType {
None = 0,
Vpn,
Other
};
Q_ENUM_NS(ServiceType)
} // namespace ProtocolEnumNS
using namespace ProtocolEnumNS;
class ProtocolProps : public QObject
namespace amnezia
{
Q_OBJECT
namespace config_key
{
public:
Q_INVOKABLE static QList<Proto> allProtocols();
// Json config strings
constexpr char hostName[] = "hostName";
constexpr char userName[] = "userName";
constexpr char password[] = "password";
constexpr char port[] = "port";
constexpr char local_port[] = "local_port";
// spelling may differ for various protocols - TCP for OpenVPN, tcp for others
Q_INVOKABLE static TransportProto transportProtoFromString(QString p);
Q_INVOKABLE static QString transportProtoToString(TransportProto proto, Proto p = Proto::Any);
constexpr char dns1[] = "dns1";
constexpr char dns2[] = "dns2";
Q_INVOKABLE static Proto protoFromString(QString p);
Q_INVOKABLE static QString protoToString(Proto p);
constexpr char description[] = "description";
constexpr char cert[] = "cert";
constexpr char config[] = "config";
Q_INVOKABLE static QMap<Proto, QString> protocolHumanNames();
Q_INVOKABLE static QMap<Proto, QString> protocolDescriptions();
constexpr char containers[] = "containers";
constexpr char container[] = "container";
constexpr char defaultContainer[] = "defaultContainer";
Q_INVOKABLE static ServiceType protocolService(Proto p);
constexpr char vpnproto[] = "protocol";
constexpr char protocols[] = "protocols";
Q_INVOKABLE static int defaultPort(Proto p);
Q_INVOKABLE static bool defaultPortChangeable(Proto p);
constexpr char remote[] = "remote";
constexpr char transport_proto[] = "transport_proto";
constexpr char cipher[] = "cipher";
constexpr char hash[] = "hash";
constexpr char ncp_disable[] = "ncp_disable";
constexpr char tls_auth[] = "tls_auth";
Q_INVOKABLE static TransportProto defaultTransportProto(Proto p);
Q_INVOKABLE static bool defaultTransportProtoChangeable(Proto p);
constexpr char client_priv_key[] = "client_priv_key";
constexpr char client_pub_key[] = "client_pub_key";
constexpr char server_priv_key[] = "server_priv_key";
constexpr char server_pub_key[] = "server_pub_key";
constexpr char psk_key[] = "psk_key";
constexpr char client_ip[] = "client_ip"; // internal ip address
Q_INVOKABLE static QString key_proto_config_data(Proto p);
Q_INVOKABLE static QString key_proto_config_path(Proto p);
constexpr char site[] = "site";
constexpr char block_outside_dns[] = "block_outside_dns";
};
constexpr char subnet_address[] = "subnet_address";
constexpr char subnet_mask[] = "subnet_mask";
constexpr char subnet_cidr[] = "subnet_cidr";
constexpr char additional_client_config[] = "additional_client_config";
constexpr char additional_server_config[] = "additional_server_config";
// proto config keys
constexpr char last_config[] = "last_config";
constexpr char isThirdPartyConfig[] = "isThirdPartyConfig";
constexpr char openvpn[] = "openvpn";
constexpr char wireguard[] = "wireguard";
constexpr char shadowsocks[] = "shadowsocks";
constexpr char cloak[] = "cloak";
constexpr char sftp[] = "sftp";
}
namespace protocols
{
namespace dns
{
constexpr char amneziaDnsIp[] = "172.29.172.254";
}
namespace openvpn
{
constexpr char defaultSubnetAddress[] = "10.8.0.0";
constexpr char defaultSubnetMask[] = "255.255.255.0";
constexpr char defaultSubnetCidr[] = "24";
constexpr char serverConfigPath[] = "/opt/amnezia/openvpn/server.conf";
constexpr char caCertPath[] = "/opt/amnezia/openvpn/pki/ca.crt";
constexpr char clientCertPath[] = "/opt/amnezia/openvpn/pki/issued";
constexpr char taKeyPath[] = "/opt/amnezia/openvpn/ta.key";
constexpr char clientsDirPath[] = "/opt/amnezia/openvpn/clients";
constexpr char defaultPort[] = "1194";
constexpr char defaultTransportProto[] = "udp";
constexpr char defaultCipher[] = "AES-256-GCM";
constexpr char defaultHash[] = "SHA512";
constexpr bool defaultBlockOutsideDns = true;
constexpr bool defaultNcpDisable = false;
constexpr bool defaultTlsAuth = true;
constexpr char ncpDisableString[] = "ncp-disable";
constexpr char tlsAuthString[] = "tls-auth /opt/amnezia/openvpn/ta.key 0";
constexpr char defaultAdditionalClientConfig[] = "";
constexpr char defaultAdditionalServerConfig[] = "";
}
namespace shadowsocks
{
constexpr char ssKeyPath[] = "/opt/amnezia/shadowsocks/shadowsocks.key";
constexpr char defaultPort[] = "6789";
constexpr char defaultLocalProxyPort[] = "8585";
constexpr char defaultCipher[] = "chacha20-ietf-poly1305";
}
namespace cloak
{
constexpr char ckPublicKeyPath[] = "/opt/amnezia/cloak/cloak_public.key";
constexpr char ckBypassUidKeyPath[] = "/opt/amnezia/cloak/cloak_bypass_uid.key";
constexpr char ckAdminKeyPath[] = "/opt/amnezia/cloak/cloak_admin_uid.key";
constexpr char defaultPort[] = "443";
constexpr char defaultRedirSite[] = "tile.openstreetmap.org";
constexpr char defaultCipher[] = "chacha20-poly1305";
}
namespace wireguard
{
constexpr char defaultSubnetAddress[] = "10.8.1.0";
constexpr char defaultSubnetMask[] = "255.255.255.0";
constexpr char defaultSubnetCidr[] = "24";
constexpr char defaultPort[] = "51820";
constexpr char serverConfigPath[] = "/opt/amnezia/wireguard/wg0.conf";
constexpr char serverPublicKeyPath[] = "/opt/amnezia/wireguard/wireguard_server_public_key.key";
constexpr char serverPskKeyPath[] = "/opt/amnezia/wireguard/wireguard_psk.key";
}
namespace sftp
{
constexpr char defaultUserName[] = "sftp_user";
} // namespace sftp
} // namespace protocols
namespace ProtocolEnumNS
{
Q_NAMESPACE
enum TransportProto {
Udp,
Tcp
};
Q_ENUM_NS(TransportProto)
enum Proto {
Any = 0,
OpenVpn,
ShadowSocks,
Cloak,
WireGuard,
Ikev2,
L2tp,
// non-vpn
TorWebSite,
Dns,
FileShare,
Sftp
};
Q_ENUM_NS(Proto)
enum ServiceType {
None = 0,
Vpn,
Other
};
Q_ENUM_NS(ServiceType)
} // namespace ProtocolEnumNS
using namespace ProtocolEnumNS;
class ProtocolProps : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE static QList<Proto> allProtocols();
// spelling may differ for various protocols - TCP for OpenVPN, tcp for others
Q_INVOKABLE static TransportProto transportProtoFromString(QString p);
Q_INVOKABLE static QString transportProtoToString(TransportProto proto, Proto p = Proto::Any);
Q_INVOKABLE static Proto protoFromString(QString p);
Q_INVOKABLE static QString protoToString(Proto p);
Q_INVOKABLE static QMap<Proto, QString> protocolHumanNames();
Q_INVOKABLE static QMap<Proto, QString> protocolDescriptions();
Q_INVOKABLE static ServiceType protocolService(Proto p);
Q_INVOKABLE static int defaultPort(Proto p);
Q_INVOKABLE static bool defaultPortChangeable(Proto p);
Q_INVOKABLE static TransportProto defaultTransportProto(Proto p);
Q_INVOKABLE static bool defaultTransportProtoChangeable(Proto p);
Q_INVOKABLE static QString key_proto_config_data(Proto p);
Q_INVOKABLE static QString key_proto_config_path(Proto p);
};
} // namespace amnezia
QDebug operator<<(QDebug debug, const amnezia::Proto &p);

View file

@ -66,7 +66,7 @@ ErrorCode ShadowSocksVpnProtocol::start()
connect(&m_ssProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus){
qDebug().noquote() << "ShadowSocksVpnProtocol finished, exitCode, exiStatus" << exitCode << exitStatus;
setConnectionState(VpnProtocol::Disconnected);
setConnectionState(Vpn::ConnectionState::Disconnected);
if (exitStatus != QProcess::NormalExit){
emit protocolError(amnezia::ErrorCode::ShadowSocksExecutableCrashed);
stop();
@ -81,7 +81,7 @@ ErrorCode ShadowSocksVpnProtocol::start()
m_ssProcess.waitForStarted();
if (m_ssProcess.state() == QProcess::ProcessState::Running) {
setConnectionState(VpnConnectionState::Connecting);
setConnectionState(Vpn::ConnectionState::Connecting);
return OpenVpnProtocol::start();
}

View file

@ -18,7 +18,7 @@
VpnProtocol::VpnProtocol(const QJsonObject &configuration, QObject* parent)
: QObject(parent),
m_connectionState(VpnConnectionState::Unknown),
m_connectionState(Vpn::ConnectionState::Unknown),
m_rawConfig(configuration),
m_timeoutTimer(new QTimer(this)),
m_receivedBytes(0),
@ -32,7 +32,7 @@ void VpnProtocol::setLastError(ErrorCode lastError)
{
m_lastError = lastError;
if (lastError){
setConnectionState(VpnConnectionState::Error);
setConnectionState(Vpn::ConnectionState::Error);
}
qCritical().noquote() << "VpnProtocol error, code" << m_lastError << errorString(m_lastError);
}
@ -60,7 +60,7 @@ void VpnProtocol::stopTimeoutTimer()
m_timeoutTimer->stop();
}
VpnProtocol::VpnConnectionState VpnProtocol::connectionState() const
Vpn::ConnectionState VpnProtocol::connectionState() const
{
return m_connectionState;
}
@ -76,19 +76,19 @@ void VpnProtocol::setBytesChanged(quint64 receivedBytes, quint64 sentBytes)
m_sentBytes = sentBytes;
}
void VpnProtocol::setConnectionState(VpnProtocol::VpnConnectionState state)
void VpnProtocol::setConnectionState(Vpn::ConnectionState state)
{
qDebug() << "VpnProtocol::setConnectionState" << textConnectionState(state);
if (m_connectionState == state) {
return;
}
if (m_connectionState == VpnConnectionState::Disconnected && state == VpnConnectionState::Disconnecting) {
if (m_connectionState == Vpn::ConnectionState::Disconnected && state == Vpn::ConnectionState::Disconnecting) {
return;
}
m_connectionState = state;
if (m_connectionState == VpnConnectionState::Disconnected) {
if (m_connectionState == Vpn::ConnectionState::Disconnected) {
m_receivedBytes = 0;
m_sentBytes = 0;
}
@ -124,17 +124,17 @@ QString VpnProtocol::routeGateway() const
return m_routeGateway;
}
QString VpnProtocol::textConnectionState(VpnConnectionState connectionState)
QString VpnProtocol::textConnectionState(Vpn::ConnectionState connectionState)
{
switch (connectionState) {
case VpnConnectionState::Unknown: return tr("Unknown");
case VpnConnectionState::Disconnected: return tr("Disconnected");
case VpnConnectionState::Preparing: return tr("Preparing");
case VpnConnectionState::Connecting: return tr("Connecting...");
case VpnConnectionState::Connected: return tr("Connected");
case VpnConnectionState::Disconnecting: return tr("Disconnecting...");
case VpnConnectionState::Reconnecting: return tr("Reconnecting...");
case VpnConnectionState::Error: return tr("Error");
case Vpn::ConnectionState::Unknown: return tr("Unknown");
case Vpn::ConnectionState::Disconnected: return tr("Disconnected");
case Vpn::ConnectionState::Preparing: return tr("Preparing");
case Vpn::ConnectionState::Connecting: return tr("Connecting...");
case Vpn::ConnectionState::Connected: return tr("Connected");
case Vpn::ConnectionState::Disconnecting: return tr("Disconnecting...");
case Vpn::ConnectionState::Reconnecting: return tr("Reconnecting...");
case Vpn::ConnectionState::Error: return tr("Error");
default:
;
}
@ -149,10 +149,10 @@ QString VpnProtocol::textConnectionState() const
bool VpnProtocol::isConnected() const
{
return m_connectionState == VpnConnectionState::Connected;
return m_connectionState == Vpn::ConnectionState::Connected;
}
bool VpnProtocol::isDisconnected() const
{
return m_connectionState == VpnConnectionState::Disconnected;
return m_connectionState == Vpn::ConnectionState::Disconnected;
}

View file

@ -12,6 +12,33 @@ using namespace amnezia;
class QTimer;
//todo change name
namespace Vpn
{
Q_NAMESPACE
enum ConnectionState {
Unknown,
Disconnected,
Preparing,
Connecting,
Connected,
Disconnecting,
Reconnecting,
Error
};
Q_ENUM_NS(ConnectionState)
static void declareQmlVpnConnectionStateEnum() {
qmlRegisterUncreatableMetaObject(
Vpn::staticMetaObject,
"ConnectionState",
1, 0,
"ConnectionState",
"Error: only enums"
);
}
}
class VpnProtocol : public QObject
{
Q_OBJECT
@ -20,10 +47,7 @@ public:
explicit VpnProtocol(const QJsonObject& configuration, QObject* parent = nullptr);
virtual ~VpnProtocol() override = default;
enum VpnConnectionState {Unknown, Disconnected, Preparing, Connecting, Connected, Disconnecting, Reconnecting, Error};
Q_ENUM(VpnConnectionState)
static QString textConnectionState(VpnConnectionState connectionState);
static QString textConnectionState(Vpn::ConnectionState connectionState);
virtual ErrorCode prepare() { return ErrorCode::NoError; }
@ -32,7 +56,7 @@ public:
virtual ErrorCode start() = 0;
virtual void stop() = 0;
VpnConnectionState connectionState() const;
Vpn::ConnectionState connectionState() const;
ErrorCode lastError() const;
QString textConnectionState() const;
void setLastError(ErrorCode lastError);
@ -44,7 +68,7 @@ public:
signals:
void bytesChanged(quint64 receivedBytes, quint64 sentBytes);
void connectionStateChanged(VpnProtocol::VpnConnectionState state);
void connectionStateChanged(Vpn::ConnectionState state);
void timeoutTimerEvent();
void protocolError(amnezia::ErrorCode e);
@ -52,13 +76,13 @@ public slots:
virtual void onTimeout(); // todo: remove?
void setBytesChanged(quint64 receivedBytes, quint64 sentBytes);
void setConnectionState(VpnProtocol::VpnConnectionState state);
void setConnectionState(Vpn::ConnectionState state);
protected:
void startTimeoutTimer();
void stopTimeoutTimer();
VpnConnectionState m_connectionState;
Vpn::ConnectionState m_connectionState;
QString m_routeGateway;
QString m_vpnLocalAddress;

View file

@ -5,12 +5,13 @@
#include <QThread>
#include "logger.h"
#include "wireguardprotocol.h"
#include "utilities.h"
#include "wireguardprotocol.h"
#include "mozilla/localsocketcontroller.h"
WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject* parent) : VpnProtocol(configuration, parent)
WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject *parent)
: VpnProtocol(configuration, parent)
{
m_configFile.setFileName(QDir::tempPath() + QDir::separator() + serviceName() + ".conf");
writeWireguardConfiguration(configuration);
@ -18,12 +19,12 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject*
// MZ
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
m_impl.reset(new LocalSocketController());
connect(m_impl.get(), &ControllerImpl::connected, this, [this](const QString& pubkey, const QDateTime& connectionTimestamp) {
emit connectionStateChanged(VpnProtocol::Connected);
});
connect(m_impl.get(), &ControllerImpl::disconnected, this, [this](){
emit connectionStateChanged(VpnProtocol::Disconnected);
});
connect(m_impl.get(), &ControllerImpl::connected, this,
[this](const QString &pubkey, const QDateTime &connectionTimestamp) {
emit connectionStateChanged(Vpn::ConnectionState::Connected);
});
connect(m_impl.get(), &ControllerImpl::disconnected, this,
[this]() { emit connectionStateChanged(Vpn::ConnectionState::Disconnected); });
m_impl->initialize(nullptr, nullptr);
#endif
}
@ -69,23 +70,24 @@ void WireguardProtocol::stop()
connect(m_wireguardStopProcess.data(), &PrivilegedProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
qDebug() << "WireguardProtocol::WireguardProtocol Stop errorOccurred" << error;
setConnectionState(VpnConnectionState::Disconnected);
setConnectionState(Vpn::ConnectionState::Disconnected);
});
connect(m_wireguardStopProcess.data(), &PrivilegedProcess::stateChanged, this, [this](QProcess::ProcessState newState) {
qDebug() << "WireguardProtocol::WireguardProtocol Stop stateChanged" << newState;
});
connect(m_wireguardStopProcess.data(), &PrivilegedProcess::stateChanged, this,
[this](QProcess::ProcessState newState) {
qDebug() << "WireguardProtocol::WireguardProtocol Stop stateChanged" << newState;
});
#ifdef Q_OS_LINUX
if (IpcClient::Interface()) {
QRemoteObjectPendingReply<bool> result = IpcClient::Interface()->isWireguardRunning();
if (result.returnValue()) {
setConnectionState(VpnProtocol::Disconnected);
setConnectionState(Vpn::ConnectionState::Disconnected);
return;
}
} else {
qCritical() << "IPC client not initialized";
setConnectionState(VpnProtocol::Disconnected);
setConnectionState(Vpn::ConnectionState::Disconnected);
return;
}
#endif
@ -93,7 +95,7 @@ void WireguardProtocol::stop()
m_wireguardStopProcess->start();
m_wireguardStopProcess->waitForFinished(10000);
setConnectionState(VpnProtocol::Disconnected);
setConnectionState(Vpn::ConnectionState::Disconnected);
}
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX)
@ -143,8 +145,8 @@ void WireguardProtocol::writeWireguardConfiguration(const QJsonObject &configura
m_isConfigLoaded = true;
qDebug().noquote() << QString("Set config data") << configPath();
qDebug().noquote() << QString("Set config data") << configuration.value(ProtocolProps::key_proto_config_data(Proto::WireGuard)).toString().toUtf8();
qDebug().noquote() << QString("Set config data")
<< configuration.value(ProtocolProps::key_proto_config_data(Proto::WireGuard)).toString().toUtf8();
}
QString WireguardProtocol::configPath() const
@ -156,7 +158,8 @@ void WireguardProtocol::updateRouteGateway(QString line)
{
// TODO: fix for macos
line = line.split("ROUTE_GATEWAY", Qt::SkipEmptyParts).at(1);
if (!line.contains("/")) return;
if (!line.contains("/"))
return;
m_routeGateway = line.split("/", Qt::SkipEmptyParts).first();
m_routeGateway.replace(" ", "");
qDebug() << "Set VPN route gateway" << m_routeGateway;
@ -190,7 +193,7 @@ ErrorCode WireguardProtocol::start()
return lastError();
}
setConnectionState(VpnConnectionState::Connecting);
setConnectionState(Vpn::ConnectionState::Connecting);
m_wireguardStartProcess = IpcClient::CreatePrivilegedProcess();
@ -213,16 +216,16 @@ ErrorCode WireguardProtocol::start()
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
qDebug() << "WireguardProtocol::WireguardProtocol errorOccurred" << error;
setConnectionState(VpnConnectionState::Disconnected);
setConnectionState(Vpn::ConnectionState::Disconnected);
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::stateChanged, this, [this](QProcess::ProcessState newState) {
qDebug() << "WireguardProtocol::WireguardProtocol stateChanged" << newState;
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::stateChanged, this,
[this](QProcess::ProcessState newState) {
qDebug() << "WireguardProtocol::WireguardProtocol stateChanged" << newState;
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::finished, this, [this]() {
setConnectionState(VpnConnectionState::Connected);
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::finished, this,
[this]() { setConnectionState(Vpn::ConnectionState::Connected); });
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::readyRead, this, [this]() {
QRemoteObjectPendingReply<QByteArray> reply = m_wireguardStartProcess->readAll();
@ -250,7 +253,6 @@ ErrorCode WireguardProtocol::start()
void WireguardProtocol::updateVpnGateway(const QString &line)
{
}
QString WireguardProtocol::serviceName() const
@ -261,9 +263,9 @@ QString WireguardProtocol::serviceName() const
QStringList WireguardProtocol::stopArgs()
{
#ifdef Q_OS_WIN
return {"--remove", configPath()};
return { "--remove", configPath() };
#elif defined Q_OS_LINUX
return {"down", "wg99"};
return { "down", "wg99" };
#else
return {};
#endif
@ -272,11 +274,10 @@ QStringList WireguardProtocol::stopArgs()
QStringList WireguardProtocol::startArgs()
{
#ifdef Q_OS_WIN
return {"--add", configPath()};
return { "--add", configPath() };
#elif defined Q_OS_LINUX
return {"up", "wg99"};
return { "up", "wg99" };
#else
return {};
#endif
}

View file

@ -1,15 +1,10 @@
<RCC>
<qresource prefix="/">
<file>translations/amneziavpn_ru.qm</file>
<file>images/close.png</file>
<file>images/settings.png</file>
<file>images/favorites_disabled.png</file>
<file>images/favorites_enabled.png</file>
<file>images/favorites_hover.png</file>
<file>images/controls/check_off.png</file>
<file>images/controls/check_on.png</file>
<file>images/controls/radio_off.png</file>
<file>images/controls/radio_on.png</file>
<file>images/download.png</file>
<file>images/upload.png</file>
<file>images/tray/active.png</file>
@ -61,75 +56,6 @@
<file>server_scripts/wireguard/template.conf</file>
<file>server_scripts/website_tor/configure_container.sh</file>
<file>server_scripts/website_tor/run_container.sh</file>
<file>ui/qml/main.qml</file>
<file>ui/qml/Pages/PageBase.qml</file>
<file>ui/qml/Pages/PageAppSetting.qml</file>
<file>ui/qml/Pages/PageGeneralSettings.qml</file>
<file>ui/qml/Pages/PageNetworkSetting.qml</file>
<file>ui/qml/Pages/PageNewServer.qml</file>
<file>ui/qml/Pages/PageServerConfiguringProgress.qml</file>
<file>ui/qml/Pages/PageNewServerProtocols.qml</file>
<file>ui/qml/Pages/PageServerList.qml</file>
<file>ui/qml/Pages/PageServerContainers.qml</file>
<file>ui/qml/Pages/PageServerSettings.qml</file>
<file>ui/qml/Pages/PageSetupWizard.qml</file>
<file>ui/qml/Pages/PageSetupWizardHighLevel.qml</file>
<file>ui/qml/Pages/PageSetupWizardLowLevel.qml</file>
<file>ui/qml/Pages/PageSetupWizardMediumLevel.qml</file>
<file>ui/qml/Pages/PageSetupWizardVPNMode.qml</file>
<file>ui/qml/Pages/PageShareConnection.qml</file>
<file>ui/qml/Pages/PageSites.qml</file>
<file>ui/qml/Pages/PageStart.qml</file>
<file>ui/qml/Pages/PageVPN.qml</file>
<file>ui/qml/Pages/PageAbout.qml</file>
<file>ui/qml/Pages/PageQrDecoderIos.qml</file>
<file>ui/qml/Pages/PageViewConfig.qml</file>
<file>ui/qml/Pages/PageClientManagement.qml</file>
<file>ui/qml/Pages/ClientInfo/PageClientInfoBase.qml</file>
<file>ui/qml/Pages/ClientInfo/PageClientInfoOpenVPN.qml</file>
<file>ui/qml/Pages/ClientInfo/PageClientInfoWireGuard.qml</file>
<file>ui/qml/Pages/Protocols/PageProtoCloak.qml</file>
<file>ui/qml/Pages/Protocols/PageProtoOpenVPN.qml</file>
<file>ui/qml/Pages/Protocols/PageProtoShadowSocks.qml</file>
<file>ui/qml/Pages/Protocols/PageProtoSftp.qml</file>
<file>ui/qml/Pages/Protocols/PageProtoTorWebSite.qml</file>
<file>ui/qml/Pages/Protocols/PageProtocolBase.qml</file>
<file>ui/qml/Pages/Protocols/PageProtoWireGuard.qml</file>
<file>ui/qml/Pages/InstallSettings/InstallSettingsBase.qml</file>
<file>ui/qml/Pages/InstallSettings/SelectContainer.qml</file>
<file>ui/qml/Pages/Share/PageShareProtoCloak.qml</file>
<file>ui/qml/Pages/Share/PageShareProtocolBase.qml</file>
<file>ui/qml/Pages/Share/PageShareProtoOpenVPN.qml</file>
<file>ui/qml/Pages/Share/PageShareProtoSftp.qml</file>
<file>ui/qml/Pages/Share/PageShareProtoShadowSocks.qml</file>
<file>ui/qml/Pages/Share/PageShareProtoTorWebSite.qml</file>
<file>ui/qml/Pages/Share/PageShareProtoAmnezia.qml</file>
<file>ui/qml/Pages/Share/PageShareProtoWireGuard.qml</file>
<file>ui/qml/Pages/Share/PageShareProtoIkev2.qml</file>
<file>ui/qml/Controls/BasicButtonType.qml</file>
<file>ui/qml/Controls/BlueButtonType.qml</file>
<file>ui/qml/Controls/CheckBoxType.qml</file>
<file>ui/qml/Controls/ComboBoxType.qml</file>
<file>ui/qml/Controls/ImageButtonType.qml</file>
<file>ui/qml/Controls/LabelType.qml</file>
<file>ui/qml/Controls/RadioButtonType.qml</file>
<file>ui/qml/Controls/SettingButtonType.qml</file>
<file>ui/qml/Controls/ShareConnectionButtonType.qml</file>
<file>ui/qml/Controls/ShareConnectionContent.qml</file>
<file>ui/qml/Controls/TextFieldType.qml</file>
<file>ui/qml/Controls/RichLabelType.qml</file>
<file>ui/qml/Controls/SvgImageType.qml</file>
<file>ui/qml/Controls/FlickableType.qml</file>
<file>ui/qml/Controls/UrlButtonType.qml</file>
<file>ui/qml/Controls/TextAreaType.qml</file>
<file>ui/qml/Controls/ContextMenu.qml</file>
<file>ui/qml/Controls/FadeBehavior.qml</file>
<file>ui/qml/Controls/VisibleBehavior.qml</file>
<file>ui/qml/Controls/Caption.qml</file>
<file>ui/qml/Controls/Logo.qml</file>
<file>ui/qml/Controls/BackButton.qml</file>
<file>ui/qml/Controls/ShareConnectionButtonCopyType.qml</file>
<file>ui/qml/Controls/SvgButtonType.qml</file>
<file>ui/qml/Config/GlobalConfig.qml</file>
<file>ui/qml/Config/qmldir</file>
<file>server_scripts/check_server_is_busy.sh</file>
@ -166,10 +92,128 @@
<file>images/svg/control_point_black_24dp.svg</file>
<file>images/svg/settings_suggest_black_24dp.svg</file>
<file>server_scripts/website_tor/Dockerfile</file>
<file>ui/qml/Controls/PopupWithQuestion.qml</file>
<file>ui/qml/Pages/PageAdvancedServerSettings.qml</file>
<file>ui/qml/Controls/PopupWarning.qml</file>
<file>ui/qml/Controls/PopupWithTextField.qml</file>
<file>server_scripts/check_user_in_sudo.sh</file>
<file>ui/qml/Controls2/BasicButtonType.qml</file>
<file>ui/qml/Controls2/TextFieldWithHeaderType.qml</file>
<file>fonts/pt-root-ui_vf.ttf</file>
<file>ui/qml/Controls2/LabelWithButtonType.qml</file>
<file>images/controls/arrow-right.svg</file>
<file>images/controls/chevron-right.svg</file>
<file>ui/qml/Controls2/ImageButtonType.qml</file>
<file>ui/qml/Controls2/CardType.qml</file>
<file>ui/qml/Controls2/CheckBoxType.qml</file>
<file>images/controls/check.svg</file>
<file>ui/qml/Controls2/DropDownType.qml</file>
<file>ui/qml/Pages2/PageSetupWizardStart.qml</file>
<file>ui/qml/main2.qml</file>
<file>images/amneziaBigLogo.png</file>
<file>images/amneziaBigLogo.svg</file>
<file>ui/qml/Controls2/FlickableType.qml</file>
<file>ui/qml/Pages2/PageSetupWizardCredentials.qml</file>
<file>ui/qml/Controls2/HeaderType.qml</file>
<file>images/controls/arrow-left.svg</file>
<file>ui/qml/Pages2/PageSetupWizardProtocols.qml</file>
<file>ui/qml/Pages2/PageSetupWizardEasy.qml</file>
<file>images/controls/chevron-down.svg</file>
<file>images/controls/chevron-up.svg</file>
<file>ui/qml/Controls2/TextTypes/ParagraphTextType.qml</file>
<file>ui/qml/Controls2/TextTypes/Header2TextType.qml</file>
<file>ui/qml/Controls2/HorizontalRadioButton.qml</file>
<file>ui/qml/Controls2/VerticalRadioButton.qml</file>
<file>ui/qml/Controls2/SwitcherType.qml</file>
<file>ui/qml/Controls2/TabButtonType.qml</file>
<file>ui/qml/Pages2/PageSetupWizardProtocolSettings.qml</file>
<file>ui/qml/Pages2/PageSetupWizardInstalling.qml</file>
<file>ui/qml/Pages2/PageSetupWizardConfigSource.qml</file>
<file>images/controls/folder-open.svg</file>
<file>images/controls/qr-code.svg</file>
<file>images/controls/text-cursor.svg</file>
<file>ui/qml/Pages2/PageSetupWizardTextKey.qml</file>
<file>ui/qml/Pages2/PageStart.qml</file>
<file>ui/qml/Controls2/TabImageButtonType.qml</file>
<file>images/controls/home.svg</file>
<file>images/controls/settings-2.svg</file>
<file>images/controls/share-2.svg</file>
<file>ui/qml/Pages2/PageHome.qml</file>
<file>ui/qml/Pages2/PageSettingsServersList.qml</file>
<file>ui/qml/Pages2/PageShare.qml</file>
<file>ui/qml/Controls2/TextTypes/Header1TextType.qml</file>
<file>ui/qml/Controls2/TextTypes/LabelTextType.qml</file>
<file>ui/qml/Controls2/TextTypes/ButtonTextType.qml</file>
<file>ui/qml/Controls2/Header2Type.qml</file>
<file>images/controls/plus.svg</file>
<file>ui/qml/Components/ConnectButton.qml</file>
<file>images/connectionProgress.svg</file>
<file>images/connectionOff.svg</file>
<file>images/connectionOn.svg</file>
<file>images/controls/download.svg</file>
<file>ui/qml/Controls2/ProgressBarType.qml</file>
<file>ui/qml/Components/ConnectionTypeSelectionDrawer.qml</file>
<file>ui/qml/Components/HomeContainersListView.qml</file>
<file>ui/qml/Controls2/TextTypes/CaptionTextType.qml</file>
<file>images/controls/settings.svg</file>
<file>ui/qml/Pages2/PageSettingsServerInfo.qml</file>
<file>ui/qml/Controls2/PageType.qml</file>
<file>ui/qml/Controls2/PopupType.qml</file>
<file>images/controls/edit-3.svg</file>
<file>ui/qml/Pages2/PageSettingsServerData.qml</file>
<file>ui/qml/Components/SettingsContainersListView.qml</file>
<file>ui/qml/Controls2/TextTypes/ListItemTitleType.qml</file>
<file>ui/qml/Controls2/DividerType.qml</file>
<file>ui/qml/Controls2/DrawerType.qml</file>
<file>ui/qml/Controls2/StackViewType.qml</file>
<file>ui/qml/Pages2/PageSettings.qml</file>
<file>images/controls/amnezia.svg</file>
<file>images/controls/app.svg</file>
<file>images/controls/radio.svg</file>
<file>images/controls/save.svg</file>
<file>images/controls/server.svg</file>
<file>ui/qml/Pages2/PageSettingsServerProtocols.qml</file>
<file>ui/qml/Pages2/PageSettingsServerServices.qml</file>
<file>ui/qml/Pages2/PageSetupWizardViewConfig.qml</file>
<file>images/controls/file-cog-2.svg</file>
<file>ui/qml/Components/QuestionDrawer.qml</file>
<file>ui/qml/Pages2/PageDeinstalling.qml</file>
<file>ui/qml/Controls2/BackButtonType.qml</file>
<file>ui/qml/Pages2/PageSettingsServerProtocol.qml</file>
<file>ui/qml/Components/TransportProtoSelector.qml</file>
<file>ui/qml/Controls2/ListViewWithRadioButtonType.qml</file>
<file>images/controls/radio-button.svg</file>
<file>images/controls/radio-button-inner-circle.png</file>
<file>images/controls/radio-button-pressed.svg</file>
<file>images/controls/radio-button-inner-circle-pressed.png</file>
<file>ui/qml/Components/ShareConnectionDrawer.qml</file>
<file>ui/qml/Pages2/PageSettingsConnection.qml</file>
<file>ui/qml/Pages2/PageSettingsDns.qml</file>
<file>ui/qml/Pages2/PageSettingsApplication.qml</file>
<file>ui/qml/Pages2/PageSettingsBackup.qml</file>
<file>images/controls/delete.svg</file>
<file>ui/qml/Pages2/PageSettingsAbout.qml</file>
<file>images/controls/github.svg</file>
<file>images/controls/mail.svg</file>
<file>images/controls/telegram.svg</file>
<file>ui/qml/Controls2/TextTypes/SmallTextType.qml</file>
<file>ui/qml/Filters/ContainersModelFilters.qml</file>
<file>ui/qml/Components/SelectLanguageDrawer.qml</file>
<file>ui/qml/Controls2/BusyIndicatorType.qml</file>
<file>ui/qml/Pages2/PageProtocolOpenVpnSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolShadowSocksSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolCloakSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolRaw.qml</file>
<file>ui/qml/Pages2/PageSettingsLogging.qml</file>
<file>ui/qml/Pages2/PageServiceSftpSettings.qml</file>
<file>images/controls/copy.svg</file>
<file>ui/qml/Pages2/PageServiceTorWebsiteSettings.qml</file>
<file>ui/qml/Pages2/PageSetupWizardQrReader.qml</file>
<file>images/controls/eye.svg</file>
<file>images/controls/eye-off.svg</file>
<file>ui/qml/Pages2/PageSettingsSplitTunneling.qml</file>
<file>ui/qml/Controls2/ContextMenuType.qml</file>
<file>ui/qml/Controls2/TextAreaType.qml</file>
<file>images/controls/trash.svg</file>
<file>images/controls/more-vertical.svg</file>
<file>ui/qml/Controls2/ListViewWithLabelsType.qml</file>
<file>ui/qml/Pages2/PageServiceDnsSettings.qml</file>
<file>ui/qml/Controls2/TopCloseButtonType.qml</file>
</qresource>
</RCC>

View file

@ -1,30 +1,28 @@
#include "secure_qsettings.h"
#include "platforms/ios/MobileUtils.h"
#include "QAead.h"
#include "QBlockCipher.h"
#include "utilities.h"
#include <QDataStream>
#include <QDebug>
#include <QEventLoop>
#include <QIODevice>
#include <QJsonDocument>
#include <QJsonObject>
#include <QRandomGenerator>
#include <QSharedPointer>
#include <QTimer>
#include "utilities.h"
#include <QRandomGenerator>
#include "QAead.h"
#include "QBlockCipher.h"
using namespace QKeychain;
SecureQSettings::SecureQSettings(const QString &organization, const QString &application, QObject *parent)
: QObject{parent},
m_settings(organization, application, parent),
encryptedKeys({"Servers/serversList"})
: QObject { parent }, m_settings(organization, application, parent), encryptedKeys({ "Servers/serversList" })
{
bool encrypted = m_settings.value("Conf/encrypted").toBool();
// convert settings to encrypted for if updated to >= 2.1.0
if (encryptionRequired() && ! encrypted) {
if (encryptionRequired() && !encrypted) {
for (const QString &key : m_settings.allKeys()) {
if (encryptedKeys.contains(key)) {
const QVariant &val = value(key);
@ -44,15 +42,15 @@ QVariant SecureQSettings::value(const QString &key, const QVariant &defaultValue
return m_cache.value(key);
}
if (!m_settings.contains(key)) return defaultValue;
if (!m_settings.contains(key))
return defaultValue;
QVariant retVal;
// check if value is not encrypted, v. < 2.0.x
retVal = m_settings.value(key);
if (retVal.isValid()) {
if (retVal.userType() == QVariant::ByteArray &&
retVal.toByteArray().mid(0, magicString.size()) == magicString) {
if (retVal.userType() == QVariant::ByteArray && retVal.toByteArray().mid(0, magicString.size()) == magicString) {
if (getEncKey().isEmpty() || getEncIv().isEmpty()) {
qCritical() << "SecureQSettings::setValue Decryption requested, but key is empty";
@ -71,8 +69,7 @@ QVariant SecureQSettings::value(const QString &key, const QVariant &defaultValue
retVal = QVariant();
}
}
}
else {
} else {
qWarning() << "SecureQSettings::value invalid QVariant value";
retVal = QVariant();
}
@ -95,14 +92,12 @@ void SecureQSettings::setValue(const QString &key, const QVariant &value)
QByteArray encryptedValue = encryptText(decryptedValue);
m_settings.setValue(key, magicString + encryptedValue);
}
else {
} else {
qCritical() << "SecureQSettings::setValue Encryption required, but key is empty";
return;
}
}
else {
} else {
m_settings.setValue(key, value);
}
@ -139,7 +134,8 @@ QByteArray SecureQSettings::backupAppConfig() const
bool SecureQSettings::restoreAppConfig(const QByteArray &json)
{
QJsonObject cfg = QJsonDocument::fromJson(json).object();
if (cfg.isEmpty()) return false;
if (cfg.isEmpty())
return false;
for (const QString &key : cfg.keys()) {
setValue(key, cfg.value(key).toVariant());
@ -149,14 +145,13 @@ bool SecureQSettings::restoreAppConfig(const QByteArray &json)
return true;
}
QByteArray SecureQSettings::encryptText(const QByteArray& value) const
QByteArray SecureQSettings::encryptText(const QByteArray &value) const
{
QSimpleCrypto::QBlockCipher cipher;
return cipher.encryptAesBlockCipher(value, getEncKey(), getEncIv());
}
QByteArray SecureQSettings::decryptText(const QByteArray& ba) const
QByteArray SecureQSettings::decryptText(const QByteArray &ba) const
{
QSimpleCrypto::QBlockCipher cipher;
return cipher.decryptAesBlockCipher(ba, getEncKey(), getEncIv());
@ -228,13 +223,11 @@ QByteArray SecureQSettings::getSecTag(const QString &tag)
job->setAutoDelete(false);
job->setKey(tag);
QEventLoop loop;
job->connect(job.data(), &ReadPasswordJob::finished, job.data(), [&loop](){
loop.quit();
});
job->connect(job.data(), &ReadPasswordJob::finished, job.data(), [&loop]() { loop.quit(); });
job->start();
loop.exec();
if ( job->error() ) {
if (job->error()) {
qCritical() << "SecureQSettings::getSecTag Error:" << job->errorString();
}
@ -249,9 +242,7 @@ void SecureQSettings::setSecTag(const QString &tag, const QByteArray &data)
job->setBinaryData(data);
QEventLoop loop;
QTimer::singleShot(1000, &loop, SLOT(quit()));
job->connect(job.data(), &WritePasswordJob::finished, job.data(), [&loop](){
loop.quit();
});
job->connect(job.data(), &WritePasswordJob::finished, job.data(), [&loop]() { loop.quit(); });
job->start();
loop.exec();
@ -260,4 +251,10 @@ void SecureQSettings::setSecTag(const QString &tag, const QByteArray &data)
}
}
void SecureQSettings::clearSettings()
{
QMutexLocker locker(&mutex);
m_settings.clear();
m_cache.clear();
sync();
}

View file

@ -1,23 +1,22 @@
#ifndef SECUREQSETTINGS_H
#define SECUREQSETTINGS_H
#include <QSettings>
#include <QObject>
#include <QMutex>
#include <QMutexLocker>
#include <QObject>
#include <QSettings>
#include "keychain.h"
constexpr const char* settingsKeyTag = "settingsKeyTag";
constexpr const char* settingsIvTag = "settingsIvTag";
constexpr const char* keyChainName = "AmneziaVPN-Keychain";
constexpr const char *settingsKeyTag = "settingsKeyTag";
constexpr const char *settingsIvTag = "settingsIvTag";
constexpr const char *keyChainName = "AmneziaVPN-Keychain";
class SecureQSettings : public QObject
{
public:
explicit SecureQSettings(const QString &organization, const QString &application = QString(), QObject *parent = nullptr);
explicit SecureQSettings(const QString &organization, const QString &application = QString(),
QObject *parent = nullptr);
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
void setValue(const QString &key, const QVariant &value);
@ -28,7 +27,7 @@ public:
bool restoreAppConfig(const QByteArray &json);
QByteArray encryptText(const QByteArray &value) const;
QByteArray decryptText(const QByteArray& ba) const;
QByteArray decryptText(const QByteArray &ba) const;
bool encryptionRequired() const;
@ -38,6 +37,8 @@ public:
static QByteArray getSecTag(const QString &tag);
static void setSecTag(const QString &tag, const QByteArray &data);
void clearSettings();
private:
QSettings m_settings;

View file

@ -1,6 +1,6 @@
#include "version.h"
#include "settings.h"
#include "utilities.h"
#include "version.h"
#include "containers/containers_defs.h"
#include "logger.h"
@ -8,10 +8,7 @@
const char Settings::cloudFlareNs1[] = "1.1.1.1";
const char Settings::cloudFlareNs2[] = "1.0.0.1";
Settings::Settings(QObject* parent) :
QObject(parent),
m_settings(ORGANIZATION_NAME, APPLICATION_NAME, this)
Settings::Settings(QObject *parent) : QObject(parent), m_settings(ORGANIZATION_NAME, APPLICATION_NAME, this)
{
// Import old settings
if (serversCount() == 0) {
@ -20,7 +17,7 @@ Settings::Settings(QObject* parent) :
QString serverName = m_settings.value("Server/serverName").toString();
int port = m_settings.value("Server/serverPort").toInt();
if (!user.isEmpty() && !password.isEmpty() && !serverName.isEmpty()){
if (!user.isEmpty() && !password.isEmpty() && !serverName.isEmpty()) {
QJsonObject server;
server.insert(config_key::userName, user);
server.insert(config_key::password, password);
@ -46,7 +43,8 @@ int Settings::serversCount() const
QJsonObject Settings::server(int index) const
{
const QJsonArray &servers = serversArray();
if (index >= servers.size()) return QJsonObject();
if (index >= servers.size())
return QJsonObject();
return servers.at(index).toObject();
}
@ -61,7 +59,8 @@ void Settings::addServer(const QJsonObject &server)
void Settings::removeServer(int index)
{
QJsonArray servers = serversArray();
if (index >= servers.size()) return;
if (index >= servers.size())
return;
servers.removeAt(index);
setServersArray(servers);
@ -70,7 +69,8 @@ void Settings::removeServer(int index)
bool Settings::editServer(int index, const QJsonObject &server)
{
QJsonArray servers = serversArray();
if (index >= servers.size()) return false;
if (index >= servers.size())
return false;
servers.replace(index, server);
setServersArray(servers);
@ -94,8 +94,8 @@ QString Settings::defaultContainerName(int serverIndex) const
QString name = server(serverIndex).value(config_key::defaultContainer).toString();
if (name.isEmpty()) {
return ContainerProps::containerToString(DockerContainer::None);
}
else return name;
} else
return name;
}
QMap<DockerContainer, QJsonObject> Settings::containers(int serverIndex) const
@ -104,7 +104,8 @@ QMap<DockerContainer, QJsonObject> Settings::containers(int serverIndex) const
QMap<DockerContainer, QJsonObject> containersMap;
for (const QJsonValue &val : containers) {
containersMap.insert(ContainerProps::containerFromString(val.toObject().value(config_key::container).toString()), val.toObject());
containersMap.insert(ContainerProps::containerFromString(val.toObject().value(config_key::container).toString()),
val.toObject());
}
return containersMap;
@ -114,17 +115,17 @@ void Settings::setContainers(int serverIndex, const QMap<DockerContainer, QJsonO
{
QJsonObject s = server(serverIndex);
QJsonArray c;
for (const QJsonObject &o: containers) {
for (const QJsonObject &o : containers) {
c.append(o);
}
s.insert(config_key::containers, c);
editServer(serverIndex, s);
}
QJsonObject Settings::containerConfig(int serverIndex, DockerContainer container)
{
if (container == DockerContainer::None) return QJsonObject();
if (container == DockerContainer::None)
return QJsonObject();
return containers(serverIndex).value(container);
}
@ -170,7 +171,7 @@ void Settings::clearLastConnectionConfig(int serverIndex, DockerContainer contai
{
// recursively remove
if (proto == Proto::Any) {
for (Proto p: ContainerProps::protocolsForContainer(container)) {
for (Proto p : ContainerProps::protocolsForContainer(container)) {
clearLastConnectionConfig(serverIndex, container, p);
}
return;
@ -183,9 +184,10 @@ void Settings::clearLastConnectionConfig(int serverIndex, DockerContainer contai
bool Settings::haveAuthData(int serverIndex) const
{
if (serverIndex < 0) return false;
if (serverIndex < 0)
return false;
ServerCredentials cred = serverCredentials(serverIndex);
return (!cred.hostName.isEmpty() && !cred.userName.isEmpty() && !cred.password.isEmpty());
return (!cred.hostName.isEmpty() && !cred.userName.isEmpty() && !cred.secretData.isEmpty());
}
QString Settings::nextAvailableServerName() const
@ -196,7 +198,7 @@ QString Settings::nextAvailableServerName() const
do {
i++;
nameExist = false;
for (const QJsonValue &server: serversArray()) {
for (const QJsonValue &server : serversArray()) {
if (server.toObject().value(config_key::description).toString() == tr("Server") + " " + QString::number(i)) {
nameExist = true;
break;
@ -223,12 +225,9 @@ void Settings::setSaveLogs(bool enabled)
QString Settings::routeModeString(RouteMode mode) const
{
switch (mode) {
case VpnAllSites:
return "AllSites";
case VpnOnlyForwardSites:
return "ForwardSites";
case VpnAllExceptSites:
return "ExceptSites";
case VpnAllSites: return "AllSites";
case VpnOnlyForwardSites: return "ForwardSites";
case VpnAllExceptSites: return "ExceptSites";
}
}
@ -241,13 +240,15 @@ Settings::RouteMode Settings::routeMode() const
return static_cast<RouteMode>(m_settings.value("Conf/routeMode", 0).toInt());
}
void Settings::addVpnSite(RouteMode mode, const QString &site, const QString &ip)
bool Settings::addVpnSite(RouteMode mode, const QString &site, const QString &ip)
{
QVariantMap sites = vpnSites(mode);
if (sites.contains(site) && ip.isEmpty()) return;
if (sites.contains(site) && ip.isEmpty())
return false;
sites.insert(site, ip);
setVpnSites(mode, sites);
return true;
}
void Settings::addVpnSites(RouteMode mode, const QMap<QString, QString> &sites)
@ -257,7 +258,8 @@ void Settings::addVpnSites(RouteMode mode, const QMap<QString, QString> &sites)
const QString &site = i.key();
const QString &ip = i.value();
if (allSites.contains(site) && allSites.value(site) == ip) continue;
if (allSites.contains(site) && allSites.value(site) == ip)
continue;
allSites.insert(site, ip);
}
@ -272,8 +274,7 @@ QStringList Settings::getVpnIps(RouteMode mode) const
for (auto i = m.constBegin(); i != m.constEnd(); ++i) {
if (Utils::checkIpSubnetFormat(i.key())) {
ips.append(i.key());
}
else if (Utils::checkIpSubnetFormat(i.value().toString())) {
} else if (Utils::checkIpSubnetFormat(i.value().toString())) {
ips.append(i.value().toString());
}
}
@ -284,7 +285,8 @@ QStringList Settings::getVpnIps(RouteMode mode) const
void Settings::removeVpnSite(RouteMode mode, const QString &site)
{
QVariantMap sites = vpnSites(mode);
if (!sites.contains(site)) return;
if (!sites.contains(site))
return;
sites.remove(site);
setVpnSites(mode, sites);
@ -294,7 +296,8 @@ void Settings::addVpnIps(RouteMode mode, const QStringList &ips)
{
QVariantMap sites = vpnSites(mode);
for (const QString &ip : ips) {
if (ip.isEmpty()) continue;
if (ip.isEmpty())
continue;
sites.insert(ip, "");
}
@ -306,7 +309,8 @@ void Settings::removeVpnSites(RouteMode mode, const QStringList &sites)
{
QVariantMap sitesMap = vpnSites(mode);
for (const QString &site : sites) {
if (site.isEmpty()) continue;
if (site.isEmpty())
continue;
sitesMap.remove(site);
}
@ -314,9 +318,25 @@ void Settings::removeVpnSites(RouteMode mode, const QStringList &sites)
setVpnSites(mode, sitesMap);
}
QString Settings::primaryDns() const { return m_settings.value("Conf/primaryDns", cloudFlareNs1).toString(); }
void Settings::removeAllVpnSites(RouteMode mode)
{
setVpnSites(mode, QVariantMap());
}
QString Settings::secondaryDns() const { return m_settings.value("Conf/secondaryDns", cloudFlareNs2).toString(); }
QString Settings::primaryDns() const
{
return m_settings.value("Conf/primaryDns", cloudFlareNs1).toString();
}
QString Settings::secondaryDns() const
{
return m_settings.value("Conf/secondaryDns", cloudFlareNs2).toString();
}
void Settings::clearSettings()
{
m_settings.clearSettings();
}
ServerCredentials Settings::defaultServerCredentials() const
{
@ -330,7 +350,7 @@ ServerCredentials Settings::serverCredentials(int index) const
ServerCredentials credentials;
credentials.hostName = s.value(config_key::hostName).toString();
credentials.userName = s.value(config_key::userName).toString();
credentials.password = s.value(config_key::password).toString();
credentials.secretData = s.value(config_key::password).toString();
credentials.port = s.value(config_key::port).toInt();
return credentials;

View file

@ -2,15 +2,15 @@
#define SETTINGS_H
#include <QObject>
#include <QString>
#include <QSettings>
#include <QString>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include "core/defs.h"
#include "containers/containers_defs.h"
#include "core/defs.h"
#include "secure_qsettings.h"
using namespace amnezia;
@ -22,13 +22,19 @@ class Settings : public QObject
Q_OBJECT
public:
explicit Settings(QObject* parent = nullptr);
explicit Settings(QObject *parent = nullptr);
ServerCredentials defaultServerCredentials() const;
ServerCredentials serverCredentials(int index) const;
QJsonArray serversArray() const { return QJsonDocument::fromJson(m_settings.value("Servers/serversList").toByteArray()).array(); }
void setServersArray(const QJsonArray &servers) { m_settings.setValue("Servers/serversList", QJsonDocument(servers).toJson()); }
QJsonArray serversArray() const
{
return QJsonDocument::fromJson(m_settings.value("Servers/serversList").toByteArray()).array();
}
void setServersArray(const QJsonArray &servers)
{
m_settings.setValue("Servers/serversList", QJsonDocument(servers).toJson());
}
// Servers section
int serversCount() const;
@ -37,9 +43,18 @@ public:
void removeServer(int index);
bool editServer(int index, const QJsonObject &server);
int defaultServerIndex() const { return m_settings.value("Servers/defaultServerIndex", 0).toInt(); }
void setDefaultServer(int index) { m_settings.setValue("Servers/defaultServerIndex", index); }
QJsonObject defaultServer() const { return server(defaultServerIndex()); }
int defaultServerIndex() const
{
return m_settings.value("Servers/defaultServerIndex", 0).toInt();
}
void setDefaultServer(int index)
{
m_settings.setValue("Servers/defaultServerIndex", index);
}
QJsonObject defaultServer() const
{
return server(defaultServerIndex());
}
void setDefaultContainer(int serverIndex, DockerContainer container);
DockerContainer defaultContainer(int serverIndex) const;
@ -61,13 +76,28 @@ public:
QString nextAvailableServerName() const;
// App settings section
bool isAutoConnect() const { return m_settings.value("Conf/autoConnect", false).toBool(); }
void setAutoConnect(bool enabled) { m_settings.setValue("Conf/autoConnect", enabled); }
bool isAutoConnect() const
{
return m_settings.value("Conf/autoConnect", false).toBool();
}
void setAutoConnect(bool enabled)
{
m_settings.setValue("Conf/autoConnect", enabled);
}
bool isStartMinimized() const { return m_settings.value("Conf/startMinimized", false).toBool(); }
void setStartMinimized(bool enabled) { m_settings.setValue("Conf/startMinimized", enabled); }
bool isStartMinimized() const
{
return m_settings.value("Conf/startMinimized", false).toBool();
}
void setStartMinimized(bool enabled)
{
m_settings.setValue("Conf/startMinimized", enabled);
}
bool isSaveLogs() const { return m_settings.value("Conf/saveLogs", false).toBool(); }
bool isSaveLogs() const
{
return m_settings.value("Conf/saveLogs", false).toBool();
}
void setSaveLogs(bool enabled);
enum RouteMode {
@ -75,50 +105,86 @@ public:
VpnOnlyForwardSites,
VpnAllExceptSites
};
Q_ENUM (RouteMode)
Q_ENUM(RouteMode)
QString routeModeString(RouteMode mode) const;
RouteMode routeMode() const;
void setRouteMode(RouteMode mode) { m_settings.setValue("Conf/routeMode", mode); }
QVariantMap vpnSites(RouteMode mode) const { return m_settings.value("Conf/" + routeModeString(mode)).toMap(); }
void setVpnSites(RouteMode mode, const QVariantMap &sites) { m_settings.setValue("Conf/"+ routeModeString(mode), sites); m_settings.sync(); }
void addVpnSite(RouteMode mode, const QString &site, const QString &ip= "");
QVariantMap vpnSites(RouteMode mode) const
{
return m_settings.value("Conf/" + routeModeString(mode)).toMap();
}
void setVpnSites(RouteMode mode, const QVariantMap &sites)
{
m_settings.setValue("Conf/" + routeModeString(mode), sites);
m_settings.sync();
}
bool addVpnSite(RouteMode mode, const QString &site, const QString &ip = "");
void addVpnSites(RouteMode mode, const QMap<QString, QString> &sites); // map <site, ip>
QStringList getVpnIps(RouteMode mode) const;
void removeVpnSite(RouteMode mode, const QString &site);
void addVpnIps(RouteMode mode, const QStringList &ip);
void removeVpnSites(RouteMode mode, const QStringList &sites);
void removeAllVpnSites(RouteMode mode);
bool useAmneziaDns() const { return m_settings.value("Conf/useAmneziaDns", true).toBool(); }
void setUseAmneziaDns(bool enabled) { m_settings.setValue("Conf/useAmneziaDns", enabled); }
bool useAmneziaDns() const
{
return m_settings.value("Conf/useAmneziaDns", true).toBool();
}
void setUseAmneziaDns(bool enabled)
{
m_settings.setValue("Conf/useAmneziaDns", enabled);
}
QString primaryDns() const;
QString secondaryDns() const;
//QString primaryDns() const { return m_primaryDns; }
void setPrimaryDns(const QString &primaryDns) { m_settings.setValue("Conf/primaryDns", primaryDns); }
// QString primaryDns() const { return m_primaryDns; }
void setPrimaryDns(const QString &primaryDns)
{
m_settings.setValue("Conf/primaryDns", primaryDns);
}
//QString secondaryDns() const { return m_secondaryDns; }
void setSecondaryDns(const QString &secondaryDns) { m_settings.setValue("Conf/secondaryDns", secondaryDns); }
// QString secondaryDns() const { return m_secondaryDns; }
void setSecondaryDns(const QString &secondaryDns)
{
m_settings.setValue("Conf/secondaryDns", secondaryDns);
}
static const char cloudFlareNs1[];
static const char cloudFlareNs2[];
// static constexpr char openNicNs5[] = "94.103.153.176";
// static constexpr char openNicNs13[] = "144.76.103.143";
// static constexpr char openNicNs5[] = "94.103.153.176";
// static constexpr char openNicNs13[] = "144.76.103.143";
QByteArray backupAppConfig() const { return m_settings.backupAppConfig(); }
bool restoreAppConfig(const QByteArray &cfg) { return m_settings.restoreAppConfig(cfg); }
QByteArray backupAppConfig() const
{
return m_settings.backupAppConfig();
}
bool restoreAppConfig(const QByteArray &cfg)
{
return m_settings.restoreAppConfig(cfg);
}
QLocale getAppLanguage()
{
return m_settings.value("Conf/appLanguage", QLocale()).toLocale();
};
void setAppLanguage(QLocale locale)
{
m_settings.setValue("Conf/appLanguage", locale);
};
void clearSettings();
signals:
void saveLogsChanged();
private:
SecureQSettings m_settings;
};
#endif // SETTINGS_H

Binary file not shown.

File diff suppressed because it is too large Load diff

34
client/ui/Controls2 Normal file
View file

@ -0,0 +1,34 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
TextArea {
id: root
width: parent.width
topPadding: 16
leftPadding: 16
color: "#D7D8DB"
selectionColor: "#412102"
selectedTextColor: "#D7D8DB"
placeholderTextColor: "#878B91"
font.pixelSize: 16
font.weight: Font.Medium
font.family: "PT Root UI VF"
wrapMode: Text.Wrap
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: contextMenu.open()
}
ContextMenuType {
id: contextMenu
textObj: textField
}
}

View file

@ -0,0 +1,142 @@
#include "connectionController.h"
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
#include <QGuiApplication>
#else
#include <QApplication>
#endif
#include "core/errorstrings.h"
ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<VpnConnection> &vpnConnection, QObject *parent)
: QObject(parent), m_serversModel(serversModel), m_containersModel(containersModel), m_vpnConnection(vpnConnection)
{
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, this,
&ConnectionController::onConnectionStateChanged);
connect(this, &ConnectionController::connectToVpn, m_vpnConnection.get(), &VpnConnection::connectToVpn,
Qt::QueuedConnection);
connect(this, &ConnectionController::disconnectFromVpn, m_vpnConnection.get(), &VpnConnection::disconnectFromVpn,
Qt::QueuedConnection);
}
ConnectionController::~ConnectionController()
{
// todo use ConnectionController instead of using m_vpnConnection directly
#ifdef AMNEZIA_DESKTOP
if (m_vpnConnection->connectionState() != Vpn::ConnectionState::Disconnected) {
m_vpnConnection->disconnectFromVpn();
for (int i = 0; i < 50; i++) {
qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
QThread::msleep(100);
if (m_vpnConnection->isDisconnected()) {
break;
}
}
}
#endif
}
void ConnectionController::openConnection()
{
int serverIndex = m_serversModel->getDefaultServerIndex();
ServerCredentials credentials =
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
DockerContainer container = m_containersModel->getDefaultContainer();
QModelIndex containerModelIndex = m_containersModel->index(container);
const QJsonObject &containerConfig =
qvariant_cast<QJsonObject>(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole));
if (container == DockerContainer::None) {
emit connectionErrorOccurred(tr("VPN Protocols is not installed.\n Please install VPN container at first"));
return;
}
qApp->processEvents();
emit connectToVpn(serverIndex, credentials, container, containerConfig);
}
void ConnectionController::closeConnection()
{
emit disconnectFromVpn();
}
QString ConnectionController::getLastConnectionError()
{
return errorString(m_vpnConnection->lastError());
}
void ConnectionController::onConnectionStateChanged(Vpn::ConnectionState state)
{
m_isConnected = false;
m_connectionStateText = tr("Connection...");
switch (state) {
case Vpn::ConnectionState::Connected: {
m_isConnectionInProgress = false;
m_isConnected = true;
m_connectionStateText = tr("Connected");
break;
}
case Vpn::ConnectionState::Connecting: {
m_isConnectionInProgress = true;
break;
}
case Vpn::ConnectionState::Reconnecting: {
m_isConnectionInProgress = true;
m_connectionStateText = tr("Reconnection...");
break;
}
case Vpn::ConnectionState::Disconnected: {
m_isConnectionInProgress = false;
m_connectionStateText = tr("Connect");
break;
}
case Vpn::ConnectionState::Disconnecting: {
m_isConnectionInProgress = true;
m_connectionStateText = tr("Disconnection...");
break;
}
case Vpn::ConnectionState::Preparing: {
m_isConnectionInProgress = true;
break;
}
case Vpn::ConnectionState::Error: {
m_isConnectionInProgress = false;
m_connectionStateText = tr("Connect");
emit connectionErrorOccurred(getLastConnectionError());
break;
}
case Vpn::ConnectionState::Unknown: {
m_isConnectionInProgress = false;
m_connectionStateText = tr("Connect");
emit connectionErrorOccurred(getLastConnectionError());
break;
}
}
emit connectionStateChanged();
}
void ConnectionController::onCurrentContainerUpdated()
{
if (m_isConnected || m_isConnectionInProgress) {
emit reconnectWithUpdatedContainer(tr("Settings updated successfully, Reconnnection..."));
openConnection();
}
}
QString ConnectionController::connectionStateText() const
{
return m_connectionStateText;
}
bool ConnectionController::isConnectionInProgress() const
{
return m_isConnectionInProgress;
}
bool ConnectionController::isConnected() const
{
return m_isConnected;
}

View file

@ -0,0 +1,57 @@
#ifndef CONNECTIONCONTROLLER_H
#define CONNECTIONCONTROLLER_H
#include "protocols/vpnprotocol.h"
#include "ui/models/containers_model.h"
#include "ui/models/servers_model.h"
#include "vpnconnection.h"
class ConnectionController : public QObject
{
Q_OBJECT
public:
Q_PROPERTY(bool isConnected READ isConnected NOTIFY connectionStateChanged)
Q_PROPERTY(bool isConnectionInProgress READ isConnectionInProgress NOTIFY connectionStateChanged)
Q_PROPERTY(QString connectionStateText READ connectionStateText NOTIFY connectionStateChanged)
explicit ConnectionController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<VpnConnection> &vpnConnection, QObject *parent = nullptr);
~ConnectionController();
bool isConnected() const;
bool isConnectionInProgress() const;
QString connectionStateText() const;
public slots:
void openConnection();
void closeConnection();
QString getLastConnectionError();
void onConnectionStateChanged(Vpn::ConnectionState state);
void onCurrentContainerUpdated();
signals:
void connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig);
void disconnectFromVpn();
void connectionStateChanged();
void connectionErrorOccurred(const QString &errorMessage);
void reconnectWithUpdatedContainer(const QString &message);
private:
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<VpnConnection> m_vpnConnection;
bool m_isConnected = false;
bool m_isConnectionInProgress = false;
QString m_connectionStateText = tr("Connect");
};
#endif // CONNECTIONCONTROLLER_H

View file

@ -0,0 +1,243 @@
#include "exportController.h"
#include <QBuffer>
#include <QDataStream>
#include <QDesktopServices>
#include <QFile>
#include <QFileInfo>
#include <QImage>
#include <QStandardPaths>
#include "configurators/openvpn_configurator.h"
#include "configurators/wireguard_configurator.h"
#include "core/errorstrings.h"
#include "systemController.h"
#ifdef Q_OS_ANDROID
#include "platforms/android/androidutils.h"
#endif
#include "qrcodegen.hpp"
ExportController::ExportController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ContainersModel> &containersModel,
const std::shared_ptr<Settings> &settings,
const std::shared_ptr<VpnConfigurator> &configurator, QObject *parent)
: QObject(parent),
m_serversModel(serversModel),
m_containersModel(containersModel),
m_settings(settings),
m_configurator(configurator)
{
#ifdef Q_OS_ANDROID
m_authResultNotifier.reset(new AuthResultNotifier);
m_authResultReceiver.reset(new AuthResultReceiver(m_authResultNotifier));
connect(m_authResultNotifier.get(), &AuthResultNotifier::authFailed, this,
[this]() { emit exportErrorOccurred(tr("Access error!")); });
connect(m_authResultNotifier.get(), &AuthResultNotifier::authSuccessful, this,
&ExportController::generateFullAccessConfig);
#endif
}
void ExportController::generateFullAccessConfig()
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
QJsonObject config = m_settings->server(serverIndex);
QByteArray compressedConfig = QJsonDocument(config).toJson();
compressedConfig = qCompress(compressedConfig, 8);
m_config = QString("vpn://%1")
.arg(QString(compressedConfig.toBase64(QByteArray::Base64UrlEncoding
| QByteArray::OmitTrailingEquals)));
m_qrCodes = generateQrCodeImageSeries(compressedConfig);
emit exportConfigChanged();
}
#if defined(Q_OS_ANDROID)
void ExportController::generateFullAccessConfigAndroid()
{
/* We use builtin keyguard for ssh key export protection on Android */
QJniObject activity = AndroidUtils::getActivity();
auto appContext = activity.callObjectMethod("getApplicationContext", "()Landroid/content/Context;");
if (appContext.isValid()) {
auto intent = QJniObject::callStaticObjectMethod("org/amnezia/vpn/AuthHelper", "getAuthIntent",
"(Landroid/content/Context;)Landroid/content/Intent;",
appContext.object());
if (intent.isValid()) {
if (intent.object<jobject>() != nullptr) {
QtAndroidPrivate::startActivity(intent.object<jobject>(), 1, m_authResultReceiver.get());
}
} else {
generateFullAccessConfig();
}
}
}
#endif
void ExportController::generateConnectionConfig()
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
ServerCredentials credentials =
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
QModelIndex containerModelIndex = m_containersModel->index(container);
QJsonObject containerConfig =
qvariant_cast<QJsonObject>(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole));
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
ErrorCode errorCode = ErrorCode::NoError;
for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
QJsonObject protocolConfig = m_settings->protocolConfig(serverIndex, container, protocol);
QString vpnConfig =
m_configurator->genVpnProtocolConfig(credentials, container, containerConfig, protocol, &errorCode);
if (errorCode) {
emit exportErrorOccurred(errorString(errorCode));
return;
}
protocolConfig.insert(config_key::last_config, vpnConfig);
containerConfig.insert(ProtocolProps::protoToString(protocol), protocolConfig);
}
QJsonObject config = m_settings->server(serverIndex);
if (!errorCode) {
config.remove(config_key::userName);
config.remove(config_key::password);
config.remove(config_key::port);
config.insert(config_key::containers, QJsonArray { containerConfig });
config.insert(config_key::defaultContainer, ContainerProps::containerToString(container));
auto dns = m_configurator->getDnsForConfig(serverIndex);
config.insert(config_key::dns1, dns.first);
config.insert(config_key::dns2, dns.second);
}
QByteArray compressedConfig = QJsonDocument(config).toJson();
compressedConfig = qCompress(compressedConfig, 8);
m_config = QString("vpn://%1")
.arg(QString(compressedConfig.toBase64(QByteArray::Base64UrlEncoding
| QByteArray::OmitTrailingEquals)));
m_qrCodes = generateQrCodeImageSeries(compressedConfig);
emit exportConfigChanged();
}
void ExportController::generateOpenVpnConfig()
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
ServerCredentials credentials =
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
QModelIndex containerModelIndex = m_containersModel->index(container);
QJsonObject containerConfig =
qvariant_cast<QJsonObject>(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole));
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
ErrorCode errorCode = ErrorCode::NoError;
QString config =
m_configurator->openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig, &errorCode);
if (errorCode) {
emit exportErrorOccurred(errorString(errorCode));
return;
}
config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::OpenVpn, config);
auto configJson = QJsonDocument::fromJson(config.toUtf8()).object();
QStringList lines = configJson.value(config_key::config).toString().replace("\r", "").split("\n");
for (const QString &line : lines) {
m_config.append(line + "\n");
}
emit exportConfigChanged();
}
void ExportController::generateWireGuardConfig()
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
ServerCredentials credentials =
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
QModelIndex containerModelIndex = m_containersModel->index(container);
QJsonObject containerConfig =
qvariant_cast<QJsonObject>(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole));
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
ErrorCode errorCode = ErrorCode::NoError;
QString config = m_configurator->wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig,
&errorCode);
if (errorCode) {
emit exportErrorOccurred(errorString(errorCode));
return;
}
config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::WireGuard, config);
auto configJson = QJsonDocument::fromJson(config.toUtf8()).object();
QStringList lines = configJson.value(config_key::config).toString().replace("\r", "").split("\n");
for (const QString &line : lines) {
m_config.append(line + "\n");
}
emit exportConfigChanged();
}
QString ExportController::getConfig()
{
return m_config;
}
QList<QString> ExportController::getQrCodes()
{
return m_qrCodes;
}
void ExportController::exportConfig(const QString &fileName)
{
SystemController::saveFile(fileName, m_config);
}
QList<QString> ExportController::generateQrCodeImageSeries(const QByteArray &data)
{
double k = 850;
quint8 chunksCount = std::ceil(data.size() / k);
QList<QString> chunks;
for (int i = 0; i < data.size(); i = i + k) {
QByteArray chunk;
QDataStream s(&chunk, QIODevice::WriteOnly);
s << amnezia::qrMagicCode << chunksCount << (quint8)std::round(i / k) << data.mid(i, k);
QByteArray ba = chunk.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(ba, qrcodegen::QrCode::Ecc::LOW);
QString svg = QString::fromStdString(toSvgString(qr, 0));
chunks.append(svgToBase64(svg));
}
return chunks;
}
QString ExportController::svgToBase64(const QString &image)
{
return "data:image/svg;base64," + QString::fromLatin1(image.toUtf8().toBase64().data());
}
int ExportController::getQrCodesCount()
{
return m_qrCodes.size();
}
void ExportController::clearPreviousConfig()
{
m_config.clear();
m_qrCodes.clear();
}

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