mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
refactor: refactor the application to the mvvm architecture (#2009)
* refactor: move business logic from servers model * refactor: move containersModel initialization * refactor: added protocol ui controller and removed settings class from protocols model * refactor: moved cli management to separate controller * refactor: moved app split to separate controller * refactor: moved site split to separate controller * refactor: moved allowed dns to separate controller * refactor: moved language logic to separate ui controller * refactor: removed Settings from devices model * refactor: moved configs and services api logit to separate core controller * refactor: added a layer with a repository between the storage and controllers * refactor: use child parent system instead of smart pointers for controllers and models initialization * refactor: moved install functions from server controller to install controller * refactor: install controller refactoring * chore: renamed exportController to exportUiController * refactor: separate export controller * refactor: removed VpnConfigurationsController * chore: renamed ServerController to SshSession * refactor: replaced ServerController to SshSession * chore: moved qml controllers to separate folder * chore: include fixes * chore: moved utils from core root to core/utils * chore: include fixes * chore: rename core/utils files to camelCase foramt * chore: include fixes * chore: moved some utils to api and selfhosted folders * chore: include fixes * chore: remove unused file * chore: moved serialization folder to core/utils * chore: include fixes * chore: moved some files from client root to core/utils * chore: include fixes * chore: moved ui utils to ui/utils folder * chore: include fixes * chore: move utils from root to ui/utils * chore: include fixes * chore: moved configurators to core/configurators * chore: include fixes * refactor: moved iap logic from ui controller to core * refactor: moved remaining core logic from ApiConfigsController to SubscriptionController * chore: rename apiNewsController to apiNewsUiController * refactor: moved core logic from news ui controller to core * chore: renamed apiConfigsController to subscriptionUiController * chore: include fixes * refactor: merge ApiSettingsController with SubscriptionUiController * chore: moved ui selfhosted controllers to separate folder * chore: include fixes * chore: rename connectionController to connectiomUiController * refactor: moved core logic from connectionUiController * chore: rename settingsController to settingsUiController * refactor: move core logic from settingsUiController * refactor: moved core controller signal/slot connections to separate class * fix: newsController fixes after refactoring * chore: rename model to camelCase * chore: include fixes * chore: remove unused code * chore: move selfhosted core to separate folder * chore: include fixes * chore: rename importController to importUiController * refactor: move core logic from importUiController * chore: minor fixes * chore: remove prem v1 migration * refactor: remove openvpn over cloak and openvpn over shadowsocks * refactor: removed protocolsForContainer function * refactor: add core models * refactor: replace json with c++ structs for server config * refactor: move getDnsPair to ServerConfigUtils * feat: add admin selfhosted config export test * feat: add multi import test * refactor: use coreController for tests * feat: add few simple tests * chore: qrepos in all core controllers * feat: add test for settings * refactor: remove repo dependency from configurators * chore: moved protocols to core folder * chore: include fixes * refactor: moved containersDefs, defs, apiDefs, protocolsDefs to different places * chore: include fixes * chore: build fixes * chore: build fixes * refactor: remove q repo and interface repo * feat: add test for ui servers model and controller * chore: renamed to camelCase * chore: include fixes * refactor: moved core logic from sites ui controller * fix: fixed api config processing * fix: fixed processed server index processing * refactor: protocol models now use c++ structs instead of json configs * refactor: servers model now use c++ struct instead of json config * fix: fixed default server index processing * fix: fix logs init * fix: fix secure settings load keys * chore: build fixes * fix: fixed clear settings * fix: fixed restore backup * fix: sshSession usage * fix: fixed export functions signatures * fix: return missing part from buildContainerWorker * fix: fixed server description on page home * refactor: add container config helpers functions * refactor: c++ structs instead of json * chore: add dns protocol config struct * refactor: move config utils functions to config structs * feat: add test for selfhosted server setup * refactor: separate resources.qrc * fix: fixed server rename * chore: return nameOverriddenByUser * fix: build fixes * fix: fixed models init * refactor: cleanup models usage * fix: fixed models init * chore: cleanup connections and functions signatures * chore: cleanup updateModel calls * feat: added cache to servers repo * chore: cleanup unused functions * chore: ssxray processing * chore: remove transportProtoWithDefault and portWithDefault functions * chore: removed proto types any and l2tp * refactor: moved some constants * fix: fixed native configs export * refactor: remove json from processConfigWith functions * fix: fixed processed server index usage * fix: qml warning fixes * chore: merge fixes * chore: update tests * fix: fixed xray config processing * fix: fixed split tunneling processing * chore: rename sites controllers and model * chore: rename fixes * chore: minor fixes * chore: remove ability to load backup from "file with connection settings" button * fix: fixed api device revoke * fix: remove full model update when renaming a user * fix: fixed premium/free server rename * fix: fixed selfhosted new server install * fix: fixed updateContainer function * fix: fixed revoke for external premium configs * feat: add native configs qr processing * chore: codestyle fixes * fix: fixed admin config create * chore: again remove ability to load backup from "file with connection settings" button * chore: minor fixes * fix: fixed variables initialization * fix: fixed qml imports * fix: minor fixes * fix: fix vpnConnection function calls * feat: add buckup error handling * fix: fixed admin config revok * fix: fixed selfhosted awg installation * fix: ad visability * feat: add empty check for primary dns * chore: minor fixes
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
/client/3rd-prebuild
|
/client/3rd-prebuild
|
||||||
/client/android
|
/client/android
|
||||||
/client/cmake
|
/client/cmake
|
||||||
/client/core/serialization
|
/client/core/utils/serialization
|
||||||
/client/daemon
|
/client/daemon
|
||||||
/client/fonts
|
/client/fonts
|
||||||
/client/images
|
/client/images
|
||||||
|
|||||||
@@ -62,7 +62,12 @@ if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
|||||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep)
|
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc)
|
qt6_add_resources(QRC ${QRC}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/images/images.qrc
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/images/flagKit.qrc
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/ui/qml/qml.qrc
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/server_scripts/serverScripts.qrc
|
||||||
|
)
|
||||||
|
|
||||||
# -- i18n begin
|
# -- i18n begin
|
||||||
set(CMAKE_AUTORCC ON)
|
set(CMAKE_AUTORCC ON)
|
||||||
@@ -228,6 +233,12 @@ if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(APPEND SOURCES ${CMAKE_CURRENT_LIST_DIR}/main.cpp)
|
||||||
|
|
||||||
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
||||||
|
|
||||||
# Finalize the executable so Qt can gather/deploy QML modules and plugins correctly (Android needs this).
|
# Finalize the executable so Qt can gather/deploy QML modules and plugins correctly (Android needs this).
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "amnezia_application.h"
|
#include "amneziaApplication.h"
|
||||||
|
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QFontDatabase>
|
#include <QFontDatabase>
|
||||||
@@ -15,17 +15,17 @@
|
|||||||
#include <QEvent>
|
#include <QEvent>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
#include <QtQuick/QQuickWindow>
|
||||||
|
#include <QWindow>
|
||||||
|
|
||||||
|
#include "core/protocols/qmlRegisterProtocols.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "ui/controllers/pageController.h"
|
#include "ui/controllers/qml/pageController.h"
|
||||||
#include "ui/models/installedAppsModel.h"
|
#include "ui/models/installedAppsModel.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#include "platforms/ios/QRCodeReaderBase.h"
|
#include "platforms/ios/QRCodeReaderBase.h"
|
||||||
|
|
||||||
#include "protocols/qml_register_protocols.h"
|
|
||||||
#include <QtQuick/QQuickWindow> // for QQuickWindow
|
|
||||||
#include <QWindow> // for qobject_cast<QWindow*>
|
|
||||||
|
|
||||||
bool AmneziaApplication::m_forceQuit = false;
|
bool AmneziaApplication::m_forceQuit = false;
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_C
|
|||||||
QFile::setPermissions(configLoc2, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
|
QFile::setPermissions(configLoc2, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_settings = std::shared_ptr<Settings>(new Settings);
|
m_settings = new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, this);
|
||||||
m_nam = new QNetworkAccessManager(this);
|
m_nam = new QNetworkAccessManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ void AmneziaApplication::init()
|
|||||||
m_engine->rootContext()->setContextProperty("IsMacOsNeBuild", false);
|
m_engine->rootContext()->setContextProperty("IsMacOsNeBuild", false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_vpnConnection.reset(new VpnConnection(m_settings));
|
m_vpnConnection.reset(new VpnConnection(nullptr, nullptr));
|
||||||
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
|
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
|
||||||
m_vpnConnectionThread.start();
|
m_vpnConnectionThread.start();
|
||||||
|
|
||||||
@@ -153,16 +153,6 @@ void AmneziaApplication::init()
|
|||||||
|
|
||||||
m_coreController->setQmlRoot();
|
m_coreController->setQmlRoot();
|
||||||
|
|
||||||
bool enabled = m_settings->isSaveLogs();
|
|
||||||
#ifndef Q_OS_ANDROID
|
|
||||||
if (enabled) {
|
|
||||||
if (!Logger::init(false)) {
|
|
||||||
qWarning() << "Initialization of debug subsystem failed";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
Logger::setServiceLogsEnabled(enabled);
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN //TODO
|
#ifdef Q_OS_WIN //TODO
|
||||||
if (m_parser.isSet(m_optAutostart))
|
if (m_parser.isSet(m_optAutostart))
|
||||||
m_coreController->pageController()->showOnStartup();
|
m_coreController->pageController()->showOnStartup();
|
||||||
@@ -207,13 +197,11 @@ void AmneziaApplication::registerTypes()
|
|||||||
qRegisterMetaType<ServerCredentials>("ServerCredentials");
|
qRegisterMetaType<ServerCredentials>("ServerCredentials");
|
||||||
|
|
||||||
qRegisterMetaType<DockerContainer>("DockerContainer");
|
qRegisterMetaType<DockerContainer>("DockerContainer");
|
||||||
|
using namespace amnezia::ProtocolEnumNS;
|
||||||
qRegisterMetaType<TransportProto>("TransportProto");
|
qRegisterMetaType<TransportProto>("TransportProto");
|
||||||
qRegisterMetaType<Proto>("Proto");
|
qRegisterMetaType<Proto>("Proto");
|
||||||
qRegisterMetaType<ServiceType>("ServiceType");
|
qRegisterMetaType<ServiceType>("ServiceType");
|
||||||
|
|
||||||
declareQmlProtocolEnum();
|
|
||||||
declareQmlContainerEnum();
|
|
||||||
|
|
||||||
qmlRegisterType<QRCodeReader>("QRCodeReader", 1, 0, "QRCodeReader");
|
qmlRegisterType<QRCodeReader>("QRCodeReader", 1, 0, "QRCodeReader");
|
||||||
|
|
||||||
m_containerProps.reset(new ContainerProps());
|
m_containerProps.reset(new ContainerProps());
|
||||||
@@ -227,6 +215,7 @@ void AmneziaApplication::registerTypes()
|
|||||||
|
|
||||||
qmlRegisterType<InstalledAppsModel>("InstalledAppsModel", 1, 0, "InstalledAppsModel");
|
qmlRegisterType<InstalledAppsModel>("InstalledAppsModel", 1, 0, "InstalledAppsModel");
|
||||||
|
|
||||||
|
amnezia::declareQmlProtocolEnum();
|
||||||
Vpn::declareQmlVpnConnectionStateEnum();
|
Vpn::declareQmlVpnConnectionStateEnum();
|
||||||
PageLoader::declareQmlPageEnum();
|
PageLoader::declareQmlPageEnum();
|
||||||
}
|
}
|
||||||
@@ -14,8 +14,10 @@
|
|||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
|
|
||||||
#include "core/controllers/coreController.h"
|
#include "core/controllers/coreController.h"
|
||||||
#include "settings.h"
|
#include "secureQSettings.h"
|
||||||
#include "vpnconnection.h"
|
#include "vpnConnection.h"
|
||||||
|
#include "ui/models/containerProps.h"
|
||||||
|
#include "ui/models/protocolProps.h"
|
||||||
|
|
||||||
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
|
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
|
||||||
|
|
||||||
@@ -51,7 +53,7 @@ public slots:
|
|||||||
private:
|
private:
|
||||||
static bool m_forceQuit;
|
static bool m_forceQuit;
|
||||||
QQmlApplicationEngine *m_engine {};
|
QQmlApplicationEngine *m_engine {};
|
||||||
std::shared_ptr<Settings> m_settings;
|
SecureQSettings* m_settings;
|
||||||
|
|
||||||
QScopedPointer<CoreController> m_coreController;
|
QScopedPointer<CoreController> m_coreController;
|
||||||
|
|
||||||
@@ -111,7 +111,6 @@ dependencies {
|
|||||||
implementation(project(":wireguard"))
|
implementation(project(":wireguard"))
|
||||||
implementation(project(":awg"))
|
implementation(project(":awg"))
|
||||||
implementation(project(":openvpn"))
|
implementation(project(":openvpn"))
|
||||||
implementation(project(":cloak"))
|
|
||||||
implementation(project(":xray"))
|
implementation(project(":xray"))
|
||||||
implementation(libs.androidx.core)
|
implementation(libs.androidx.core)
|
||||||
implementation(libs.androidx.activity)
|
implementation(libs.androidx.activity)
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id(libs.plugins.android.library.get().pluginId)
|
|
||||||
id(libs.plugins.kotlin.android.get().pluginId)
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
jvmToolchain(17)
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "org.amnezia.vpn.protocol.cloak"
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compileOnly(project(":utils"))
|
|
||||||
compileOnly(project(":protocolApi"))
|
|
||||||
implementation(project(":openvpn"))
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package org.amnezia.vpn.protocol.cloak
|
|
||||||
|
|
||||||
import android.util.Base64
|
|
||||||
import net.openvpn.ovpn3.ClientAPI_Config
|
|
||||||
import org.amnezia.vpn.protocol.openvpn.OpenVpn
|
|
||||||
import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary
|
|
||||||
import org.json.JSONObject
|
|
||||||
|
|
||||||
class Cloak : OpenVpn() {
|
|
||||||
|
|
||||||
override fun internalInit() {
|
|
||||||
super.internalInit()
|
|
||||||
if (!isInitialized) loadSharedLibrary(context, "ck-ovpn-plugin")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun parseConfig(config: JSONObject): ClientAPI_Config {
|
|
||||||
val openVpnConfig = ClientAPI_Config()
|
|
||||||
|
|
||||||
val openVpnConfigStr = config.getJSONObject("openvpn_config_data").getString("config")
|
|
||||||
val cloakConfigJson = checkCloakJson(config.getJSONObject("cloak_config_data"))
|
|
||||||
val cloakConfigStr = Base64.encodeToString(cloakConfigJson.toString().toByteArray(), Base64.DEFAULT)
|
|
||||||
|
|
||||||
val configStr = "$openVpnConfigStr\n<cloak>\n$cloakConfigStr\n</cloak>\n"
|
|
||||||
|
|
||||||
openVpnConfig.usePluggableTransports = true
|
|
||||||
openVpnConfig.content = configStr
|
|
||||||
return openVpnConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkCloakJson(cloakConfigJson: JSONObject): JSONObject {
|
|
||||||
cloakConfigJson.put("NumConn", 1)
|
|
||||||
cloakConfigJson.put("ProxyMethod", "openvpn")
|
|
||||||
if (cloakConfigJson.has("port")) {
|
|
||||||
val port = cloakConfigJson["port"]
|
|
||||||
cloakConfigJson.remove("port")
|
|
||||||
cloakConfigJson.put("RemotePort", port)
|
|
||||||
}
|
|
||||||
if (cloakConfigJson.has("remote")) {
|
|
||||||
val remote = cloakConfigJson["remote"]
|
|
||||||
cloakConfigJson.remove("remote")
|
|
||||||
cloakConfigJson.put("RemoteHost", remote)
|
|
||||||
}
|
|
||||||
return cloakConfigJson
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -35,7 +35,6 @@ include(":protocolApi")
|
|||||||
include(":wireguard")
|
include(":wireguard")
|
||||||
include(":awg")
|
include(":awg")
|
||||||
include(":openvpn")
|
include(":openvpn")
|
||||||
include(":cloak")
|
|
||||||
include(":xray")
|
include(":xray")
|
||||||
include(":xray:libXray")
|
include(":xray:libXray")
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.amnezia.vpn
|
|||||||
|
|
||||||
import org.amnezia.vpn.protocol.Protocol
|
import org.amnezia.vpn.protocol.Protocol
|
||||||
import org.amnezia.vpn.protocol.awg.Awg
|
import org.amnezia.vpn.protocol.awg.Awg
|
||||||
import org.amnezia.vpn.protocol.cloak.Cloak
|
|
||||||
import org.amnezia.vpn.protocol.openvpn.OpenVpn
|
import org.amnezia.vpn.protocol.openvpn.OpenVpn
|
||||||
import org.amnezia.vpn.protocol.wireguard.Wireguard
|
import org.amnezia.vpn.protocol.wireguard.Wireguard
|
||||||
import org.amnezia.vpn.protocol.xray.Xray
|
import org.amnezia.vpn.protocol.xray.Xray
|
||||||
@@ -36,14 +35,6 @@ enum class VpnProto(
|
|||||||
override fun createProtocol(): Protocol = OpenVpn()
|
override fun createProtocol(): Protocol = OpenVpn()
|
||||||
},
|
},
|
||||||
|
|
||||||
CLOAK(
|
|
||||||
"Cloak",
|
|
||||||
"org.amnezia.vpn:amneziaOpenVpnService",
|
|
||||||
OpenVpnService::class.java
|
|
||||||
) {
|
|
||||||
override fun createProtocol(): Protocol = Cloak()
|
|
||||||
},
|
|
||||||
|
|
||||||
XRAY(
|
XRAY(
|
||||||
"XRay",
|
"XRay",
|
||||||
"org.amnezia.vpn:amneziaXrayService",
|
"org.amnezia.vpn:amneziaXrayService",
|
||||||
@@ -72,4 +63,4 @@ enum class VpnProto(
|
|||||||
companion object {
|
companion object {
|
||||||
fun get(protocolName: String): VpnProto = VpnProto.valueOf(protocolName.uppercase())
|
fun get(protocolName: String): VpnProto = VpnProto.valueOf(protocolName.uppercase())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,13 +77,13 @@ class Xray : Protocol() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val xrayJsonConfig = config.optJSONObject("xray_config_data")
|
val xrayConfigData = config.optJSONObject("xray_config_data")
|
||||||
?: config.optJSONObject("ssxray_config_data")
|
?: config.optJSONObject("ssxray_config_data")
|
||||||
?: throw BadConfigException("config_data not found")
|
?: throw BadConfigException("config_data not found")
|
||||||
|
val xrayJsonConfig = JSONObject(xrayConfigData.optString("config"))
|
||||||
|
|
||||||
// Inject SOCKS5 auth before starting xray. Re-uses existing credentials if present.
|
// Inject SOCKS5 auth before starting xray. Re-uses existing credentials if present.
|
||||||
ensureInboundAuth(xrayJsonConfig)
|
ensureInboundAuth(xrayJsonConfig)
|
||||||
|
|
||||||
val xrayConfig = parseConfig(config, xrayJsonConfig)
|
val xrayConfig = parseConfig(config, xrayJsonConfig)
|
||||||
|
|
||||||
(xrayJsonConfig.optJSONObject("log") ?: JSONObject().also { xrayJsonConfig.put("log", it) })
|
(xrayJsonConfig.optJSONObject("log") ?: JSONObject().also { xrayJsonConfig.put("log", it) })
|
||||||
|
|||||||
@@ -31,15 +31,15 @@ link_directories(${CMAKE_CURRENT_SOURCE_DIR}/platforms/android)
|
|||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.h
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.h
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.h
|
${CMAKE_CURRENT_SOURCE_DIR}/core/protocols/androidVpnProtocol.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.h
|
${CMAKE_CURRENT_SOURCE_DIR}/core/utils/installedAppsImageProvider.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/core/protocols/androidVpnProtocol.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/core/utils/installedAppsImageProvider.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach(abi IN ITEMS ${QT_ANDROID_ABIS})
|
foreach(abi IN ITEMS ${QT_ANDROID_ABIS})
|
||||||
|
|||||||
@@ -28,11 +28,11 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
|||||||
|
|
||||||
|
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.h
|
${CMAKE_CURRENT_SOURCE_DIR}/ui/utils/macosUtil.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.mm
|
${CMAKE_CURRENT_SOURCE_DIR}/ui/utils/macosUtil.mm
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,34 +1,68 @@
|
|||||||
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
|
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
|
||||||
|
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CLIENT_ROOT_DIR}/migrations.h
|
${CLIENT_ROOT_DIR}/core/utils/migrations.h
|
||||||
${CLIENT_ROOT_DIR}/../ipc/ipc.h
|
${CLIENT_ROOT_DIR}/../ipc/ipc.h
|
||||||
${CLIENT_ROOT_DIR}/amnezia_application.h
|
${CLIENT_ROOT_DIR}/amneziaApplication.h
|
||||||
${CLIENT_ROOT_DIR}/containers/containers_defs.h
|
${CLIENT_ROOT_DIR}/core/utils/errorCodes.h
|
||||||
${CLIENT_ROOT_DIR}/core/defs.h
|
${CLIENT_ROOT_DIR}/core/utils/routeModes.h
|
||||||
${CLIENT_ROOT_DIR}/core/errorstrings.h
|
${CLIENT_ROOT_DIR}/core/utils/commonStructs.h
|
||||||
${CLIENT_ROOT_DIR}/core/scripts_registry.h
|
${CLIENT_ROOT_DIR}/core/utils/containerEnum.h
|
||||||
${CLIENT_ROOT_DIR}/core/server_defs.h
|
${CLIENT_ROOT_DIR}/core/utils/protocolEnum.h
|
||||||
${CLIENT_ROOT_DIR}/core/api/apiDefs.h
|
${CLIENT_ROOT_DIR}/core/utils/containers/containerUtils.h
|
||||||
${CLIENT_ROOT_DIR}/core/qrCodeUtils.h
|
${CLIENT_ROOT_DIR}/core/protocols/protocolUtils.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/constants/configKeys.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/constants/protocolConstants.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/constants/apiKeys.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/constants/apiConstants.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/api/apiEnums.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/errorStrings.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/selfhosted/scriptsRegistry.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/qrCodeUtils.h
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/coreController.h
|
${CLIENT_ROOT_DIR}/core/controllers/coreController.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/coreSignalHandlers.h
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/gatewayController.h
|
${CLIENT_ROOT_DIR}/core/controllers/gatewayController.h
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/serverController.h
|
${CLIENT_ROOT_DIR}/core/utils/selfhosted/sshSession.h
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/vpnConfigurationController.h
|
${CLIENT_ROOT_DIR}/core/controllers/serversController.h
|
||||||
${CLIENT_ROOT_DIR}/protocols/protocols_defs.h
|
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/usersController.h
|
||||||
${CLIENT_ROOT_DIR}/protocols/qml_register_protocols.h
|
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/installController.h
|
||||||
${CLIENT_ROOT_DIR}/ui/pages.h
|
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/exportController.h
|
||||||
${CLIENT_ROOT_DIR}/ui/qautostart.h
|
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/importController.h
|
||||||
${CLIENT_ROOT_DIR}/protocols/vpnprotocol.h
|
${CLIENT_ROOT_DIR}/core/installers/installerBase.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/installers/awgInstaller.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/installers/wireguardInstaller.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/installers/openvpnInstaller.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/installers/xrayInstaller.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/installers/torInstaller.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/installers/sftpInstaller.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/installers/socks5Installer.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/appSplitTunnelingController.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/ipSplitTunnelingController.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/allowedDnsController.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/exportController.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/connectionController.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/settingsController.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/api/servicesCatalogController.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/api/subscriptionController.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/api/newsController.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/repositories/secureServersRepository.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/repositories/secureAppSettingsRepository.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/protocols/qmlRegisterProtocols.h
|
||||||
|
${CLIENT_ROOT_DIR}/ui/utils/pages.h
|
||||||
|
${CLIENT_ROOT_DIR}/ui/utils/qAutoStart.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/protocols/vpnProtocol.h
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/version.h
|
${CMAKE_CURRENT_BINARY_DIR}/version.h
|
||||||
${CLIENT_ROOT_DIR}/core/sshclient.h
|
${CLIENT_ROOT_DIR}/core/utils/selfhosted/sshClient.h
|
||||||
${CLIENT_ROOT_DIR}/core/networkUtilities.h
|
${CLIENT_ROOT_DIR}/core/utils/networkUtilities.h
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/serialization.h
|
${CLIENT_ROOT_DIR}/core/utils/serialization/serialization.h
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/transfer.h
|
${CLIENT_ROOT_DIR}/core/utils/serialization/transfer.h
|
||||||
${CLIENT_ROOT_DIR}/../common/logger/logger.h
|
${CLIENT_ROOT_DIR}/../common/logger/logger.h
|
||||||
${CLIENT_ROOT_DIR}/utils/qmlUtils.h
|
${CLIENT_ROOT_DIR}/ui/utils/qmlUtils.h
|
||||||
${CLIENT_ROOT_DIR}/core/api/apiUtils.h
|
${CLIENT_ROOT_DIR}/core/utils/api/apiUtils.h
|
||||||
${CLIENT_ROOT_DIR}/core/osSignalHandler.h
|
${CLIENT_ROOT_DIR}/core/utils/osSignalHandler.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/utilities.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/managementServer.h
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/constants.h
|
||||||
)
|
)
|
||||||
|
|
||||||
# Mozilla headres
|
# Mozilla headres
|
||||||
@@ -47,39 +81,64 @@ endif()
|
|||||||
|
|
||||||
if(NOT ANDROID)
|
if(NOT ANDROID)
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CLIENT_ROOT_DIR}/ui/notificationhandler.h
|
${CLIENT_ROOT_DIR}/ui/utils/notificationHandler.h
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CLIENT_ROOT_DIR}/migrations.cpp
|
${CLIENT_ROOT_DIR}/core/utils/migrations.cpp
|
||||||
${CLIENT_ROOT_DIR}/amnezia_application.cpp
|
${CLIENT_ROOT_DIR}/amneziaApplication.cpp
|
||||||
${CLIENT_ROOT_DIR}/containers/containers_defs.cpp
|
${CLIENT_ROOT_DIR}/core/utils/errorStrings.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/errorstrings.cpp
|
${CLIENT_ROOT_DIR}/core/utils/containers/containerUtils.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/scripts_registry.cpp
|
${CLIENT_ROOT_DIR}/core/protocols/protocolUtils.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/server_defs.cpp
|
${CLIENT_ROOT_DIR}/core/utils/selfhosted/scriptsRegistry.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/qrCodeUtils.cpp
|
${CLIENT_ROOT_DIR}/core/utils/qrCodeUtils.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/coreController.cpp
|
${CLIENT_ROOT_DIR}/core/controllers/coreController.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/coreSignalHandlers.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/gatewayController.cpp
|
${CLIENT_ROOT_DIR}/core/controllers/gatewayController.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/serverController.cpp
|
${CLIENT_ROOT_DIR}/core/utils/selfhosted/sshSession.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/controllers/vpnConfigurationController.cpp
|
${CLIENT_ROOT_DIR}/core/controllers/serversController.cpp
|
||||||
${CLIENT_ROOT_DIR}/protocols/protocols_defs.cpp
|
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/usersController.cpp
|
||||||
${CLIENT_ROOT_DIR}/ui/qautostart.cpp
|
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/installController.cpp
|
||||||
${CLIENT_ROOT_DIR}/protocols/vpnprotocol.cpp
|
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/exportController.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/sshclient.cpp
|
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/importController.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/networkUtilities.cpp
|
${CLIENT_ROOT_DIR}/core/installers/installerBase.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/outbound.cpp
|
${CLIENT_ROOT_DIR}/core/installers/awgInstaller.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/inbound.cpp
|
${CLIENT_ROOT_DIR}/core/installers/wireguardInstaller.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/ss.cpp
|
${CLIENT_ROOT_DIR}/core/installers/openvpnInstaller.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/ssd.cpp
|
${CLIENT_ROOT_DIR}/core/installers/xrayInstaller.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/vless.cpp
|
${CLIENT_ROOT_DIR}/core/installers/torInstaller.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/trojan.cpp
|
${CLIENT_ROOT_DIR}/core/installers/sftpInstaller.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/vmess.cpp
|
${CLIENT_ROOT_DIR}/core/installers/socks5Installer.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/serialization/vmess_new.cpp
|
${CLIENT_ROOT_DIR}/core/controllers/appSplitTunnelingController.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/ipSplitTunnelingController.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/allowedDnsController.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/selfhosted/exportController.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/connectionController.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/settingsController.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/api/servicesCatalogController.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/api/subscriptionController.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/controllers/api/newsController.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/repositories/secureServersRepository.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/repositories/secureAppSettingsRepository.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/ui/utils/qAutoStart.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/protocols/vpnProtocol.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/selfhosted/sshClient.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/networkUtilities.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/serialization/outbound.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/serialization/inbound.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/serialization/ss.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/serialization/ssd.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/serialization/vless.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/serialization/trojan.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/serialization/vmess.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/serialization/vmess_new.cpp
|
||||||
${CLIENT_ROOT_DIR}/../common/logger/logger.cpp
|
${CLIENT_ROOT_DIR}/../common/logger/logger.cpp
|
||||||
${CLIENT_ROOT_DIR}/utils/qmlUtils.cpp
|
${CLIENT_ROOT_DIR}/ui/utils/qmlUtils.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/api/apiUtils.cpp
|
${CLIENT_ROOT_DIR}/core/utils/api/apiUtils.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/osSignalHandler.cpp
|
${CLIENT_ROOT_DIR}/core/utils/osSignalHandler.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/utilities.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/core/utils/managementServer.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Mozilla sources
|
# Mozilla sources
|
||||||
@@ -100,29 +159,41 @@ if(APPLE AND NOT IOS)
|
|||||||
list(APPEND HEADERS
|
list(APPEND HEADERS
|
||||||
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.h
|
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.h
|
||||||
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.h
|
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.h
|
||||||
${CLIENT_ROOT_DIR}/ui/macos_util.h
|
${CLIENT_ROOT_DIR}/ui/utils/macosUtil.h
|
||||||
)
|
)
|
||||||
list(APPEND SOURCES
|
list(APPEND SOURCES
|
||||||
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.mm
|
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.mm
|
||||||
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.mm
|
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.mm
|
||||||
${CLIENT_ROOT_DIR}/ui/macos_util.mm
|
${CLIENT_ROOT_DIR}/ui/utils/macosUtil.mm
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT ANDROID)
|
if(NOT ANDROID)
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CLIENT_ROOT_DIR}/ui/notificationhandler.cpp
|
${CLIENT_ROOT_DIR}/ui/utils/notificationHandler.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
file(GLOB COMMON_FILES_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/*.h)
|
set(COMMON_FILES_H
|
||||||
file(GLOB COMMON_FILES_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/*.cpp)
|
${CLIENT_ROOT_DIR}/amneziaApplication.h
|
||||||
|
${CLIENT_ROOT_DIR}/secureQSettings.h
|
||||||
|
${CLIENT_ROOT_DIR}/vpnConnection.h
|
||||||
|
)
|
||||||
|
|
||||||
|
set(COMMON_FILES_CPP
|
||||||
|
${CLIENT_ROOT_DIR}/amneziaApplication.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/secureQSettings.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/vpnConnection.cpp
|
||||||
|
)
|
||||||
|
|
||||||
file(GLOB_RECURSE PAGE_LOGIC_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/pages_logic/*.h)
|
file(GLOB_RECURSE PAGE_LOGIC_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/pages_logic/*.h)
|
||||||
file(GLOB_RECURSE PAGE_LOGIC_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/pages_logic/*.cpp)
|
file(GLOB_RECURSE PAGE_LOGIC_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/pages_logic/*.cpp)
|
||||||
|
|
||||||
file(GLOB CONFIGURATORS_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/configurators/*.h)
|
file(GLOB CONFIGURATORS_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/core/configurators/*.h)
|
||||||
file(GLOB CONFIGURATORS_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/configurators/*.cpp)
|
file(GLOB CONFIGURATORS_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/core/configurators/*.cpp)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE CORE_MODELS_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/core/models/*.h)
|
||||||
|
file(GLOB_RECURSE CORE_MODELS_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/core/models/*.cpp)
|
||||||
|
|
||||||
file(GLOB UI_MODELS_H CONFIGURE_DEPENDS
|
file(GLOB UI_MODELS_H CONFIGURE_DEPENDS
|
||||||
${CLIENT_ROOT_DIR}/ui/models/*.h
|
${CLIENT_ROOT_DIR}/ui/models/*.h
|
||||||
@@ -140,16 +211,21 @@ file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS
|
|||||||
file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS
|
file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS
|
||||||
${CLIENT_ROOT_DIR}/ui/controllers/*.h
|
${CLIENT_ROOT_DIR}/ui/controllers/*.h
|
||||||
${CLIENT_ROOT_DIR}/ui/controllers/api/*.h
|
${CLIENT_ROOT_DIR}/ui/controllers/api/*.h
|
||||||
|
${CLIENT_ROOT_DIR}/ui/controllers/qml/*.h
|
||||||
|
${CLIENT_ROOT_DIR}/ui/controllers/selfhosted/*.h
|
||||||
)
|
)
|
||||||
file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS
|
file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS
|
||||||
${CLIENT_ROOT_DIR}/ui/controllers/*.cpp
|
${CLIENT_ROOT_DIR}/ui/controllers/*.cpp
|
||||||
${CLIENT_ROOT_DIR}/ui/controllers/api/*.cpp
|
${CLIENT_ROOT_DIR}/ui/controllers/api/*.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/ui/controllers/qml/*.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/ui/controllers/selfhosted/*.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${COMMON_FILES_H}
|
${COMMON_FILES_H}
|
||||||
${PAGE_LOGIC_H}
|
${PAGE_LOGIC_H}
|
||||||
${CONFIGURATORS_H}
|
${CONFIGURATORS_H}
|
||||||
|
${CORE_MODELS_H}
|
||||||
${UI_MODELS_H}
|
${UI_MODELS_H}
|
||||||
${UI_CONTROLLERS_H}
|
${UI_CONTROLLERS_H}
|
||||||
)
|
)
|
||||||
@@ -157,17 +233,18 @@ set(SOURCES ${SOURCES}
|
|||||||
${COMMON_FILES_CPP}
|
${COMMON_FILES_CPP}
|
||||||
${PAGE_LOGIC_CPP}
|
${PAGE_LOGIC_CPP}
|
||||||
${CONFIGURATORS_CPP}
|
${CONFIGURATORS_CPP}
|
||||||
|
${CORE_MODELS_CPP}
|
||||||
${UI_MODELS_CPP}
|
${UI_MODELS_CPP}
|
||||||
${UI_CONTROLLERS_CPP}
|
${UI_CONTROLLERS_CPP}
|
||||||
)
|
)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.h
|
${CLIENT_ROOT_DIR}/core/protocols/ikev2VpnProtocolWindows.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.cpp
|
${CLIENT_ROOT_DIR}/core/protocols/ikev2VpnProtocolWindows.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(RESOURCES ${RESOURCES}
|
set(RESOURCES ${RESOURCES}
|
||||||
@@ -180,37 +257,33 @@ if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
|||||||
add_compile_definitions(AMNEZIA_DESKTOP)
|
add_compile_definitions(AMNEZIA_DESKTOP)
|
||||||
|
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CLIENT_ROOT_DIR}/core/ipcclient.h
|
${CLIENT_ROOT_DIR}/core/utils/ipcClient.h
|
||||||
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.h
|
${CLIENT_ROOT_DIR}/ui/utils/systemTrayNotificationHandler.h
|
||||||
${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.h
|
${CLIENT_ROOT_DIR}/core/protocols/openVpnProtocol.h
|
||||||
${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.h
|
${CLIENT_ROOT_DIR}/core/protocols/wireGuardProtocol.h
|
||||||
${CLIENT_ROOT_DIR}/protocols/shadowsocksvpnprotocol.h
|
${CLIENT_ROOT_DIR}/core/protocols/xrayProtocol.h
|
||||||
${CLIENT_ROOT_DIR}/protocols/wireguardprotocol.h
|
${CLIENT_ROOT_DIR}/core/protocols/awgProtocol.h
|
||||||
${CLIENT_ROOT_DIR}/protocols/xrayprotocol.h
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/awgprotocol.h
|
|
||||||
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h
|
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CLIENT_ROOT_DIR}/core/ipcclient.cpp
|
${CLIENT_ROOT_DIR}/core/utils/ipcClient.cpp
|
||||||
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
|
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
|
||||||
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.cpp
|
${CLIENT_ROOT_DIR}/ui/utils/systemTrayNotificationHandler.cpp
|
||||||
${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.cpp
|
${CLIENT_ROOT_DIR}/core/protocols/openVpnProtocol.cpp
|
||||||
${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.cpp
|
${CLIENT_ROOT_DIR}/core/protocols/wireGuardProtocol.cpp
|
||||||
${CLIENT_ROOT_DIR}/protocols/shadowsocksvpnprotocol.cpp
|
${CLIENT_ROOT_DIR}/core/protocols/xrayProtocol.cpp
|
||||||
${CLIENT_ROOT_DIR}/protocols/wireguardprotocol.cpp
|
${CLIENT_ROOT_DIR}/core/protocols/awgProtocol.cpp
|
||||||
${CLIENT_ROOT_DIR}/protocols/xrayprotocol.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/protocols/awgprotocol.cpp
|
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE AND MACOS_NE)
|
if(APPLE AND MACOS_NE)
|
||||||
# Include only the tray notification handler in NE builds
|
# Include only the tray notification handler in NE builds
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.h
|
${CLIENT_ROOT_DIR}/ui/utils/systemTrayNotificationHandler.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.cpp
|
${CLIENT_ROOT_DIR}/ui/utils/systemTrayNotificationHandler.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
#include "awg_configurator.h"
|
|
||||||
#include "protocols/protocols_defs.h"
|
|
||||||
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
|
|
||||||
AwgConfigurator::AwgConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
|
||||||
: WireguardConfigurator(settings, serverController, true, parent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString AwgConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
|
||||||
ErrorCode &errorCode)
|
|
||||||
{
|
|
||||||
QString config = WireguardConfigurator::createConfig(credentials, container, containerConfig, errorCode);
|
|
||||||
|
|
||||||
QJsonObject jsonConfig = QJsonDocument::fromJson(config.toUtf8()).object();
|
|
||||||
QString awgConfig = jsonConfig.value(config_key::config).toString();
|
|
||||||
|
|
||||||
QMap<QString, QString> configMap;
|
|
||||||
auto configLines = awgConfig.split("\n");
|
|
||||||
for (auto &line : configLines) {
|
|
||||||
auto trimmedLine = line.trimmed();
|
|
||||||
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
QStringList parts = trimmedLine.split(" = ");
|
|
||||||
if (parts.count() == 2) {
|
|
||||||
configMap.insert(parts[0].trimmed(), parts[1].trimmed());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonConfig[config_key::junkPacketCount] = configMap.value(config_key::junkPacketCount);
|
|
||||||
jsonConfig[config_key::junkPacketMinSize] = configMap.value(config_key::junkPacketMinSize);
|
|
||||||
jsonConfig[config_key::junkPacketMaxSize] = configMap.value(config_key::junkPacketMaxSize);
|
|
||||||
jsonConfig[config_key::initPacketJunkSize] = configMap.value(config_key::initPacketJunkSize);
|
|
||||||
jsonConfig[config_key::responsePacketJunkSize] = configMap.value(config_key::responsePacketJunkSize);
|
|
||||||
jsonConfig[config_key::initPacketMagicHeader] = configMap.value(config_key::initPacketMagicHeader);
|
|
||||||
jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader);
|
|
||||||
jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader);
|
|
||||||
jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader);
|
|
||||||
|
|
||||||
if (container == DockerContainer::Awg2) {
|
|
||||||
jsonConfig[config_key::cookieReplyPacketJunkSize] = configMap.value(config_key::cookieReplyPacketJunkSize);
|
|
||||||
jsonConfig[config_key::transportPacketJunkSize] = configMap.value(config_key::transportPacketJunkSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonConfig[config_key::specialJunk1] = configMap.value(amnezia::config_key::specialJunk1);
|
|
||||||
jsonConfig[config_key::specialJunk2] = configMap.value(amnezia::config_key::specialJunk2);
|
|
||||||
jsonConfig[config_key::specialJunk3] = configMap.value(amnezia::config_key::specialJunk3);
|
|
||||||
jsonConfig[config_key::specialJunk4] = configMap.value(amnezia::config_key::specialJunk4);
|
|
||||||
jsonConfig[config_key::specialJunk5] = configMap.value(amnezia::config_key::specialJunk5);
|
|
||||||
|
|
||||||
jsonConfig[config_key::mtu] =
|
|
||||||
containerConfig.value(ProtocolProps::protoToString(Proto::Awg)).toObject().value(config_key::mtu).toString(protocols::awg::defaultMtu);
|
|
||||||
|
|
||||||
return QJsonDocument(jsonConfig).toJson();
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#ifndef AWGCONFIGURATOR_H
|
|
||||||
#define AWGCONFIGURATOR_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include "wireguard_configurator.h"
|
|
||||||
|
|
||||||
class AwgConfigurator : public WireguardConfigurator
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
AwgConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // AWGCONFIGURATOR_H
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#include "cloak_configurator.h"
|
|
||||||
|
|
||||||
#include <QFile>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
|
||||||
#include "core/controllers/serverController.h"
|
|
||||||
|
|
||||||
CloakConfigurator::CloakConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
|
||||||
: ConfiguratorBase(settings, serverController, parent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString CloakConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
|
||||||
ErrorCode &errorCode)
|
|
||||||
{
|
|
||||||
QString cloakPublicKey =
|
|
||||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::cloak::ckPublicKeyPath, errorCode);
|
|
||||||
cloakPublicKey.replace("\n", "");
|
|
||||||
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
QString cloakBypassUid =
|
|
||||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::cloak::ckBypassUidKeyPath, errorCode);
|
|
||||||
cloakBypassUid.replace("\n", "");
|
|
||||||
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject config;
|
|
||||||
config.insert("Transport", "direct");
|
|
||||||
config.insert("ProxyMethod", "openvpn");
|
|
||||||
config.insert("EncryptionMethod", "aes-gcm");
|
|
||||||
config.insert("UID", cloakBypassUid);
|
|
||||||
config.insert("PublicKey", cloakPublicKey);
|
|
||||||
config.insert("ServerName", "$FAKE_WEB_SITE_ADDRESS");
|
|
||||||
config.insert("NumConn", 1);
|
|
||||||
config.insert("BrowserSig", "chrome");
|
|
||||||
config.insert("StreamTimeout", 300);
|
|
||||||
config.insert("RemoteHost", credentials.hostName);
|
|
||||||
config.insert("RemotePort", "$CLOAK_SERVER_PORT");
|
|
||||||
|
|
||||||
QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(),
|
|
||||||
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
|
||||||
|
|
||||||
return textCfg;
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#ifndef CLOAK_CONFIGURATOR_H
|
|
||||||
#define CLOAK_CONFIGURATOR_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include "configurator_base.h"
|
|
||||||
|
|
||||||
using namespace amnezia;
|
|
||||||
|
|
||||||
class CloakConfigurator : public ConfiguratorBase
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
CloakConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // CLOAK_CONFIGURATOR_H
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#include "configurator_base.h"
|
|
||||||
|
|
||||||
ConfiguratorBase::ConfiguratorBase(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
|
||||||
: QObject { parent }, m_settings(settings), m_serverController(serverController)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ConfiguratorBase::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
|
||||||
QString &protocolConfigString)
|
|
||||||
{
|
|
||||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
|
||||||
return protocolConfigString;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ConfiguratorBase::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
|
||||||
QString &protocolConfigString)
|
|
||||||
{
|
|
||||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
|
||||||
return protocolConfigString;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConfiguratorBase::processConfigWithDnsSettings(const QPair<QString, QString> &dns, QString &protocolConfigString)
|
|
||||||
{
|
|
||||||
protocolConfigString.replace("$PRIMARY_DNS", dns.first);
|
|
||||||
protocolConfigString.replace("$SECONDARY_DNS", dns.second);
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#ifndef CONFIGURATORBASE_H
|
|
||||||
#define CONFIGURATORBASE_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
|
||||||
#include "core/defs.h"
|
|
||||||
#include "core/controllers/serverController.h"
|
|
||||||
#include "settings.h"
|
|
||||||
|
|
||||||
class ConfiguratorBase : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit ConfiguratorBase(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
virtual QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode) = 0;
|
|
||||||
|
|
||||||
virtual QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
|
||||||
QString &protocolConfigString);
|
|
||||||
virtual QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
|
||||||
QString &protocolConfigString);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void processConfigWithDnsSettings(const QPair<QString, QString> &dns, QString &protocolConfigString);
|
|
||||||
|
|
||||||
std::shared_ptr<Settings> m_settings;
|
|
||||||
QSharedPointer<ServerController> m_serverController;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // CONFIGURATORBASE_H
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
#ifndef IKEV2_CONFIGURATOR_H
|
|
||||||
#define IKEV2_CONFIGURATOR_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QProcessEnvironment>
|
|
||||||
|
|
||||||
#include "configurator_base.h"
|
|
||||||
#include "core/defs.h"
|
|
||||||
|
|
||||||
class Ikev2Configurator : public ConfiguratorBase
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
Ikev2Configurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
struct ConnectionData {
|
|
||||||
QByteArray clientCert; // p12 client cert
|
|
||||||
QByteArray caCert; // p12 server cert
|
|
||||||
QString clientId;
|
|
||||||
QString password; // certificate password
|
|
||||||
QString host; // host ip
|
|
||||||
};
|
|
||||||
|
|
||||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
|
||||||
|
|
||||||
QString genIkev2Config(const ConnectionData &connData);
|
|
||||||
QString genMobileConfig(const ConnectionData &connData);
|
|
||||||
QString genStrongSwanConfig(const ConnectionData &connData);
|
|
||||||
|
|
||||||
ConnectionData prepareIkev2Config(const ServerCredentials &credentials,
|
|
||||||
DockerContainer container, ErrorCode &errorCode);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // IKEV2_CONFIGURATOR_H
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
#ifndef OPENVPN_CONFIGURATOR_H
|
|
||||||
#define OPENVPN_CONFIGURATOR_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QProcessEnvironment>
|
|
||||||
|
|
||||||
#include "configurator_base.h"
|
|
||||||
#include "core/defs.h"
|
|
||||||
|
|
||||||
class OpenVpnConfigurator : public ConfiguratorBase
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
OpenVpnConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
struct ConnectionData
|
|
||||||
{
|
|
||||||
QString clientId;
|
|
||||||
QString request; // certificate request
|
|
||||||
QString privKey; // client private key
|
|
||||||
QString clientCert; // client signed certificate
|
|
||||||
QString caCert; // server certificate
|
|
||||||
QString taKey; // tls-auth key
|
|
||||||
QString host; // host ip
|
|
||||||
};
|
|
||||||
|
|
||||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
|
||||||
|
|
||||||
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
|
||||||
QString &protocolConfigString);
|
|
||||||
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
|
||||||
QString &protocolConfigString);
|
|
||||||
|
|
||||||
static ConnectionData createCertRequest();
|
|
||||||
|
|
||||||
private:
|
|
||||||
ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container,
|
|
||||||
ErrorCode &errorCode);
|
|
||||||
ErrorCode signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // OPENVPN_CONFIGURATOR_H
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
#include "shadowsocks_configurator.h"
|
|
||||||
|
|
||||||
#include <QFile>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
|
||||||
#include "core/controllers/serverController.h"
|
|
||||||
|
|
||||||
ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
|
||||||
QObject *parent)
|
|
||||||
: ConfiguratorBase(settings, serverController, parent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ShadowSocksConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
|
||||||
{
|
|
||||||
QString ssKey =
|
|
||||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::shadowsocks::ssKeyPath, errorCode);
|
|
||||||
ssKey.replace("\n", "");
|
|
||||||
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject config;
|
|
||||||
config.insert("server", credentials.hostName);
|
|
||||||
config.insert("server_port", "$SHADOWSOCKS_SERVER_PORT");
|
|
||||||
config.insert("local_port", "$SHADOWSOCKS_LOCAL_PORT");
|
|
||||||
config.insert("password", ssKey);
|
|
||||||
config.insert("timeout", 60);
|
|
||||||
config.insert("method", "$SHADOWSOCKS_CIPHER");
|
|
||||||
|
|
||||||
QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(),
|
|
||||||
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
|
||||||
|
|
||||||
// qDebug().noquote() << textCfg;
|
|
||||||
return textCfg;
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#ifndef SHADOWSOCKS_CONFIGURATOR_H
|
|
||||||
#define SHADOWSOCKS_CONFIGURATOR_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include "configurator_base.h"
|
|
||||||
#include "core/defs.h"
|
|
||||||
|
|
||||||
class ShadowSocksConfigurator : public ConfiguratorBase
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
ShadowSocksConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SHADOWSOCKS_CONFIGURATOR_H
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
#include "ssh_configurator.h"
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QObject>
|
|
||||||
#include <QProcess>
|
|
||||||
#include <QString>
|
|
||||||
#include <QTemporaryDir>
|
|
||||||
#include <QTemporaryFile>
|
|
||||||
#include <QThread>
|
|
||||||
#include <qtimer.h>
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
|
||||||
#include <QGuiApplication>
|
|
||||||
#else
|
|
||||||
#include <QApplication>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "core/server_defs.h"
|
|
||||||
#include "utilities.h"
|
|
||||||
|
|
||||||
SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
|
||||||
: ConfiguratorBase(settings, serverController, parent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString SshConfigurator::convertOpenSShKey(const QString &key)
|
|
||||||
{
|
|
||||||
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
|
||||||
QProcess p;
|
|
||||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
|
||||||
|
|
||||||
QTemporaryFile tmp;
|
|
||||||
#ifdef QT_DEBUG
|
|
||||||
tmp.setAutoRemove(false);
|
|
||||||
#endif
|
|
||||||
tmp.open();
|
|
||||||
tmp.write(key.toUtf8());
|
|
||||||
tmp.close();
|
|
||||||
|
|
||||||
// ssh-keygen -p -P "" -N "" -m pem -f id_ssh
|
|
||||||
|
|
||||||
#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
|
|
||||||
p.setProgram("ssh-keygen");
|
|
||||||
p.setArguments(QStringList() << "-p"
|
|
||||||
<< "-P"
|
|
||||||
<< ""
|
|
||||||
<< "-N"
|
|
||||||
<< ""
|
|
||||||
<< "-m"
|
|
||||||
<< "pem"
|
|
||||||
<< "-f" << tmp.fileName());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
p.start();
|
|
||||||
p.waitForFinished();
|
|
||||||
|
|
||||||
qDebug().noquote() << "OpenVpnConfigurator::convertOpenSShKey" << p.exitCode() << p.exitStatus() << p.readAll();
|
|
||||||
|
|
||||||
tmp.open();
|
|
||||||
|
|
||||||
return tmp.readAll();
|
|
||||||
#else
|
|
||||||
return key;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// DEAD CODE.
|
|
||||||
void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
|
|
||||||
{
|
|
||||||
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
|
||||||
QProcess *p = new QProcess();
|
|
||||||
p->setProcessChannelMode(QProcess::SeparateChannels);
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
p->setProcessEnvironment(prepareEnv());
|
|
||||||
p->setProgram(qApp->applicationDirPath() + "\\cygwin\\putty.exe");
|
|
||||||
|
|
||||||
if (credentials.secretData.contains("PRIVATE KEY")) {
|
|
||||||
// todo: connect by key
|
|
||||||
// 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->setProgram("/bin/bash");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
p->startDetached();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
QProcessEnvironment SshConfigurator::prepareEnv()
|
|
||||||
{
|
|
||||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
|
||||||
QString pathEnvVar = env.value("PATH");
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
pathEnvVar.clear();
|
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
|
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;");
|
|
||||||
#elif defined(Q_OS_MACX) && !defined(MACOS_NE)
|
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
env.insert("PATH", pathEnvVar);
|
|
||||||
// qDebug().noquote() << "ENV PATH" << pathEnvVar;
|
|
||||||
return env;
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#ifndef SSH_CONFIGURATOR_H
|
|
||||||
#define SSH_CONFIGURATOR_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QProcessEnvironment>
|
|
||||||
|
|
||||||
#include "configurator_base.h"
|
|
||||||
#include "core/defs.h"
|
|
||||||
|
|
||||||
class SshConfigurator : ConfiguratorBase
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
SshConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
QProcessEnvironment prepareEnv();
|
|
||||||
QString convertOpenSShKey(const QString &key);
|
|
||||||
void openSshTerminal(const ServerCredentials &credentials);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SSH_CONFIGURATOR_H
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
#ifndef WIREGUARD_CONFIGURATOR_H
|
|
||||||
#define WIREGUARD_CONFIGURATOR_H
|
|
||||||
|
|
||||||
#include <QHostAddress>
|
|
||||||
#include <QObject>
|
|
||||||
#include <QProcessEnvironment>
|
|
||||||
|
|
||||||
#include "configurator_base.h"
|
|
||||||
#include "core/defs.h"
|
|
||||||
#include "core/scripts_registry.h"
|
|
||||||
|
|
||||||
class WireguardConfigurator : public ConfiguratorBase
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
|
||||||
bool isAwg, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
struct ConnectionData
|
|
||||||
{
|
|
||||||
QString clientPrivKey; // client private key
|
|
||||||
QString clientPubKey; // client public key
|
|
||||||
QString clientIP; // internal client IP address
|
|
||||||
QString serverPubKey; // tls-auth key
|
|
||||||
QString pskKey; // preshared key
|
|
||||||
QString host; // host ip
|
|
||||||
QString port;
|
|
||||||
};
|
|
||||||
|
|
||||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
|
||||||
|
|
||||||
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
|
||||||
QString &protocolConfigString);
|
|
||||||
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
|
||||||
QString &protocolConfigString);
|
|
||||||
|
|
||||||
static ConnectionData genClientKeys();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QList<QHostAddress> getIpsFromConf(const QString &input);
|
|
||||||
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
|
||||||
|
|
||||||
bool m_isAwg;
|
|
||||||
QString m_serverConfigPath;
|
|
||||||
QString m_serverPublicKeyPath;
|
|
||||||
QString m_serverPskKeyPath;
|
|
||||||
amnezia::ProtocolScriptType m_configTemplate;
|
|
||||||
QString m_protocolName;
|
|
||||||
QString m_defaultPort;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // WIREGUARD_CONFIGURATOR_H
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#ifndef XRAY_CONFIGURATOR_H
|
|
||||||
#define XRAY_CONFIGURATOR_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include "configurator_base.h"
|
|
||||||
#include "core/defs.h"
|
|
||||||
|
|
||||||
class XrayConfigurator : public ConfiguratorBase
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
XrayConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
|
||||||
ErrorCode &errorCode);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString prepareServerConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
|
||||||
ErrorCode &errorCode);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // XRAY_CONFIGURATOR_H
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
#ifndef CONTAINERS_DEFS_H
|
|
||||||
#define CONTAINERS_DEFS_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QQmlEngine>
|
|
||||||
|
|
||||||
#include "../protocols/protocols_defs.h"
|
|
||||||
|
|
||||||
using namespace amnezia;
|
|
||||||
|
|
||||||
namespace amnezia
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace ContainerEnumNS
|
|
||||||
{
|
|
||||||
Q_NAMESPACE
|
|
||||||
enum DockerContainer {
|
|
||||||
None = 0,
|
|
||||||
Awg,
|
|
||||||
Awg2,
|
|
||||||
WireGuard,
|
|
||||||
OpenVpn,
|
|
||||||
Cloak,
|
|
||||||
ShadowSocks,
|
|
||||||
Ipsec,
|
|
||||||
Xray,
|
|
||||||
SSXray,
|
|
||||||
|
|
||||||
// non-vpn
|
|
||||||
TorWebSite,
|
|
||||||
Dns,
|
|
||||||
Sftp,
|
|
||||||
Socks5Proxy
|
|
||||||
};
|
|
||||||
Q_ENUM_NS(DockerContainer)
|
|
||||||
} // namespace ContainerEnumNS
|
|
||||||
|
|
||||||
using namespace ContainerEnumNS;
|
|
||||||
using namespace ProtocolEnumNS;
|
|
||||||
|
|
||||||
class ContainerProps : public QObject
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
Q_INVOKABLE static QString containerTypeToProtocolString(amnezia::DockerContainer c);
|
|
||||||
|
|
||||||
Q_INVOKABLE static QList<amnezia::DockerContainer> allContainers();
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// 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 bool isAwgContainer(amnezia::DockerContainer container);
|
|
||||||
|
|
||||||
|
|
||||||
static QJsonObject getProtocolConfigFromContainer(const amnezia::Proto protocol, const QJsonObject &containerConfig);
|
|
||||||
|
|
||||||
static int installPageOrder(amnezia::DockerContainer container);
|
|
||||||
};
|
|
||||||
|
|
||||||
static void declareQmlContainerEnum()
|
|
||||||
{
|
|
||||||
qmlRegisterUncreatableMetaObject(ContainerEnumNS::staticMetaObject, "ContainerEnum", 1, 0, "ContainerEnum",
|
|
||||||
"Error: only enums");
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace amnezia
|
|
||||||
|
|
||||||
QDebug operator<<(QDebug debug, const amnezia::DockerContainer &c);
|
|
||||||
|
|
||||||
#endif // CONTAINERS_DEFS_H
|
|
||||||
109
client/core/configurators/awgConfigurator.cpp
Normal file
109
client/core/configurators/awgConfigurator.cpp
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#include "awgConfigurator.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/models/containerConfig.h"
|
||||||
|
#include "core/models/protocols/awgProtocolConfig.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
|
||||||
|
AwgConfigurator::AwgConfigurator(SshSession* sshSession, QObject *parent)
|
||||||
|
: WireguardConfigurator(sshSession, true, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolConfig AwgConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &containerConfig,
|
||||||
|
const DnsSettings &dnsSettings,
|
||||||
|
ErrorCode &errorCode)
|
||||||
|
{
|
||||||
|
const AwgServerConfig* serverConfig = nullptr;
|
||||||
|
const AwgClientConfig* clientConfig = nullptr;
|
||||||
|
|
||||||
|
if (auto* awgProtocolConfig = containerConfig.getAwgProtocolConfig()) {
|
||||||
|
serverConfig = &awgProtocolConfig->serverConfig;
|
||||||
|
if (awgProtocolConfig->clientConfig.has_value()) {
|
||||||
|
clientConfig = &awgProtocolConfig->clientConfig.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolConfig wireguardConfig = WireguardConfigurator::createConfig(credentials, container, containerConfig, dnsSettings, errorCode);
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
return AwgProtocolConfig{};
|
||||||
|
}
|
||||||
|
|
||||||
|
WireGuardProtocolConfig* wgConfig = wireguardConfig.as<WireGuardProtocolConfig>();
|
||||||
|
if (!wgConfig || !wgConfig->clientConfig.has_value()) {
|
||||||
|
errorCode = ErrorCode::InternalError;
|
||||||
|
return AwgProtocolConfig{};
|
||||||
|
}
|
||||||
|
|
||||||
|
QString awgConfig = wgConfig->clientConfig->nativeConfig;
|
||||||
|
|
||||||
|
QMap<QString, QString> configMap;
|
||||||
|
auto configLines = awgConfig.split("\n");
|
||||||
|
for (auto &line : configLines) {
|
||||||
|
auto trimmedLine = line.trimmed();
|
||||||
|
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
QStringList parts = trimmedLine.split(" = ");
|
||||||
|
if (parts.count() == 2) {
|
||||||
|
configMap.insert(parts[0].trimmed(), parts[1].trimmed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AwgProtocolConfig protocolConfig;
|
||||||
|
if (serverConfig) {
|
||||||
|
protocolConfig.serverConfig = *serverConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
AwgClientConfig newClientConfig;
|
||||||
|
newClientConfig.nativeConfig = awgConfig;
|
||||||
|
newClientConfig.hostName = wgConfig->clientConfig->hostName;
|
||||||
|
newClientConfig.port = wgConfig->clientConfig->port;
|
||||||
|
newClientConfig.clientIp = wgConfig->clientConfig->clientIp;
|
||||||
|
newClientConfig.clientPrivateKey = wgConfig->clientConfig->clientPrivateKey;
|
||||||
|
newClientConfig.clientPublicKey = wgConfig->clientConfig->clientPublicKey;
|
||||||
|
newClientConfig.serverPublicKey = wgConfig->clientConfig->serverPublicKey;
|
||||||
|
newClientConfig.presharedKey = wgConfig->clientConfig->presharedKey;
|
||||||
|
newClientConfig.clientId = wgConfig->clientConfig->clientId;
|
||||||
|
newClientConfig.allowedIps = wgConfig->clientConfig->allowedIps;
|
||||||
|
newClientConfig.persistentKeepAlive = wgConfig->clientConfig->persistentKeepAlive;
|
||||||
|
|
||||||
|
QString mtu = protocols::awg::defaultMtu;
|
||||||
|
if (clientConfig && !clientConfig->mtu.isEmpty()) {
|
||||||
|
mtu = clientConfig->mtu;
|
||||||
|
}
|
||||||
|
newClientConfig.mtu = mtu;
|
||||||
|
|
||||||
|
newClientConfig.junkPacketCount = configMap.value(configKey::junkPacketCount);
|
||||||
|
newClientConfig.junkPacketMinSize = configMap.value(configKey::junkPacketMinSize);
|
||||||
|
newClientConfig.junkPacketMaxSize = configMap.value(configKey::junkPacketMaxSize);
|
||||||
|
newClientConfig.initPacketJunkSize = configMap.value(configKey::initPacketJunkSize);
|
||||||
|
newClientConfig.responsePacketJunkSize = configMap.value(configKey::responsePacketJunkSize);
|
||||||
|
newClientConfig.initPacketMagicHeader = configMap.value(configKey::initPacketMagicHeader);
|
||||||
|
newClientConfig.responsePacketMagicHeader = configMap.value(configKey::responsePacketMagicHeader);
|
||||||
|
newClientConfig.underloadPacketMagicHeader = configMap.value(configKey::underloadPacketMagicHeader);
|
||||||
|
newClientConfig.transportPacketMagicHeader = configMap.value(configKey::transportPacketMagicHeader);
|
||||||
|
newClientConfig.specialJunk1 = configMap.value(configKey::specialJunk1);
|
||||||
|
newClientConfig.specialJunk2 = configMap.value(configKey::specialJunk2);
|
||||||
|
newClientConfig.specialJunk3 = configMap.value(configKey::specialJunk3);
|
||||||
|
newClientConfig.specialJunk4 = configMap.value(configKey::specialJunk4);
|
||||||
|
newClientConfig.specialJunk5 = configMap.value(configKey::specialJunk5);
|
||||||
|
|
||||||
|
if (container == DockerContainer::Awg2) {
|
||||||
|
newClientConfig.cookieReplyPacketJunkSize = configMap.value(configKey::cookieReplyPacketJunkSize);
|
||||||
|
newClientConfig.transportPacketJunkSize = configMap.value(configKey::transportPacketJunkSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
newClientConfig.isObfuscationEnabled = false;
|
||||||
|
|
||||||
|
protocolConfig.setClientConfig(newClientConfig);
|
||||||
|
|
||||||
|
return protocolConfig;
|
||||||
|
}
|
||||||
20
client/core/configurators/awgConfigurator.h
Normal file
20
client/core/configurators/awgConfigurator.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef AWGCONFIGURATOR_H
|
||||||
|
#define AWGCONFIGURATOR_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "wireguardConfigurator.h"
|
||||||
|
|
||||||
|
class AwgConfigurator : public WireguardConfigurator
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
AwgConfigurator(SshSession* sshSession, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
amnezia::ProtocolConfig createConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container,
|
||||||
|
const amnezia::ContainerConfig &containerConfig,
|
||||||
|
const amnezia::DnsSettings &dnsSettings,
|
||||||
|
amnezia::ErrorCode &errorCode) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // AWGCONFIGURATOR_H
|
||||||
50
client/core/configurators/configuratorBase.cpp
Normal file
50
client/core/configurators/configuratorBase.cpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#include "configuratorBase.h"
|
||||||
|
|
||||||
|
#include "core/configurators/awgConfigurator.h"
|
||||||
|
#include "core/configurators/ikev2Configurator.h"
|
||||||
|
#include "core/configurators/openVpnConfigurator.h"
|
||||||
|
#include "core/configurators/wireguardConfigurator.h"
|
||||||
|
#include "core/configurators/xrayConfigurator.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
|
||||||
|
ConfiguratorBase::ConfiguratorBase(SshSession* sshSession, QObject *parent)
|
||||||
|
: QObject { parent }, m_sshSession(sshSession)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QScopedPointer<ConfiguratorBase> ConfiguratorBase::create(Proto protocol,
|
||||||
|
SshSession* sshSession)
|
||||||
|
{
|
||||||
|
switch (protocol) {
|
||||||
|
case Proto::OpenVpn: return QScopedPointer<ConfiguratorBase>(new OpenVpnConfigurator(sshSession));
|
||||||
|
case Proto::WireGuard: return QScopedPointer<ConfiguratorBase>(new WireguardConfigurator(sshSession, false));
|
||||||
|
case Proto::Awg: return QScopedPointer<ConfiguratorBase>(new AwgConfigurator(sshSession));
|
||||||
|
case Proto::Ikev2: return QScopedPointer<ConfiguratorBase>(new Ikev2Configurator(sshSession));
|
||||||
|
case Proto::Xray: return QScopedPointer<ConfiguratorBase>(new XrayConfigurator(sshSession));
|
||||||
|
case Proto::SSXray: return QScopedPointer<ConfiguratorBase>(new XrayConfigurator(sshSession));
|
||||||
|
default: return QScopedPointer<ConfiguratorBase>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolConfig ConfiguratorBase::processConfigWithLocalSettings(const ConnectionSettings &settings,
|
||||||
|
ProtocolConfig protocolConfig)
|
||||||
|
{
|
||||||
|
applyDnsToNativeConfig(settings.dns, protocolConfig);
|
||||||
|
return protocolConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolConfig ConfiguratorBase::processConfigWithExportSettings(const ExportSettings &settings,
|
||||||
|
ProtocolConfig protocolConfig)
|
||||||
|
{
|
||||||
|
applyDnsToNativeConfig(settings.dns, protocolConfig);
|
||||||
|
return protocolConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfiguratorBase::applyDnsToNativeConfig(const DnsSettings &dns, ProtocolConfig &protocolConfig)
|
||||||
|
{
|
||||||
|
QString config = protocolConfig.nativeConfig();
|
||||||
|
config.replace("$PRIMARY_DNS", dns.primaryDns);
|
||||||
|
config.replace("$SECONDARY_DNS", dns.secondaryDns);
|
||||||
|
protocolConfig.setNativeConfig(config);
|
||||||
|
}
|
||||||
43
client/core/configurators/configuratorBase.h
Normal file
43
client/core/configurators/configuratorBase.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#ifndef CONFIGURATORBASE_H
|
||||||
|
#define CONFIGURATORBASE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
#include "core/models/containerConfig.h"
|
||||||
|
#include "core/models/protocolConfig.h"
|
||||||
|
|
||||||
|
class SshSession;
|
||||||
|
|
||||||
|
class ConfiguratorBase : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ConfiguratorBase(SshSession* sshSession, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
static QScopedPointer<ConfiguratorBase> create(amnezia::Proto protocol,
|
||||||
|
SshSession* sshSession);
|
||||||
|
|
||||||
|
virtual amnezia::ProtocolConfig createConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container,
|
||||||
|
const amnezia::ContainerConfig &containerConfig,
|
||||||
|
const amnezia::DnsSettings &dnsSettings,
|
||||||
|
amnezia::ErrorCode &errorCode) = 0;
|
||||||
|
|
||||||
|
virtual amnezia::ProtocolConfig processConfigWithLocalSettings(const amnezia::ConnectionSettings &settings,
|
||||||
|
amnezia::ProtocolConfig protocolConfig);
|
||||||
|
virtual amnezia::ProtocolConfig processConfigWithExportSettings(const amnezia::ExportSettings &settings,
|
||||||
|
amnezia::ProtocolConfig protocolConfig);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void applyDnsToNativeConfig(const amnezia::DnsSettings &dns, amnezia::ProtocolConfig &protocolConfig);
|
||||||
|
|
||||||
|
SshSession* m_sshSession;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CONFIGURATORBASE_H
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "ikev2_configurator.h"
|
#include "ikev2Configurator.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
@@ -8,14 +8,16 @@
|
|||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
#include "core/utils/containerEnum.h"
|
||||||
#include "core/controllers/serverController.h"
|
#include "core/utils/containers/containerUtils.h"
|
||||||
#include "core/scripts_registry.h"
|
#include "core/utils/protocolEnum.h"
|
||||||
#include "core/server_defs.h"
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
#include "utilities.h"
|
#include "core/utils/selfhosted/scriptsRegistry.h"
|
||||||
|
#include "core/utils/utilities.h"
|
||||||
|
#include "core/models/protocols/ikev2ProtocolConfig.h"
|
||||||
|
|
||||||
Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
Ikev2Configurator::Ikev2Configurator(SshSession* sshSession, QObject *parent)
|
||||||
: ConfiguratorBase(settings, serverController, parent)
|
: ConfiguratorBase(sshSession, parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +27,6 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
|
|||||||
Ikev2Configurator::ConnectionData connData;
|
Ikev2Configurator::ConnectionData connData;
|
||||||
connData.host = credentials.hostName;
|
connData.host = credentials.hostName;
|
||||||
connData.clientId = Utils::getRandomString(16);
|
connData.clientId = Utils::getRandomString(16);
|
||||||
connData.password = Utils::getRandomString(16);
|
|
||||||
connData.password = "";
|
connData.password = "";
|
||||||
|
|
||||||
QString certFileName = "/opt/amnezia/ikev2/clients/" + connData.clientId + ".p12";
|
QString certFileName = "/opt/amnezia/ikev2/clients/" + connData.clientId + ".p12";
|
||||||
@@ -39,14 +40,14 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
|
|||||||
"--extKeyUsage serverAuth,clientAuth -8 \"%1\"")
|
"--extKeyUsage serverAuth,clientAuth -8 \"%1\"")
|
||||||
.arg(connData.clientId);
|
.arg(connData.clientId);
|
||||||
|
|
||||||
errorCode = m_serverController->runContainerScript(credentials, container, scriptCreateCert);
|
errorCode = m_sshSession->runContainerScript(credentials, container, scriptCreateCert);
|
||||||
|
|
||||||
QString scriptExportCert =
|
QString scriptExportCert =
|
||||||
QString("pk12util -W \"%1\" -d sql:/etc/ipsec.d -n \"%2\" -o \"%3\"").arg(connData.password).arg(connData.clientId).arg(certFileName);
|
QString("pk12util -W \"%1\" -d sql:/etc/ipsec.d -n \"%2\" -o \"%3\"").arg(connData.password).arg(connData.clientId).arg(certFileName);
|
||||||
errorCode = m_serverController->runContainerScript(credentials, container, scriptExportCert);
|
errorCode = m_sshSession->runContainerScript(credentials, container, scriptExportCert);
|
||||||
|
|
||||||
connData.clientCert = m_serverController->getTextFileFromContainer(container, credentials, certFileName, errorCode);
|
connData.clientCert = m_sshSession->getTextFileFromContainer(container, credentials, certFileName, errorCode);
|
||||||
connData.caCert = m_serverController->getTextFileFromContainer(container, credentials, "/etc/ipsec.d/ca_cert_base64.p12", errorCode);
|
connData.caCert = m_sshSession->getTextFileFromContainer(container, credentials, "/etc/ipsec.d/ca_cert_base64.p12", errorCode);
|
||||||
|
|
||||||
qDebug() << "Ikev2Configurator::ConnectionData client cert size:" << connData.clientCert.size();
|
qDebug() << "Ikev2Configurator::ConnectionData client cert size:" << connData.clientCert.size();
|
||||||
qDebug() << "Ikev2Configurator::ConnectionData ca cert size:" << connData.caCert.size();
|
qDebug() << "Ikev2Configurator::ConnectionData ca cert size:" << connData.caCert.size();
|
||||||
@@ -54,26 +55,51 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
|
|||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Ikev2Configurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
ProtocolConfig Ikev2Configurator::createConfig(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &containerConfig,
|
||||||
ErrorCode &errorCode)
|
const DnsSettings &dnsSettings,
|
||||||
|
ErrorCode &errorCode)
|
||||||
{
|
{
|
||||||
Q_UNUSED(containerConfig)
|
const Ikev2ServerConfig* serverConfig = nullptr;
|
||||||
|
if (auto* ikev2Config = containerConfig.protocolConfig.as<Ikev2ProtocolConfig>()) {
|
||||||
|
serverConfig = &ikev2Config->serverConfig;
|
||||||
|
}
|
||||||
|
|
||||||
ConnectionData connData = prepareIkev2Config(credentials, container, errorCode);
|
ConnectionData connData = prepareIkev2Config(credentials, container, errorCode);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return "";
|
return Ikev2ProtocolConfig{};
|
||||||
}
|
}
|
||||||
|
|
||||||
return genIkev2Config(connData);
|
QString configJson = genIkev2Config(connData);
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(configJson.toUtf8());
|
||||||
|
QJsonObject configObj = doc.object();
|
||||||
|
|
||||||
|
Ikev2ProtocolConfig protocolConfig;
|
||||||
|
if (serverConfig) {
|
||||||
|
protocolConfig.serverConfig = *serverConfig;
|
||||||
|
} else {
|
||||||
|
protocolConfig.serverConfig.hostName = connData.host;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ikev2ClientConfig clientConfig;
|
||||||
|
clientConfig.nativeConfig = configJson;
|
||||||
|
clientConfig.hostName = connData.host;
|
||||||
|
clientConfig.userName = connData.clientId;
|
||||||
|
clientConfig.cert = QString(connData.clientCert.toBase64());
|
||||||
|
clientConfig.password = connData.password;
|
||||||
|
clientConfig.clientId = connData.clientId;
|
||||||
|
|
||||||
|
protocolConfig.setClientConfig(clientConfig);
|
||||||
|
|
||||||
|
return protocolConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Ikev2Configurator::genIkev2Config(const ConnectionData &connData)
|
QString Ikev2Configurator::genIkev2Config(const ConnectionData &connData)
|
||||||
{
|
{
|
||||||
QJsonObject config;
|
QJsonObject config;
|
||||||
config[config_key::hostName] = connData.host;
|
config[configKey::hostName] = connData.host;
|
||||||
config[config_key::userName] = connData.clientId;
|
config[configKey::userName] = connData.clientId;
|
||||||
config[config_key::cert] = QString(connData.clientCert.toBase64());
|
config[configKey::cert] = QString(connData.clientCert.toBase64());
|
||||||
config[config_key::password] = connData.password;
|
config[configKey::password] = connData.password;
|
||||||
|
|
||||||
return QJsonDocument(config).toJson();
|
return QJsonDocument(config).toJson();
|
||||||
}
|
}
|
||||||
39
client/core/configurators/ikev2Configurator.h
Normal file
39
client/core/configurators/ikev2Configurator.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#ifndef IKEV2_CONFIGURATOR_H
|
||||||
|
#define IKEV2_CONFIGURATOR_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QProcessEnvironment>
|
||||||
|
|
||||||
|
#include "configuratorBase.h"
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
|
||||||
|
class Ikev2Configurator : public ConfiguratorBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Ikev2Configurator(SshSession* sshSession, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
struct ConnectionData {
|
||||||
|
QByteArray clientCert; // p12 client cert
|
||||||
|
QByteArray caCert; // p12 server cert
|
||||||
|
QString clientId;
|
||||||
|
QString password; // certificate password
|
||||||
|
QString host; // host ip
|
||||||
|
};
|
||||||
|
|
||||||
|
amnezia::ProtocolConfig createConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container,
|
||||||
|
const amnezia::ContainerConfig &containerConfig,
|
||||||
|
const amnezia::DnsSettings &dnsSettings,
|
||||||
|
amnezia::ErrorCode &errorCode) override;
|
||||||
|
|
||||||
|
QString genIkev2Config(const ConnectionData &connData);
|
||||||
|
QString genMobileConfig(const ConnectionData &connData);
|
||||||
|
QString genStrongSwanConfig(const ConnectionData &connData);
|
||||||
|
|
||||||
|
ConnectionData prepareIkev2Config(const amnezia::ServerCredentials &credentials,
|
||||||
|
amnezia::DockerContainer container, amnezia::ErrorCode &errorCode);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // IKEV2_CONFIGURATOR_H
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
#include "openvpn_configurator.h"
|
#include "openVpnConfigurator.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QTemporaryDir>
|
#include <QTemporaryDir>
|
||||||
@@ -13,26 +14,34 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "core/networkUtilities.h"
|
#include "core/utils/errorCodes.h"
|
||||||
#include "containers/containers_defs.h"
|
#include "core/utils/routeModes.h"
|
||||||
#include "core/controllers/serverController.h"
|
#include "core/utils/commonStructs.h"
|
||||||
#include "core/scripts_registry.h"
|
#include "core/utils/networkUtilities.h"
|
||||||
#include "settings.h"
|
#include "core/utils/containerEnum.h"
|
||||||
#include "utilities.h"
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
|
#include "core/utils/selfhosted/scriptsRegistry.h"
|
||||||
|
#include "core/utils/utilities.h"
|
||||||
|
#include "core/models/protocols/openVpnProtocolConfig.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
|
||||||
#include <openssl/pem.h>
|
#include <openssl/pem.h>
|
||||||
#include <openssl/rsa.h>
|
#include <openssl/rsa.h>
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
|
|
||||||
|
|
||||||
OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
OpenVpnConfigurator::OpenVpnConfigurator(SshSession* sshSession, QObject *parent)
|
||||||
QObject *parent)
|
: ConfiguratorBase(sshSession, parent)
|
||||||
: ConfiguratorBase(settings, serverController, parent)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(const ServerCredentials &credentials,
|
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(const ServerCredentials &credentials,
|
||||||
DockerContainer container, ErrorCode &errorCode)
|
DockerContainer container,
|
||||||
|
const DnsSettings &dnsSettings,
|
||||||
|
ErrorCode &errorCode)
|
||||||
{
|
{
|
||||||
OpenVpnConfigurator::ConnectionData connData = OpenVpnConfigurator::createCertRequest();
|
OpenVpnConfigurator::ConnectionData connData = OpenVpnConfigurator::createCertRequest();
|
||||||
connData.host = credentials.hostName;
|
connData.host = credentials.hostName;
|
||||||
@@ -44,26 +53,26 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
|
|||||||
|
|
||||||
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);
|
||||||
|
|
||||||
errorCode = m_serverController->uploadTextFileToContainer(container, credentials, connData.request, reqFileName);
|
errorCode = m_sshSession->uploadTextFileToContainer(container, credentials, connData.request, reqFileName);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
|
||||||
errorCode = signCert(container, credentials, connData.clientId);
|
errorCode = signCert(container, credentials, dnsSettings, connData.clientId);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
|
||||||
connData.caCert =
|
connData.caCert =
|
||||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::caCertPath, errorCode);
|
m_sshSession->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::caCertPath, errorCode);
|
||||||
connData.clientCert = m_serverController->getTextFileFromContainer(
|
connData.clientCert = m_sshSession->getTextFileFromContainer(
|
||||||
container, credentials, QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath).arg(connData.clientId), errorCode);
|
container, credentials, QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath).arg(connData.clientId), errorCode);
|
||||||
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
|
||||||
connData.taKey = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::taKeyPath, errorCode);
|
connData.taKey = m_sshSession->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::taKeyPath, errorCode);
|
||||||
|
|
||||||
if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) {
|
if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) {
|
||||||
errorCode = ErrorCode::SshScpFailureError;
|
errorCode = ErrorCode::SshScpFailureError;
|
||||||
@@ -72,15 +81,23 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
|
|||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString OpenVpnConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
ProtocolConfig OpenVpnConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
const ContainerConfig &containerConfig,
|
||||||
|
const DnsSettings &dnsSettings,
|
||||||
|
ErrorCode &errorCode)
|
||||||
{
|
{
|
||||||
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container),
|
const OpenVpnServerConfig* serverConfig = nullptr;
|
||||||
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
if (auto* openVpnProtocolConfig = containerConfig.getOpenVpnProtocolConfig()) {
|
||||||
|
serverConfig = &openVpnProtocolConfig->serverConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
amnezia::ScriptVars vars = amnezia::genBaseVars(credentials, container, dnsSettings.primaryDns, dnsSettings.secondaryDns);
|
||||||
|
vars.append(amnezia::genProtocolVarsForContainer(container, containerConfig));
|
||||||
|
QString config = m_sshSession->replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container), vars);
|
||||||
|
|
||||||
ConnectionData connData = prepareOpenVpnConfig(credentials, container, errorCode);
|
ConnectionData connData = prepareOpenVpnConfig(credentials, container, dnsSettings, errorCode);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return "";
|
return OpenVpnProtocolConfig{};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sanitizeStaticKey = [](const QString &key) {
|
auto sanitizeStaticKey = [](const QString &key) {
|
||||||
@@ -116,42 +133,45 @@ QString OpenVpnConfigurator::createConfig(const ServerCredentials &credentials,
|
|||||||
config.replace("block-outside-dns", "");
|
config.replace("block-outside-dns", "");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QJsonObject jConfig;
|
OpenVpnProtocolConfig protocolConfig;
|
||||||
jConfig[config_key::config] = config;
|
if (serverConfig) {
|
||||||
|
protocolConfig.serverConfig = *serverConfig;
|
||||||
jConfig[config_key::clientId] = connData.clientId;
|
}
|
||||||
|
|
||||||
return QJsonDocument(jConfig).toJson();
|
OpenVpnClientConfig clientConfig;
|
||||||
|
clientConfig.nativeConfig = config;
|
||||||
|
clientConfig.clientId = connData.clientId;
|
||||||
|
clientConfig.blockOutsideDns = false;
|
||||||
|
|
||||||
|
protocolConfig.setClientConfig(clientConfig);
|
||||||
|
|
||||||
|
return protocolConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
ProtocolConfig OpenVpnConfigurator::processConfigWithLocalSettings(const ConnectionSettings &settings,
|
||||||
QString &protocolConfigString)
|
ProtocolConfig protocolConfig)
|
||||||
{
|
{
|
||||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
applyDnsToNativeConfig(settings.dns, protocolConfig);
|
||||||
|
|
||||||
QJsonObject json = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
QString config = protocolConfig.nativeConfig();
|
||||||
QString config = json[config_key::config].toString();
|
|
||||||
|
|
||||||
if (!isApiConfig) {
|
if (!settings.isApiConfig) {
|
||||||
QRegularExpression regex("redirect-gateway.*");
|
QRegularExpression regex("redirect-gateway.*");
|
||||||
config.replace(regex, "");
|
config.replace(regex, "");
|
||||||
|
|
||||||
// We don't use secondary DNS if primary DNS is AmneziaDNS
|
if (settings.dns.primaryDns.contains(protocols::dns::amneziaDnsIp)) {
|
||||||
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
|
QRegularExpression dnsRegex("dhcp-option DNS " + settings.dns.secondaryDns);
|
||||||
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
|
|
||||||
config.replace(dnsRegex, "");
|
config.replace(dnsRegex, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_settings->isSitesSplitTunnelingEnabled()) {
|
if (!settings.splitTunneling.isSitesSplitTunnelingEnabled) {
|
||||||
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
||||||
config.append("block-ipv6\n");
|
config.append("block-ipv6\n");
|
||||||
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
} else if (settings.splitTunneling.routeMode == RouteMode::VpnOnlyForwardSites) {
|
||||||
|
// no redirect-gateway
|
||||||
// no redirect-gateway
|
} else if (settings.splitTunneling.routeMode == RouteMode::VpnAllExceptSites) {
|
||||||
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
||||||
// Prevent ipv6 leak
|
|
||||||
#endif
|
#endif
|
||||||
config.append("block-ipv6\n");
|
config.append("block-ipv6\n");
|
||||||
}
|
}
|
||||||
@@ -162,64 +182,57 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (defined(MZ_MACOS) || defined(MZ_LINUX))
|
#if (defined(MZ_MACOS) || defined(MZ_LINUX))
|
||||||
QString dnsConf = QString("\nscript-security 2\n"
|
config.append(QString("\nscript-security 2\n"
|
||||||
"up %1/update-resolv-conf.sh\n"
|
"up %1/update-resolv-conf.sh\n"
|
||||||
"down %1/update-resolv-conf.sh\n")
|
"down %1/update-resolv-conf.sh\n")
|
||||||
.arg(qApp->applicationDirPath());
|
.arg(qApp->applicationDirPath()));
|
||||||
|
|
||||||
config.append(dnsConf);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
json[config_key::config] = config;
|
protocolConfig.setNativeConfig(config);
|
||||||
return QJsonDocument(json).toJson();
|
return protocolConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString OpenVpnConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
ProtocolConfig OpenVpnConfigurator::processConfigWithExportSettings(const ExportSettings &settings,
|
||||||
QString &protocolConfigString)
|
ProtocolConfig protocolConfig)
|
||||||
{
|
{
|
||||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
applyDnsToNativeConfig(settings.dns, protocolConfig);
|
||||||
|
|
||||||
QJsonObject json = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
QString config = protocolConfig.nativeConfig();
|
||||||
QString config = json[config_key::config].toString();
|
|
||||||
|
|
||||||
QRegularExpression regex("redirect-gateway.*");
|
QRegularExpression regex("redirect-gateway.*");
|
||||||
config.replace(regex, "");
|
config.replace(regex, "");
|
||||||
|
|
||||||
// We don't use secondary DNS if primary DNS is AmneziaDNS
|
if (settings.dns.primaryDns.contains(protocols::dns::amneziaDnsIp)) {
|
||||||
if (dns.first.contains(protocols::dns::amneziaDnsIp)) {
|
QRegularExpression dnsRegex("dhcp-option DNS " + settings.dns.secondaryDns);
|
||||||
QRegularExpression dnsRegex("dhcp-option DNS " + dns.second);
|
|
||||||
config.replace(dnsRegex, "");
|
config.replace(dnsRegex, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
||||||
|
|
||||||
// Prevent ipv6 leak
|
|
||||||
config.append("block-ipv6\n");
|
config.append("block-ipv6\n");
|
||||||
|
|
||||||
// remove block-outside-dns for all exported configs
|
|
||||||
config.replace("block-outside-dns", "");
|
config.replace("block-outside-dns", "");
|
||||||
|
|
||||||
json[config_key::config] = config;
|
protocolConfig.setNativeConfig(config);
|
||||||
return QJsonDocument(json).toJson();
|
return protocolConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId)
|
ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
const DnsSettings &dnsSettings, QString clientId)
|
||||||
{
|
{
|
||||||
QString script_import = QString("sudo docker exec -i %1 bash -c \"cd /opt/amnezia/openvpn && "
|
QString script_import = QString("sudo docker exec -i %1 bash -c \"cd /opt/amnezia/openvpn && "
|
||||||
"easyrsa import-req %2/%3.req %3\"")
|
"easyrsa import-req %2/%3.req %3\"")
|
||||||
.arg(ContainerProps::containerToString(container))
|
.arg(ContainerUtils::containerToString(container))
|
||||||
.arg(amnezia::protocols::openvpn::clientsDirPath)
|
.arg(amnezia::protocols::openvpn::clientsDirPath)
|
||||||
.arg(clientId);
|
.arg(clientId);
|
||||||
|
|
||||||
QString script_sign = QString("sudo docker exec -i %1 bash -c \"export EASYRSA_BATCH=1; cd /opt/amnezia/openvpn && "
|
QString script_sign = QString("sudo docker exec -i %1 bash -c \"export EASYRSA_BATCH=1; cd /opt/amnezia/openvpn && "
|
||||||
"easyrsa sign-req client %2\"")
|
"easyrsa sign-req client %2\"")
|
||||||
.arg(ContainerProps::containerToString(container))
|
.arg(ContainerUtils::containerToString(container))
|
||||||
.arg(clientId);
|
.arg(clientId);
|
||||||
|
|
||||||
QStringList scriptList { script_import, script_sign };
|
QStringList scriptList { script_import, script_sign };
|
||||||
QString script = m_serverController->replaceVars(scriptList.join("\n"), m_serverController->genVarsForScript(credentials, container));
|
QString script = m_sshSession->replaceVars(scriptList.join("\n"), amnezia::genBaseVars(credentials, container, dnsSettings.primaryDns, dnsSettings.secondaryDns));
|
||||||
|
|
||||||
return m_serverController->runScript(credentials, script);
|
return m_sshSession->runScript(credentials, script);
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
|
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
|
||||||
49
client/core/configurators/openVpnConfigurator.h
Normal file
49
client/core/configurators/openVpnConfigurator.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#ifndef OPENVPN_CONFIGURATOR_H
|
||||||
|
#define OPENVPN_CONFIGURATOR_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QProcessEnvironment>
|
||||||
|
|
||||||
|
#include "configuratorBase.h"
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
|
||||||
|
class OpenVpnConfigurator : public ConfiguratorBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
OpenVpnConfigurator(SshSession* sshSession, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
struct ConnectionData
|
||||||
|
{
|
||||||
|
QString clientId;
|
||||||
|
QString request; // certificate request
|
||||||
|
QString privKey; // client private key
|
||||||
|
QString clientCert; // client signed certificate
|
||||||
|
QString caCert; // server certificate
|
||||||
|
QString taKey; // tls-auth key
|
||||||
|
QString host; // host ip
|
||||||
|
};
|
||||||
|
|
||||||
|
amnezia::ProtocolConfig createConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container,
|
||||||
|
const amnezia::ContainerConfig &containerConfig,
|
||||||
|
const amnezia::DnsSettings &dnsSettings,
|
||||||
|
amnezia::ErrorCode &errorCode) override;
|
||||||
|
|
||||||
|
amnezia::ProtocolConfig processConfigWithLocalSettings(const amnezia::ConnectionSettings &settings,
|
||||||
|
amnezia::ProtocolConfig protocolConfig) override;
|
||||||
|
amnezia::ProtocolConfig processConfigWithExportSettings(const amnezia::ExportSettings &settings,
|
||||||
|
amnezia::ProtocolConfig protocolConfig) override;
|
||||||
|
|
||||||
|
static ConnectionData createCertRequest();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConnectionData prepareOpenVpnConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container,
|
||||||
|
const amnezia::DnsSettings &dnsSettings,
|
||||||
|
amnezia::ErrorCode &errorCode);
|
||||||
|
amnezia::ErrorCode signCert(amnezia::DockerContainer container, const amnezia::ServerCredentials &credentials,
|
||||||
|
const amnezia::DnsSettings &dnsSettings, QString clientId);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // OPENVPN_CONFIGURATOR_H
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "wireguard_configurator.h"
|
#include "wireguardConfigurator.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
@@ -13,17 +13,26 @@
|
|||||||
#include <openssl/rsa.h>
|
#include <openssl/rsa.h>
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
#include "core/utils/containerEnum.h"
|
||||||
#include "core/controllers/serverController.h"
|
#include "core/utils/containers/containerUtils.h"
|
||||||
#include "core/scripts_registry.h"
|
#include "core/utils/protocolEnum.h"
|
||||||
#include "core/server_defs.h"
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
#include "settings.h"
|
#include "core/utils/selfhosted/scriptsRegistry.h"
|
||||||
#include "utilities.h"
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/utils/utilities.h"
|
||||||
|
#include "core/models/containerConfig.h"
|
||||||
|
#include "core/models/protocols/wireGuardProtocolConfig.h"
|
||||||
|
#include "core/models/protocols/awgProtocolConfig.h"
|
||||||
|
#include <QJsonArray>
|
||||||
|
|
||||||
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings,
|
using namespace amnezia;
|
||||||
const QSharedPointer<ServerController> &serverController, bool isAwg,
|
|
||||||
|
WireguardConfigurator::WireguardConfigurator(SshSession* sshSession, bool isAwg,
|
||||||
QObject *parent)
|
QObject *parent)
|
||||||
: ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg)
|
: ConfiguratorBase(sshSession, parent), m_isAwg(isAwg)
|
||||||
{
|
{
|
||||||
m_serverConfigPath =
|
m_serverConfigPath =
|
||||||
m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
|
m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
|
||||||
@@ -33,8 +42,8 @@ WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings,
|
|||||||
m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
|
m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
|
||||||
m_configTemplate = m_isAwg ? ProtocolScriptType::awg_template : ProtocolScriptType::wireguard_template;
|
m_configTemplate = m_isAwg ? ProtocolScriptType::awg_template : ProtocolScriptType::wireguard_template;
|
||||||
|
|
||||||
m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard;
|
m_protocolName = m_isAwg ? configKey::awg : configKey::wireguard;
|
||||||
m_defaultPort = m_isAwg ? protocols::wireguard::defaultPort : protocols::awg::defaultPort;
|
m_defaultPort = m_isAwg ? protocols::awg::defaultPort : protocols::wireguard::defaultPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
|
WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
|
||||||
@@ -91,12 +100,21 @@ QList<QHostAddress> WireguardConfigurator::getIpsFromConf(const QString &input)
|
|||||||
|
|
||||||
WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
|
WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
|
||||||
DockerContainer container,
|
DockerContainer container,
|
||||||
const QJsonObject &containerConfig,
|
const WireGuardServerConfig* serverConfig,
|
||||||
|
const AwgServerConfig* awgServerConfig,
|
||||||
|
const DnsSettings &dnsSettings,
|
||||||
ErrorCode &errorCode)
|
ErrorCode &errorCode)
|
||||||
{
|
{
|
||||||
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
|
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
|
||||||
connData.host = credentials.hostName;
|
connData.host = credentials.hostName;
|
||||||
connData.port = containerConfig.value(m_protocolName).toObject().value(config_key::port).toString(m_defaultPort);
|
|
||||||
|
QString portStr = m_defaultPort;
|
||||||
|
if (serverConfig && !serverConfig->port.isEmpty()) {
|
||||||
|
portStr = serverConfig->port;
|
||||||
|
} else if (awgServerConfig && !awgServerConfig->port.isEmpty()) {
|
||||||
|
portStr = awgServerConfig->port;
|
||||||
|
}
|
||||||
|
connData.port = portStr;
|
||||||
|
|
||||||
if (connData.clientPrivKey.isEmpty() || connData.clientPubKey.isEmpty()) {
|
if (connData.clientPrivKey.isEmpty() || connData.clientPubKey.isEmpty()) {
|
||||||
errorCode = ErrorCode::InternalError;
|
errorCode = ErrorCode::InternalError;
|
||||||
@@ -114,7 +132,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
|||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
|
|
||||||
errorCode = m_serverController->runContainerScript(credentials, container, getIpsScript, cbReadStdOut);
|
errorCode = m_sshSession->runContainerScript(credentials, container, getIpsScript, cbReadStdOut);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
@@ -123,11 +141,14 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
|||||||
QHostAddress nextIp = [&] {
|
QHostAddress nextIp = [&] {
|
||||||
QHostAddress result;
|
QHostAddress result;
|
||||||
QHostAddress lastIp;
|
QHostAddress lastIp;
|
||||||
|
QString subnetAddress = protocols::wireguard::defaultSubnetAddress;
|
||||||
|
if (serverConfig && !serverConfig->subnetAddress.isEmpty()) {
|
||||||
|
subnetAddress = serverConfig->subnetAddress;
|
||||||
|
} else if (awgServerConfig && !awgServerConfig->subnetAddress.isEmpty()) {
|
||||||
|
subnetAddress = awgServerConfig->subnetAddress;
|
||||||
|
}
|
||||||
if (ips.empty()) {
|
if (ips.empty()) {
|
||||||
lastIp.setAddress(containerConfig.value(m_protocolName)
|
lastIp.setAddress(subnetAddress);
|
||||||
.toObject()
|
|
||||||
.value(config_key::subnet_address)
|
|
||||||
.toString(protocols::wireguard::defaultSubnetAddress));
|
|
||||||
} else {
|
} else {
|
||||||
lastIp = ips.last();
|
lastIp = ips.last();
|
||||||
}
|
}
|
||||||
@@ -145,13 +166,13 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
|||||||
|
|
||||||
// Get keys
|
// Get keys
|
||||||
connData.serverPubKey =
|
connData.serverPubKey =
|
||||||
m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
|
m_sshSession->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
|
||||||
connData.serverPubKey.replace("\n", "");
|
connData.serverPubKey.replace("\n", "");
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
|
||||||
connData.pskKey = m_serverController->getTextFileFromContainer(container, credentials, m_serverPskKeyPath, errorCode);
|
connData.pskKey = m_sshSession->getTextFileFromContainer(container, credentials, m_serverPskKeyPath, errorCode);
|
||||||
connData.pskKey.replace("\n", "");
|
connData.pskKey.replace("\n", "");
|
||||||
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
@@ -165,7 +186,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
|||||||
"AllowedIPs = %3/32\n\n")
|
"AllowedIPs = %3/32\n\n")
|
||||||
.arg(connData.clientPubKey, connData.pskKey, connData.clientIP);
|
.arg(connData.clientPubKey, connData.pskKey, connData.clientIP);
|
||||||
|
|
||||||
errorCode = m_serverController->uploadTextFileToContainer(container, credentials, configPart, configPath,
|
errorCode = m_sshSession->uploadTextFileToContainer(container, credentials, configPart, configPath,
|
||||||
libssh::ScpOverwriteMode::ScpAppendToExisting);
|
libssh::ScpOverwriteMode::ScpAppendToExisting);
|
||||||
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
@@ -178,23 +199,43 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
|||||||
QString script = QString(
|
QString script = QString(
|
||||||
"sudo docker exec -i $CONTAINER_NAME bash -c '%1 syncconf %2 <(%1-quick strip %3)'").arg(bin, iface, configPath);
|
"sudo docker exec -i $CONTAINER_NAME bash -c '%1 syncconf %2 <(%1-quick strip %3)'").arg(bin, iface, configPath);
|
||||||
|
|
||||||
errorCode = m_serverController->runScript(
|
errorCode = m_sshSession->runScript(
|
||||||
credentials,
|
credentials,
|
||||||
m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
|
m_sshSession->replaceVars(script, amnezia::genBaseVars(credentials, container, dnsSettings.primaryDns, dnsSettings.secondaryDns)));
|
||||||
|
|
||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString WireguardConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
ProtocolConfig WireguardConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
const ContainerConfig &containerConfig,
|
||||||
|
const DnsSettings &dnsSettings,
|
||||||
|
ErrorCode &errorCode)
|
||||||
{
|
{
|
||||||
|
const WireGuardServerConfig* wireguardServerConfig = nullptr;
|
||||||
|
const WireGuardClientConfig* wireguardClientConfig = nullptr;
|
||||||
|
const AwgServerConfig* awgServerConfig = nullptr;
|
||||||
|
const AwgClientConfig* awgClientConfig = nullptr;
|
||||||
|
|
||||||
|
if (auto* wireGuardProtocolConfig = containerConfig.getWireGuardProtocolConfig()) {
|
||||||
|
wireguardServerConfig = &wireGuardProtocolConfig->serverConfig;
|
||||||
|
if (wireGuardProtocolConfig->clientConfig.has_value()) {
|
||||||
|
wireguardClientConfig = &wireGuardProtocolConfig->clientConfig.value();
|
||||||
|
}
|
||||||
|
} else if (auto* awgProtocolConfig = containerConfig.getAwgProtocolConfig()) {
|
||||||
|
awgServerConfig = &awgProtocolConfig->serverConfig;
|
||||||
|
if (awgProtocolConfig->clientConfig.has_value()) {
|
||||||
|
awgClientConfig = &awgProtocolConfig->clientConfig.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
amnezia::ScriptVars vars = amnezia::genBaseVars(credentials, container, dnsSettings.primaryDns, dnsSettings.secondaryDns);
|
||||||
|
vars.append(amnezia::genProtocolVarsForContainer(container, containerConfig));
|
||||||
QString scriptData = amnezia::scriptData(m_configTemplate, container);
|
QString scriptData = amnezia::scriptData(m_configTemplate, container);
|
||||||
QString config = m_serverController->replaceVars(
|
QString config = m_sshSession->replaceVars(scriptData, vars);
|
||||||
scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
|
|
||||||
|
|
||||||
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
|
ConnectionData connData = prepareWireguardConfig(credentials, container, wireguardServerConfig, awgServerConfig, dnsSettings, errorCode);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return "";
|
return WireGuardProtocolConfig{};
|
||||||
}
|
}
|
||||||
|
|
||||||
config.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", connData.clientPrivKey);
|
config.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", connData.clientPrivKey);
|
||||||
@@ -202,40 +243,46 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
|
|||||||
config.replace("$WIREGUARD_SERVER_PUBLIC_KEY", connData.serverPubKey);
|
config.replace("$WIREGUARD_SERVER_PUBLIC_KEY", connData.serverPubKey);
|
||||||
config.replace("$WIREGUARD_PSK", connData.pskKey);
|
config.replace("$WIREGUARD_PSK", connData.pskKey);
|
||||||
|
|
||||||
const QJsonObject &wireguarConfig = containerConfig.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject();
|
QString mtu = protocols::wireguard::defaultMtu;
|
||||||
QJsonObject jConfig;
|
if (wireguardClientConfig && !wireguardClientConfig->mtu.isEmpty()) {
|
||||||
jConfig[config_key::config] = config;
|
mtu = wireguardClientConfig->mtu;
|
||||||
|
} else if (awgClientConfig && !awgClientConfig->mtu.isEmpty()) {
|
||||||
jConfig[config_key::hostName] = connData.host;
|
mtu = awgClientConfig->mtu;
|
||||||
jConfig[config_key::port] = connData.port.toInt();
|
}
|
||||||
jConfig[config_key::client_priv_key] = connData.clientPrivKey;
|
|
||||||
jConfig[config_key::client_ip] = connData.clientIP;
|
WireGuardProtocolConfig protocolConfig;
|
||||||
jConfig[config_key::client_pub_key] = connData.clientPubKey;
|
if (wireguardServerConfig) {
|
||||||
jConfig[config_key::psk_key] = connData.pskKey;
|
protocolConfig.serverConfig = *wireguardServerConfig;
|
||||||
jConfig[config_key::server_pub_key] = connData.serverPubKey;
|
}
|
||||||
jConfig[config_key::mtu] = wireguarConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu);
|
|
||||||
|
WireGuardClientConfig clientConfig;
|
||||||
jConfig[config_key::persistent_keep_alive] = "25";
|
clientConfig.nativeConfig = config;
|
||||||
QJsonArray allowedIps { "0.0.0.0/0", "::/0" };
|
clientConfig.hostName = connData.host;
|
||||||
jConfig[config_key::allowed_ips] = allowedIps;
|
clientConfig.port = connData.port.toInt();
|
||||||
|
clientConfig.clientIp = connData.clientIP;
|
||||||
jConfig[config_key::clientId] = connData.clientPubKey;
|
clientConfig.clientPrivateKey = connData.clientPrivKey;
|
||||||
|
clientConfig.clientPublicKey = connData.clientPubKey;
|
||||||
return QJsonDocument(jConfig).toJson();
|
clientConfig.serverPublicKey = connData.serverPubKey;
|
||||||
|
clientConfig.presharedKey = connData.pskKey;
|
||||||
|
clientConfig.clientId = connData.clientPubKey;
|
||||||
|
clientConfig.allowedIps = QStringList { "0.0.0.0/0", "::/0" };
|
||||||
|
clientConfig.persistentKeepAlive = "25";
|
||||||
|
clientConfig.mtu = mtu;
|
||||||
|
clientConfig.isObfuscationEnabled = false;
|
||||||
|
|
||||||
|
protocolConfig.setClientConfig(clientConfig);
|
||||||
|
|
||||||
|
return protocolConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns,
|
ProtocolConfig WireguardConfigurator::processConfigWithLocalSettings(const ConnectionSettings &settings,
|
||||||
const bool isApiConfig, QString &protocolConfigString)
|
ProtocolConfig protocolConfig)
|
||||||
{
|
{
|
||||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
return ConfiguratorBase::processConfigWithLocalSettings(settings, protocolConfig);
|
||||||
|
|
||||||
return protocolConfigString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns,
|
ProtocolConfig WireguardConfigurator::processConfigWithExportSettings(const ExportSettings &settings,
|
||||||
const bool isApiConfig, QString &protocolConfigString)
|
ProtocolConfig protocolConfig)
|
||||||
{
|
{
|
||||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
return ConfiguratorBase::processConfigWithExportSettings(settings, protocolConfig);
|
||||||
|
|
||||||
return protocolConfigString;
|
|
||||||
}
|
}
|
||||||
61
client/core/configurators/wireguardConfigurator.h
Normal file
61
client/core/configurators/wireguardConfigurator.h
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#ifndef WIREGUARD_CONFIGURATOR_H
|
||||||
|
#define WIREGUARD_CONFIGURATOR_H
|
||||||
|
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QProcessEnvironment>
|
||||||
|
|
||||||
|
#include "configuratorBase.h"
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
#include "core/utils/selfhosted/scriptsRegistry.h"
|
||||||
|
|
||||||
|
class WireguardConfigurator : public ConfiguratorBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
WireguardConfigurator(SshSession* sshSession,
|
||||||
|
bool isAwg, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
struct ConnectionData
|
||||||
|
{
|
||||||
|
QString clientPrivKey; // client private key
|
||||||
|
QString clientPubKey; // client public key
|
||||||
|
QString clientIP; // internal client IP address
|
||||||
|
QString serverPubKey; // tls-auth key
|
||||||
|
QString pskKey; // preshared key
|
||||||
|
QString host; // host ip
|
||||||
|
QString port;
|
||||||
|
};
|
||||||
|
|
||||||
|
amnezia::ProtocolConfig createConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container,
|
||||||
|
const amnezia::ContainerConfig &containerConfig,
|
||||||
|
const amnezia::DnsSettings &dnsSettings,
|
||||||
|
amnezia::ErrorCode &errorCode) override;
|
||||||
|
|
||||||
|
amnezia::ProtocolConfig processConfigWithLocalSettings(const amnezia::ConnectionSettings &settings,
|
||||||
|
amnezia::ProtocolConfig protocolConfig) override;
|
||||||
|
amnezia::ProtocolConfig processConfigWithExportSettings(const amnezia::ExportSettings &settings,
|
||||||
|
amnezia::ProtocolConfig protocolConfig) override;
|
||||||
|
|
||||||
|
static ConnectionData genClientKeys();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<QHostAddress> getIpsFromConf(const QString &input);
|
||||||
|
ConnectionData prepareWireguardConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container,
|
||||||
|
const amnezia::WireGuardServerConfig* serverConfig,
|
||||||
|
const amnezia::AwgServerConfig* awgServerConfig,
|
||||||
|
const amnezia::DnsSettings &dnsSettings,
|
||||||
|
amnezia::ErrorCode &errorCode);
|
||||||
|
|
||||||
|
bool m_isAwg;
|
||||||
|
QString m_serverConfigPath;
|
||||||
|
QString m_serverPublicKeyPath;
|
||||||
|
QString m_serverPskKeyPath;
|
||||||
|
amnezia::ProtocolScriptType m_configTemplate;
|
||||||
|
QString m_protocolName;
|
||||||
|
QString m_defaultPort;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WIREGUARD_CONFIGURATOR_H
|
||||||
@@ -1,32 +1,43 @@
|
|||||||
#include "xray_configurator.h"
|
#include "xrayConfigurator.h"
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
#include "core/utils/containerEnum.h"
|
||||||
#include "core/controllers/serverController.h"
|
#include "core/utils/containers/containerUtils.h"
|
||||||
#include "core/scripts_registry.h"
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
|
#include "core/utils/selfhosted/scriptsRegistry.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/models/containerConfig.h"
|
||||||
|
#include "core/models/protocols/xrayProtocolConfig.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
Logger logger("XrayConfigurator");
|
Logger logger("XrayConfigurator");
|
||||||
}
|
}
|
||||||
|
|
||||||
XrayConfigurator::XrayConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, QObject *parent)
|
XrayConfigurator::XrayConfigurator(SshSession* sshSession, QObject *parent)
|
||||||
: ConfiguratorBase(settings, serverController, parent)
|
: ConfiguratorBase(sshSession, parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentials, DockerContainer container,
|
QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
const ContainerConfig &containerConfig,
|
||||||
|
const DnsSettings &dnsSettings,
|
||||||
|
ErrorCode &errorCode)
|
||||||
{
|
{
|
||||||
// Generate new UUID for client
|
// Generate new UUID for client
|
||||||
QString clientId = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
QString clientId = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
||||||
|
|
||||||
// Get current server config
|
// Get current server config
|
||||||
QString currentConfig = m_serverController->getTextFileFromContainer(
|
QString currentConfig = m_sshSession->getTextFileFromContainer(
|
||||||
container, credentials, amnezia::protocols::xray::serverConfigPath, errorCode);
|
container, credentials, amnezia::protocols::xray::serverConfigPath, errorCode);
|
||||||
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
@@ -45,13 +56,13 @@ QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentia
|
|||||||
QJsonObject serverConfig = doc.object();
|
QJsonObject serverConfig = doc.object();
|
||||||
|
|
||||||
// Validate server config structure
|
// Validate server config structure
|
||||||
if (!serverConfig.contains("inbounds")) {
|
if (!serverConfig.contains(amnezia::protocols::xray::inbounds)) {
|
||||||
logger.error() << "Server config missing 'inbounds' field";
|
logger.error() << "Server config missing 'inbounds' field";
|
||||||
errorCode = ErrorCode::InternalError;
|
errorCode = ErrorCode::InternalError;
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonArray inbounds = serverConfig["inbounds"].toArray();
|
QJsonArray inbounds = serverConfig[amnezia::protocols::xray::inbounds].toArray();
|
||||||
if (inbounds.isEmpty()) {
|
if (inbounds.isEmpty()) {
|
||||||
logger.error() << "Server config has empty 'inbounds' array";
|
logger.error() << "Server config has empty 'inbounds' array";
|
||||||
errorCode = ErrorCode::InternalError;
|
errorCode = ErrorCode::InternalError;
|
||||||
@@ -59,38 +70,38 @@ QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentia
|
|||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject inbound = inbounds[0].toObject();
|
QJsonObject inbound = inbounds[0].toObject();
|
||||||
if (!inbound.contains("settings")) {
|
if (!inbound.contains(amnezia::protocols::xray::settings)) {
|
||||||
logger.error() << "Inbound missing 'settings' field";
|
logger.error() << "Inbound missing 'settings' field";
|
||||||
errorCode = ErrorCode::InternalError;
|
errorCode = ErrorCode::InternalError;
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject settings = inbound["settings"].toObject();
|
QJsonObject settings = inbound[amnezia::protocols::xray::settings].toObject();
|
||||||
if (!settings.contains("clients")) {
|
if (!settings.contains(amnezia::protocols::xray::clients)) {
|
||||||
logger.error() << "Settings missing 'clients' field";
|
logger.error() << "Settings missing 'clients' field";
|
||||||
errorCode = ErrorCode::InternalError;
|
errorCode = ErrorCode::InternalError;
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonArray clients = settings["clients"].toArray();
|
QJsonArray clients = settings[amnezia::protocols::xray::clients].toArray();
|
||||||
|
|
||||||
// Create configuration for new client
|
// Create configuration for new client
|
||||||
QJsonObject clientConfig {
|
QJsonObject clientConfig {
|
||||||
{"id", clientId},
|
{amnezia::protocols::xray::id, clientId},
|
||||||
{"flow", "xtls-rprx-vision"}
|
{amnezia::protocols::xray::flow, "xtls-rprx-vision"}
|
||||||
};
|
};
|
||||||
|
|
||||||
clients.append(clientConfig);
|
clients.append(clientConfig);
|
||||||
|
|
||||||
// Update config
|
// Update config
|
||||||
settings["clients"] = clients;
|
settings[amnezia::protocols::xray::clients] = clients;
|
||||||
inbound["settings"] = settings;
|
inbound[amnezia::protocols::xray::settings] = settings;
|
||||||
inbounds[0] = inbound;
|
inbounds[0] = inbound;
|
||||||
serverConfig["inbounds"] = inbounds;
|
serverConfig[amnezia::protocols::xray::inbounds] = inbounds;
|
||||||
|
|
||||||
// Save updated config to server
|
// Save updated config to server
|
||||||
QString updatedConfig = QJsonDocument(serverConfig).toJson();
|
QString updatedConfig = QJsonDocument(serverConfig).toJson();
|
||||||
errorCode = m_serverController->uploadTextFileToContainer(
|
errorCode = m_sshSession->uploadTextFileToContainer(
|
||||||
container,
|
container,
|
||||||
credentials,
|
credentials,
|
||||||
updatedConfig,
|
updatedConfig,
|
||||||
@@ -104,9 +115,9 @@ QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentia
|
|||||||
|
|
||||||
// Restart container
|
// Restart container
|
||||||
QString restartScript = QString("sudo docker restart $CONTAINER_NAME");
|
QString restartScript = QString("sudo docker restart $CONTAINER_NAME");
|
||||||
errorCode = m_serverController->runScript(
|
errorCode = m_sshSession->runScript(
|
||||||
credentials,
|
credentials,
|
||||||
m_serverController->replaceVars(restartScript, m_serverController->genVarsForScript(credentials, container))
|
m_sshSession->replaceVars(restartScript, amnezia::genBaseVars(credentials, container, dnsSettings.primaryDns, dnsSettings.secondaryDns))
|
||||||
);
|
);
|
||||||
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
@@ -117,57 +128,75 @@ QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentia
|
|||||||
return clientId;
|
return clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
ProtocolConfig XrayConfigurator::createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
const ContainerConfig &containerConfig,
|
||||||
|
const DnsSettings &dnsSettings,
|
||||||
|
ErrorCode &errorCode)
|
||||||
{
|
{
|
||||||
// Get client ID from prepareServerConfig
|
const XrayServerConfig* serverConfig = nullptr;
|
||||||
QString xrayClientId = prepareServerConfig(credentials, container, containerConfig, errorCode);
|
if (auto* xrayConfig = containerConfig.protocolConfig.as<XrayProtocolConfig>()) {
|
||||||
|
serverConfig = &xrayConfig->serverConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString xrayClientId = prepareServerConfig(credentials, container, containerConfig, dnsSettings, errorCode);
|
||||||
if (errorCode != ErrorCode::NoError || xrayClientId.isEmpty()) {
|
if (errorCode != ErrorCode::NoError || xrayClientId.isEmpty()) {
|
||||||
logger.error() << "Failed to prepare server config";
|
logger.error() << "Failed to prepare server config";
|
||||||
errorCode = ErrorCode::InternalError;
|
errorCode = ErrorCode::InternalError;
|
||||||
return "";
|
return XrayProtocolConfig{};
|
||||||
}
|
}
|
||||||
|
|
||||||
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container),
|
amnezia::ScriptVars vars = amnezia::genBaseVars(credentials, container, dnsSettings.primaryDns, dnsSettings.secondaryDns);
|
||||||
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
vars.append(amnezia::genProtocolVarsForContainer(container, containerConfig));
|
||||||
|
QString config = m_sshSession->replaceVars(amnezia::scriptData(ProtocolScriptType::xray_template, container), vars);
|
||||||
|
|
||||||
if (config.isEmpty()) {
|
if (config.isEmpty()) {
|
||||||
logger.error() << "Failed to get config template";
|
logger.error() << "Failed to get config template";
|
||||||
errorCode = ErrorCode::InternalError;
|
errorCode = ErrorCode::InternalError;
|
||||||
return "";
|
return XrayProtocolConfig{};
|
||||||
}
|
}
|
||||||
|
|
||||||
QString xrayPublicKey =
|
QString xrayPublicKey =
|
||||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::PublicKeyPath, errorCode);
|
m_sshSession->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::PublicKeyPath, errorCode);
|
||||||
if (errorCode != ErrorCode::NoError || xrayPublicKey.isEmpty()) {
|
if (errorCode != ErrorCode::NoError || xrayPublicKey.isEmpty()) {
|
||||||
logger.error() << "Failed to get public key";
|
logger.error() << "Failed to get public key";
|
||||||
errorCode = ErrorCode::InternalError;
|
errorCode = ErrorCode::InternalError;
|
||||||
return "";
|
return XrayProtocolConfig{};
|
||||||
}
|
}
|
||||||
xrayPublicKey.replace("\n", "");
|
xrayPublicKey.replace("\n", "");
|
||||||
|
|
||||||
QString xrayShortId =
|
QString xrayShortId =
|
||||||
m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::shortidPath, errorCode);
|
m_sshSession->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::shortidPath, errorCode);
|
||||||
if (errorCode != ErrorCode::NoError || xrayShortId.isEmpty()) {
|
if (errorCode != ErrorCode::NoError || xrayShortId.isEmpty()) {
|
||||||
logger.error() << "Failed to get short ID";
|
logger.error() << "Failed to get short ID";
|
||||||
errorCode = ErrorCode::InternalError;
|
errorCode = ErrorCode::InternalError;
|
||||||
return "";
|
return XrayProtocolConfig{};
|
||||||
}
|
}
|
||||||
xrayShortId.replace("\n", "");
|
xrayShortId.replace("\n", "");
|
||||||
|
|
||||||
// Validate all required variables are present
|
|
||||||
if (!config.contains("$XRAY_CLIENT_ID") || !config.contains("$XRAY_PUBLIC_KEY") || !config.contains("$XRAY_SHORT_ID")) {
|
if (!config.contains("$XRAY_CLIENT_ID") || !config.contains("$XRAY_PUBLIC_KEY") || !config.contains("$XRAY_SHORT_ID")) {
|
||||||
logger.error() << "Config template missing required variables:"
|
logger.error() << "Config template missing required variables:"
|
||||||
<< "XRAY_CLIENT_ID:" << !config.contains("$XRAY_CLIENT_ID")
|
<< "XRAY_CLIENT_ID:" << !config.contains("$XRAY_CLIENT_ID")
|
||||||
<< "XRAY_PUBLIC_KEY:" << !config.contains("$XRAY_PUBLIC_KEY")
|
<< "XRAY_PUBLIC_KEY:" << !config.contains("$XRAY_PUBLIC_KEY")
|
||||||
<< "XRAY_SHORT_ID:" << !config.contains("$XRAY_SHORT_ID");
|
<< "XRAY_SHORT_ID:" << !config.contains("$XRAY_SHORT_ID");
|
||||||
errorCode = ErrorCode::InternalError;
|
errorCode = ErrorCode::InternalError;
|
||||||
return "";
|
return XrayProtocolConfig{};
|
||||||
}
|
}
|
||||||
|
|
||||||
config.replace("$XRAY_CLIENT_ID", xrayClientId);
|
config.replace("$XRAY_CLIENT_ID", xrayClientId);
|
||||||
config.replace("$XRAY_PUBLIC_KEY", xrayPublicKey);
|
config.replace("$XRAY_PUBLIC_KEY", xrayPublicKey);
|
||||||
config.replace("$XRAY_SHORT_ID", xrayShortId);
|
config.replace("$XRAY_SHORT_ID", xrayShortId);
|
||||||
|
|
||||||
return config;
|
XrayProtocolConfig protocolConfig;
|
||||||
|
if (serverConfig) {
|
||||||
|
protocolConfig.serverConfig = *serverConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
XrayClientConfig clientConfig;
|
||||||
|
clientConfig.nativeConfig = config;
|
||||||
|
clientConfig.localPort = "";
|
||||||
|
clientConfig.id = xrayClientId;
|
||||||
|
|
||||||
|
protocolConfig.setClientConfig(clientConfig);
|
||||||
|
|
||||||
|
return protocolConfig;
|
||||||
}
|
}
|
||||||
27
client/core/configurators/xrayConfigurator.h
Normal file
27
client/core/configurators/xrayConfigurator.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#ifndef XRAY_CONFIGURATOR_H
|
||||||
|
#define XRAY_CONFIGURATOR_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "configuratorBase.h"
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
|
||||||
|
class XrayConfigurator : public ConfiguratorBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
XrayConfigurator(SshSession* sshSession, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
amnezia::ProtocolConfig createConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container, const amnezia::ContainerConfig &containerConfig,
|
||||||
|
const amnezia::DnsSettings &dnsSettings,
|
||||||
|
amnezia::ErrorCode &errorCode) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString prepareServerConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container, const amnezia::ContainerConfig &containerConfig,
|
||||||
|
const amnezia::DnsSettings &dnsSettings,
|
||||||
|
amnezia::ErrorCode &errorCode);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // XRAY_CONFIGURATOR_H
|
||||||
54
client/core/controllers/allowedDnsController.cpp
Normal file
54
client/core/controllers/allowedDnsController.cpp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#include "allowedDnsController.h"
|
||||||
|
|
||||||
|
AllowedDnsController::AllowedDnsController(SecureAppSettingsRepository* appSettingsRepository)
|
||||||
|
: m_appSettingsRepository(appSettingsRepository)
|
||||||
|
{
|
||||||
|
fillDnsServers();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AllowedDnsController::addDns(const QString &ip)
|
||||||
|
{
|
||||||
|
if (m_dnsServers.contains(ip)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dnsServers.append(ip);
|
||||||
|
m_appSettingsRepository->setAllowedDnsServers(m_dnsServers);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AllowedDnsController::addDnsList(const QStringList &dnsServers, bool replaceExisting)
|
||||||
|
{
|
||||||
|
if (replaceExisting) {
|
||||||
|
m_dnsServers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const QString &ip : dnsServers) {
|
||||||
|
if (!m_dnsServers.contains(ip)) {
|
||||||
|
m_dnsServers.append(ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_appSettingsRepository->setAllowedDnsServers(m_dnsServers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AllowedDnsController::removeDns(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= m_dnsServers.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dnsServers.removeAt(index);
|
||||||
|
m_appSettingsRepository->setAllowedDnsServers(m_dnsServers);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList AllowedDnsController::getCurrentDnsServers() const
|
||||||
|
{
|
||||||
|
return m_dnsServers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AllowedDnsController::fillDnsServers()
|
||||||
|
{
|
||||||
|
m_dnsServers = m_appSettingsRepository->getAllowedDnsServers();
|
||||||
|
}
|
||||||
|
|
||||||
26
client/core/controllers/allowedDnsController.h
Normal file
26
client/core/controllers/allowedDnsController.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#ifndef ALLOWEDDNSCONTROLLER_H
|
||||||
|
#define ALLOWEDDNSCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
|
|
||||||
|
class AllowedDnsController
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AllowedDnsController(SecureAppSettingsRepository* appSettingsRepository);
|
||||||
|
|
||||||
|
bool addDns(const QString &ip);
|
||||||
|
void addDnsList(const QStringList &dnsServers, bool replaceExisting);
|
||||||
|
void removeDns(int index);
|
||||||
|
QStringList getCurrentDnsServers() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void fillDnsServers();
|
||||||
|
|
||||||
|
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||||
|
QStringList m_dnsServers;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ALLOWEDDNSCONTROLLER_H
|
||||||
|
|
||||||
72
client/core/controllers/api/newsController.cpp
Normal file
72
client/core/controllers/api/newsController.cpp
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#include "newsController.h"
|
||||||
|
|
||||||
|
#include "core/controllers/gatewayController.h"
|
||||||
|
#include "core/utils/api/apiEnums.h"
|
||||||
|
#include "core/utils/constants/apiKeys.h"
|
||||||
|
#include "core/utils/constants/apiConstants.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include <QtConcurrent/QtConcurrent>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
|
||||||
|
NewsController::NewsController(SecureAppSettingsRepository* appSettingsRepository,
|
||||||
|
ServersController* serversController)
|
||||||
|
: m_appSettingsRepository(appSettingsRepository), m_serversController(serversController)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture<QPair<ErrorCode, QJsonArray>> NewsController::fetchNews()
|
||||||
|
{
|
||||||
|
if (!m_serversController) {
|
||||||
|
qWarning() << "ServersController is null, skip fetchNews";
|
||||||
|
return QtFuture::makeReadyFuture(qMakePair(ErrorCode::InternalError, QJsonArray()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto stacks = m_serversController->gatewayStacks();
|
||||||
|
if (stacks.isEmpty()) {
|
||||||
|
qDebug() << "No Gateway stacks, skip fetchNews";
|
||||||
|
return QtFuture::makeReadyFuture(qMakePair(ErrorCode::NoError, QJsonArray()));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto gatewayController = QSharedPointer<GatewayController>::create(
|
||||||
|
m_appSettingsRepository->getGatewayEndpoint(),
|
||||||
|
m_appSettingsRepository->isDevGatewayEnv(),
|
||||||
|
apiDefs::requestTimeoutMsecs,
|
||||||
|
m_appSettingsRepository->isStrictKillSwitchEnabled());
|
||||||
|
|
||||||
|
QJsonObject payload;
|
||||||
|
payload.insert("locale", m_appSettingsRepository->getAppLanguage().name().split("_").first());
|
||||||
|
|
||||||
|
const QJsonObject stacksJson = stacks.toJson();
|
||||||
|
if (stacksJson.contains(apiDefs::key::userCountryCode)) {
|
||||||
|
payload.insert(apiDefs::key::userCountryCode, stacksJson.value(apiDefs::key::userCountryCode));
|
||||||
|
}
|
||||||
|
if (stacksJson.contains(apiDefs::key::serviceType)) {
|
||||||
|
payload.insert(apiDefs::key::serviceType, stacksJson.value(apiDefs::key::serviceType));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto future = gatewayController->postAsync(QString("%1v1/news"), payload);
|
||||||
|
return future.then([gatewayController](QPair<ErrorCode, QByteArray> result) -> QPair<ErrorCode, QJsonArray> {
|
||||||
|
auto [errorCode, responseBody] = result;
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
return qMakePair(errorCode, QJsonArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(responseBody);
|
||||||
|
QJsonArray newsArray;
|
||||||
|
if (doc.isArray()) {
|
||||||
|
newsArray = doc.array();
|
||||||
|
} else if (doc.isObject()) {
|
||||||
|
QJsonObject obj = doc.object();
|
||||||
|
if (obj.value("news").isArray()) {
|
||||||
|
newsArray = obj.value("news").toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return qMakePair(ErrorCode::NoError, newsArray);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
28
client/core/controllers/api/newsController.h
Normal file
28
client/core/controllers/api/newsController.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#ifndef NEWSCONTROLLER_H
|
||||||
|
#define NEWSCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QPair>
|
||||||
|
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
|
#include "core/controllers/serversController.h"
|
||||||
|
|
||||||
|
class NewsController
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit NewsController(SecureAppSettingsRepository* appSettingsRepository,
|
||||||
|
ServersController* serversController);
|
||||||
|
|
||||||
|
QFuture<QPair<ErrorCode, QJsonArray>> fetchNews();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||||
|
ServersController* m_serversController;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NEWSCONTROLLER_H
|
||||||
|
|
||||||
248
client/core/controllers/api/servicesCatalogController.cpp
Normal file
248
client/core/controllers/api/servicesCatalogController.cpp
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
#include "servicesCatalogController.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QSysInfo>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QSet>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "core/controllers/gatewayController.h"
|
||||||
|
#include "core/utils/api/apiEnums.h"
|
||||||
|
#include "core/utils/constants/apiKeys.h"
|
||||||
|
#include "core/utils/constants/apiConstants.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
|
#include "platforms/ios/ios_controller.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
namespace configKey
|
||||||
|
{
|
||||||
|
constexpr char serviceDescription[] = "service_description";
|
||||||
|
constexpr char subscriptionPlans[] = "subscription_plans";
|
||||||
|
constexpr char storeProductId[] = "store_product_id";
|
||||||
|
constexpr char priceLabel[] = "price_label";
|
||||||
|
constexpr char subtitle[] = "subtitle";
|
||||||
|
constexpr char isTrial[] = "is_trial";
|
||||||
|
constexpr char minPriceLabel[] = "min_price_label";
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace serviceType
|
||||||
|
{
|
||||||
|
constexpr char amneziaPremium[] = "amnezia-premium";
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
|
struct StoreKitPlanQuote {
|
||||||
|
QString displayPrice;
|
||||||
|
double priceAmount = 0.0;
|
||||||
|
double subscriptionBillingMonths = 0.0;
|
||||||
|
QString displayPricePerMonth;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr double oneMonthThreshold = 1.0 + 1e-6;
|
||||||
|
constexpr double monthsFallbackThreshold = 1e-6;
|
||||||
|
constexpr double monthlyPriceEpsilon = 1e-9;
|
||||||
|
|
||||||
|
QStringList collectPremiumStoreProductIds(const QJsonArray &services)
|
||||||
|
{
|
||||||
|
QStringList productIds;
|
||||||
|
QSet<QString> seenProductIds;
|
||||||
|
for (const QJsonValue &serviceValue : services) {
|
||||||
|
const QJsonObject serviceObject = serviceValue.toObject();
|
||||||
|
if (serviceObject.value(apiDefs::key::serviceType).toString() != serviceType::amneziaPremium) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const QJsonArray subscriptionPlans =
|
||||||
|
serviceObject.value(configKey::serviceDescription).toObject().value(configKey::subscriptionPlans).toArray();
|
||||||
|
for (const QJsonValue &planValue : subscriptionPlans) {
|
||||||
|
if (!planValue.isObject()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const QString storeProductId = planValue.toObject().value(configKey::storeProductId).toString();
|
||||||
|
if (storeProductId.isEmpty() || seenProductIds.contains(storeProductId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
seenProductIds.insert(storeProductId);
|
||||||
|
productIds.append(storeProductId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return productIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<QString, StoreKitPlanQuote> buildStoreKitQuoteMap(const QList<QVariantMap> &fetchedProducts)
|
||||||
|
{
|
||||||
|
QHash<QString, StoreKitPlanQuote> quotesByProductId;
|
||||||
|
quotesByProductId.reserve(fetchedProducts.size());
|
||||||
|
|
||||||
|
for (const QVariantMap &productInfo : fetchedProducts) {
|
||||||
|
const QString productId = productInfo.value(QStringLiteral("productId")).toString();
|
||||||
|
if (productId.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString displayPrice = productInfo.value(QStringLiteral("displayPrice")).toString();
|
||||||
|
if (displayPrice.isEmpty()) {
|
||||||
|
const QString price = productInfo.value(QStringLiteral("price")).toString();
|
||||||
|
const QString currencyCode = productInfo.value(QStringLiteral("currencyCode")).toString();
|
||||||
|
displayPrice = currencyCode.isEmpty() ? price : (price + QLatin1Char(' ') + currencyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
StoreKitPlanQuote quote;
|
||||||
|
quote.displayPrice = displayPrice;
|
||||||
|
quote.priceAmount = productInfo.value(QStringLiteral("priceAmount")).toDouble();
|
||||||
|
quote.subscriptionBillingMonths = productInfo.value(QStringLiteral("subscriptionBillingMonths")).toDouble();
|
||||||
|
quote.displayPricePerMonth = productInfo.value(QStringLiteral("displayPricePerMonth")).toString();
|
||||||
|
quotesByProductId.insert(productId, quote);
|
||||||
|
}
|
||||||
|
|
||||||
|
return quotesByProductId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mergeStoreKitPricesIntoPremiumPlans(QJsonObject &data)
|
||||||
|
{
|
||||||
|
QJsonArray services = data.value(apiDefs::key::services).toArray();
|
||||||
|
if (services.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStringList productIds = collectPremiumStoreProductIds(services);
|
||||||
|
if (productIds.isEmpty()) {
|
||||||
|
qInfo().noquote() << "[IAP] No store_product_id in premium plans; skip StoreKit merge into services payload";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QVariantMap> fetchedProducts;
|
||||||
|
QEventLoop loop;
|
||||||
|
IosController::Instance()->fetchProducts(productIds,
|
||||||
|
[&](const QList<QVariantMap> &products, const QStringList &invalidIds,
|
||||||
|
const QString &errorString) {
|
||||||
|
if (!errorString.isEmpty()) {
|
||||||
|
qWarning().noquote() << "[IAP] StoreKit merge fetch:" << errorString;
|
||||||
|
}
|
||||||
|
if (!invalidIds.isEmpty()) {
|
||||||
|
qWarning().noquote() << "[IAP] Unknown App Store product ids:" << invalidIds;
|
||||||
|
}
|
||||||
|
fetchedProducts = products;
|
||||||
|
loop.quit();
|
||||||
|
});
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
const QHash<QString, StoreKitPlanQuote> quotesByProductId = buildStoreKitQuoteMap(fetchedProducts);
|
||||||
|
|
||||||
|
for (int serviceIndex = 0; serviceIndex < services.size(); ++serviceIndex) {
|
||||||
|
QJsonObject serviceObject = services.at(serviceIndex).toObject();
|
||||||
|
if (serviceObject.value(apiDefs::key::serviceType).toString() != serviceType::amneziaPremium) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject descriptionObject = serviceObject.value(configKey::serviceDescription).toObject();
|
||||||
|
const QJsonArray sourcePlans = descriptionObject.value(configKey::subscriptionPlans).toArray();
|
||||||
|
|
||||||
|
QJsonArray mergedPlans;
|
||||||
|
double minMonthlyAmount = std::numeric_limits<double>::infinity();
|
||||||
|
QString minMonthlyDisplay;
|
||||||
|
|
||||||
|
for (const QJsonValue &planValue : sourcePlans) {
|
||||||
|
if (!planValue.isObject()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject planObject = planValue.toObject();
|
||||||
|
const QString storeProductId = planObject.value(configKey::storeProductId).toString();
|
||||||
|
if (storeProductId.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto quoteIterator = quotesByProductId.constFind(storeProductId);
|
||||||
|
if (quoteIterator == quotesByProductId.cend()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool isTrialPlan = planObject.value(configKey::isTrial).toBool();
|
||||||
|
const StoreKitPlanQuote "e = *quoteIterator;
|
||||||
|
planObject.insert(configKey::priceLabel, quote.displayPrice);
|
||||||
|
|
||||||
|
const double months = quote.subscriptionBillingMonths;
|
||||||
|
if (!isTrialPlan && months > oneMonthThreshold && !quote.displayPricePerMonth.isEmpty()) {
|
||||||
|
planObject.insert(
|
||||||
|
configKey::subtitle,
|
||||||
|
QCoreApplication::translate("ServicesCatalogController", "%1/mo",
|
||||||
|
"IAP: price per month in plan subtitle")
|
||||||
|
.arg(quote.displayPricePerMonth));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isTrialPlan && quote.priceAmount > 0.0) {
|
||||||
|
const double monthsForMin = months > monthsFallbackThreshold ? months : 1.0;
|
||||||
|
const double monthly = quote.priceAmount / monthsForMin;
|
||||||
|
if (monthly < minMonthlyAmount - monthlyPriceEpsilon) {
|
||||||
|
minMonthlyAmount = monthly;
|
||||||
|
minMonthlyDisplay = !quote.displayPricePerMonth.isEmpty() ? quote.displayPricePerMonth : quote.displayPrice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mergedPlans.append(planObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptionObject.insert(configKey::subscriptionPlans, mergedPlans);
|
||||||
|
if (minMonthlyAmount < std::numeric_limits<double>::infinity() && !minMonthlyDisplay.isEmpty()) {
|
||||||
|
descriptionObject.insert(configKey::minPriceLabel,
|
||||||
|
QCoreApplication::translate("ServicesCatalogController", "from %1 per month",
|
||||||
|
"IAP: card footer minimum monthly price from StoreKit")
|
||||||
|
.arg(minMonthlyDisplay));
|
||||||
|
}
|
||||||
|
serviceObject.insert(configKey::serviceDescription, descriptionObject);
|
||||||
|
services.replace(serviceIndex, serviceObject);
|
||||||
|
}
|
||||||
|
data.insert(apiDefs::key::services, services);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ServicesCatalogController::ServicesCatalogController(SecureAppSettingsRepository* appSettingsRepository)
|
||||||
|
: m_appSettingsRepository(appSettingsRepository)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode ServicesCatalogController::fillAvailableServices(QJsonObject &servicesData)
|
||||||
|
{
|
||||||
|
QJsonObject apiPayload;
|
||||||
|
apiPayload[apiDefs::key::osVersion] = QSysInfo::productType();
|
||||||
|
apiPayload[apiDefs::key::appVersion] = QString(APP_VERSION);
|
||||||
|
apiPayload[apiDefs::key::cliName] = QString(APPLICATION_NAME);
|
||||||
|
apiPayload[apiDefs::key::appLanguage] = m_appSettingsRepository->getAppLanguage().name().split("_").first();
|
||||||
|
|
||||||
|
QByteArray responseBody;
|
||||||
|
ErrorCode errorCode = executeRequest(QString("%1v1/services"), apiPayload, responseBody);
|
||||||
|
if (errorCode == ErrorCode::NoError) {
|
||||||
|
if (!responseBody.contains(apiDefs::key::services.data())) {
|
||||||
|
errorCode = ErrorCode::ApiServicesMissingError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
servicesData = QJsonDocument::fromJson(responseBody).object();
|
||||||
|
|
||||||
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
|
mergeStoreKitPricesIntoPremiumPlans(servicesData);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode ServicesCatalogController::executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody)
|
||||||
|
{
|
||||||
|
GatewayController gatewayController(m_appSettingsRepository->getGatewayEndpoint(), m_appSettingsRepository->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
|
||||||
|
m_appSettingsRepository->isStrictKillSwitchEnabled());
|
||||||
|
return gatewayController.post(endpoint, apiPayload, responseBody);
|
||||||
|
}
|
||||||
|
|
||||||
26
client/core/controllers/api/servicesCatalogController.h
Normal file
26
client/core/controllers/api/servicesCatalogController.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#ifndef SERVICESCATALOGCONTROLLER_H
|
||||||
|
#define SERVICESCATALOGCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
|
|
||||||
|
class ServicesCatalogController
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ServicesCatalogController(SecureAppSettingsRepository* appSettingsRepository);
|
||||||
|
|
||||||
|
ErrorCode fillAvailableServices(QJsonObject &servicesData);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody);
|
||||||
|
|
||||||
|
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SERVICESCATALOGCONTROLLER_H
|
||||||
|
|
||||||
1094
client/core/controllers/api/subscriptionController.cpp
Normal file
1094
client/core/controllers/api/subscriptionController.cpp
Normal file
File diff suppressed because it is too large
Load Diff
122
client/core/controllers/api/subscriptionController.h
Normal file
122
client/core/controllers/api/subscriptionController.h
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
#ifndef SUBSCRIPTIONCONTROLLER_H
|
||||||
|
#define SUBSCRIPTIONCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QList>
|
||||||
|
#include <QVariantMap>
|
||||||
|
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
#include "core/repositories/secureServersRepository.h"
|
||||||
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
|
|
||||||
|
class ServersController;
|
||||||
|
|
||||||
|
class SubscriptionController
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct ProtocolData
|
||||||
|
{
|
||||||
|
QString certRequest;
|
||||||
|
QString certPrivKey;
|
||||||
|
QString wireGuardClientPrivKey;
|
||||||
|
QString wireGuardClientPubKey;
|
||||||
|
QString xrayUuid;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GatewayRequestData
|
||||||
|
{
|
||||||
|
QString osVersion;
|
||||||
|
QString appVersion;
|
||||||
|
QString appLanguage;
|
||||||
|
QString installationUuid;
|
||||||
|
QString userCountryCode;
|
||||||
|
QString serverCountryCode;
|
||||||
|
QString serviceType;
|
||||||
|
QString serviceProtocol;
|
||||||
|
QJsonObject authData;
|
||||||
|
|
||||||
|
QJsonObject toJsonObject() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit SubscriptionController(SecureServersRepository* serversRepository,
|
||||||
|
SecureAppSettingsRepository* appSettingsRepository);
|
||||||
|
|
||||||
|
ProtocolData generateProtocolData(const QString &protocol);
|
||||||
|
void appendProtocolDataToApiPayload(const QString &protocol, const ProtocolData &protocolData, QJsonObject &apiPayload);
|
||||||
|
ErrorCode fillServerConfig(const QJsonObject &serverConfigJson, ServerConfig &serverConfig);
|
||||||
|
|
||||||
|
ErrorCode importServiceFromGateway(const QString &userCountryCode, const QString &serviceType,
|
||||||
|
const QString &serviceProtocol, const ProtocolData &protocolData,
|
||||||
|
ServerConfig &serverConfig);
|
||||||
|
ErrorCode importTrialFromGateway(const QString &userCountryCode, const QString &serviceType,
|
||||||
|
const QString &serviceProtocol, const QString &email,
|
||||||
|
ServerConfig &serverConfig);
|
||||||
|
|
||||||
|
ErrorCode importServiceFromAppStore(const QString &userCountryCode, const QString &serviceType,
|
||||||
|
const QString &serviceProtocol, const ProtocolData &protocolData,
|
||||||
|
const QString &transactionId, bool isTestPurchase,
|
||||||
|
ServerConfig &serverConfig,
|
||||||
|
int *duplicateServerIndex = nullptr);
|
||||||
|
|
||||||
|
ErrorCode updateServiceFromGateway(int serverIndex, const QString &newCountryCode, bool isConnectEvent);
|
||||||
|
|
||||||
|
ErrorCode deactivateDevice(int serverIndex, bool isRemoveEvent);
|
||||||
|
|
||||||
|
ErrorCode deactivateExternalDevice(int serverIndex, const QString &uuid, const QString &serverCountryCode);
|
||||||
|
|
||||||
|
ErrorCode exportNativeConfig(int serverIndex, const QString &serverCountryCode, QString &nativeConfig);
|
||||||
|
|
||||||
|
ErrorCode revokeNativeConfig(int serverIndex, const QString &serverCountryCode);
|
||||||
|
|
||||||
|
ErrorCode updateServiceFromTelegram(int serverIndex);
|
||||||
|
|
||||||
|
ErrorCode prepareVpnKeyExport(int serverIndex, QString &vpnKey);
|
||||||
|
|
||||||
|
ErrorCode validateAndUpdateConfig(int serverIndex, bool hasInstalledContainers);
|
||||||
|
|
||||||
|
void removeApiConfig(int serverIndex);
|
||||||
|
|
||||||
|
void setCurrentProtocol(int serverIndex, const QString &protocolName);
|
||||||
|
bool isVlessProtocol(int serverIndex) const;
|
||||||
|
|
||||||
|
ErrorCode getAccountInfo(int serverIndex, QJsonObject &accountInfo);
|
||||||
|
QFuture<QPair<ErrorCode, QString>> getRenewalLink(int serverIndex);
|
||||||
|
|
||||||
|
struct AppStoreRestoreResult
|
||||||
|
{
|
||||||
|
bool hasInstalledConfig = false;
|
||||||
|
bool duplicateConfigAlreadyPresent = false;
|
||||||
|
int duplicateCount = 0;
|
||||||
|
int duplicateServerIndex = -1;
|
||||||
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
|
};
|
||||||
|
|
||||||
|
ErrorCode processAppStorePurchase(const QString &userCountryCode, const QString &serviceType,
|
||||||
|
const QString &serviceProtocol, const QString &productId,
|
||||||
|
ServerConfig &serverConfig,
|
||||||
|
int *duplicateServerIndex = nullptr);
|
||||||
|
|
||||||
|
AppStoreRestoreResult processAppStoreRestore(const QString &userCountryCode, const QString &serviceType,
|
||||||
|
const QString &serviceProtocol);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody, bool isTestPurchase = false);
|
||||||
|
bool isApiKeyExpired(int serverIndex) const;
|
||||||
|
|
||||||
|
ErrorCode extractServerConfigJsonFromResponse(const QByteArray &apiResponseBody, const QString &protocol,
|
||||||
|
const ProtocolData &protocolData, QJsonObject &serverConfigJson);
|
||||||
|
void updateApiConfigInJson(QJsonObject &serverConfigJson, const QString &serviceType,
|
||||||
|
const QString &serviceProtocol, const QString &userCountryCode,
|
||||||
|
const QByteArray &apiResponseBody);
|
||||||
|
|
||||||
|
SecureServersRepository* m_serversRepository;
|
||||||
|
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SUBSCRIPTIONCONTROLLER_H
|
||||||
|
|
||||||
70
client/core/controllers/appSplitTunnelingController.cpp
Normal file
70
client/core/controllers/appSplitTunnelingController.cpp
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#include "appSplitTunnelingController.h"
|
||||||
|
|
||||||
|
AppSplitTunnelingController::AppSplitTunnelingController(SecureAppSettingsRepository* appSettingsRepository)
|
||||||
|
: m_appSettingsRepository(appSettingsRepository)
|
||||||
|
{
|
||||||
|
m_currentRouteMode = m_appSettingsRepository->appsRouteMode();
|
||||||
|
if (m_currentRouteMode == AppsRouteMode::VpnAllApps) { // for old split tunneling configs
|
||||||
|
m_currentRouteMode = AppsRouteMode::VpnAllExceptApps;
|
||||||
|
m_apps = m_appSettingsRepository->vpnApps(m_currentRouteMode);
|
||||||
|
m_appSettingsRepository->setAppsRouteMode(AppsRouteMode::VpnAllExceptApps);
|
||||||
|
} else {
|
||||||
|
m_apps = m_appSettingsRepository->vpnApps(m_currentRouteMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppSplitTunnelingController::addApp(const amnezia::InstalledAppInfo &appInfo)
|
||||||
|
{
|
||||||
|
if (m_apps.contains(appInfo)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_apps.append(appInfo);
|
||||||
|
m_appSettingsRepository->setVpnApps(m_currentRouteMode, m_apps);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppSplitTunnelingController::removeApp(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= m_apps.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_apps.removeAt(index);
|
||||||
|
m_appSettingsRepository->setVpnApps(m_currentRouteMode, m_apps);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppSplitTunnelingController::clearAppsList()
|
||||||
|
{
|
||||||
|
m_apps.clear();
|
||||||
|
m_appSettingsRepository->setVpnApps(m_currentRouteMode, m_apps);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppSplitTunnelingController::setRouteMode(AppsRouteMode routeMode)
|
||||||
|
{
|
||||||
|
m_currentRouteMode = routeMode;
|
||||||
|
m_apps = m_appSettingsRepository->vpnApps(m_currentRouteMode);
|
||||||
|
m_appSettingsRepository->setAppsRouteMode(routeMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppSplitTunnelingController::toggleSplitTunneling(bool enabled)
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->setAppsSplitTunnelingEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
AppsRouteMode AppSplitTunnelingController::getRouteMode() const
|
||||||
|
{
|
||||||
|
return m_currentRouteMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppSplitTunnelingController::isSplitTunnelingEnabled() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->isAppsSplitTunnelingEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<amnezia::InstalledAppInfo> AppSplitTunnelingController::getApps() const
|
||||||
|
{
|
||||||
|
return m_apps;
|
||||||
|
}
|
||||||
|
|
||||||
32
client/core/controllers/appSplitTunnelingController.h
Normal file
32
client/core/controllers/appSplitTunnelingController.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#ifndef APPSPLITTUNNELINGCONTROLLER_H
|
||||||
|
#define APPSPLITTUNNELINGCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
|
|
||||||
|
class AppSplitTunnelingController
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AppSplitTunnelingController(SecureAppSettingsRepository* appSettingsRepository);
|
||||||
|
|
||||||
|
bool addApp(const amnezia::InstalledAppInfo &appInfo);
|
||||||
|
void removeApp(int index);
|
||||||
|
void clearAppsList();
|
||||||
|
void setRouteMode(AppsRouteMode routeMode);
|
||||||
|
void toggleSplitTunneling(bool enabled);
|
||||||
|
|
||||||
|
AppsRouteMode getRouteMode() const;
|
||||||
|
bool isSplitTunnelingEnabled() const;
|
||||||
|
QVector<amnezia::InstalledAppInfo> getApps() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||||
|
AppsRouteMode m_currentRouteMode;
|
||||||
|
QVector<amnezia::InstalledAppInfo> m_apps;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // APPSPLITTUNNELINGCONTROLLER_H
|
||||||
|
|
||||||
183
client/core/controllers/connectionController.cpp
Normal file
183
client/core/controllers/connectionController.cpp
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
#include "connectionController.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
#include "core/configurators/configuratorBase.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/utils/utilities.h"
|
||||||
|
#include "core/utils/networkUtilities.h"
|
||||||
|
#include "version.h"
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
|
#include "core/models/containerConfig.h"
|
||||||
|
#include "core/models/protocolConfig.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
using namespace ProtocolUtils;
|
||||||
|
|
||||||
|
ConnectionController::ConnectionController(SecureServersRepository* serversRepository,
|
||||||
|
SecureAppSettingsRepository* appSettingsRepository,
|
||||||
|
VpnConnection* vpnConnection,
|
||||||
|
QObject* parent)
|
||||||
|
: QObject(parent),
|
||||||
|
m_serversRepository(serversRepository),
|
||||||
|
m_appSettingsRepository(appSettingsRepository),
|
||||||
|
m_vpnConnection(vpnConnection)
|
||||||
|
{
|
||||||
|
connect(m_vpnConnection, &VpnConnection::connectionStateChanged, this, &ConnectionController::connectionStateChanged);
|
||||||
|
connect(this, &ConnectionController::openConnectionRequested, m_vpnConnection, &VpnConnection::connectToVpn, Qt::QueuedConnection);
|
||||||
|
connect(this, &ConnectionController::closeConnectionRequested, m_vpnConnection, &VpnConnection::disconnectFromVpn, Qt::QueuedConnection);
|
||||||
|
connect(this, &ConnectionController::setConnectionStateRequested, m_vpnConnection, &VpnConnection::setConnectionState, Qt::QueuedConnection);
|
||||||
|
connect(this, &ConnectionController::killSwitchModeChangedRequested, m_vpnConnection, &VpnConnection::onKillSwitchModeChanged, Qt::QueuedConnection);
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
connect(this, &ConnectionController::restoreConnectionRequested, m_vpnConnection, &VpnConnection::restoreConnection, Qt::QueuedConnection);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionController::isConnected() const
|
||||||
|
{
|
||||||
|
return m_vpnConnection && m_vpnConnection->connectionState() == Vpn::ConnectionState::Connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionController::setConnectionState(Vpn::ConnectionState state)
|
||||||
|
{
|
||||||
|
if (m_vpnConnection) {
|
||||||
|
emit setConnectionStateRequested(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode ConnectionController::prepareConnection(int serverIndex,
|
||||||
|
QJsonObject& vpnConfiguration,
|
||||||
|
DockerContainer& container)
|
||||||
|
{
|
||||||
|
if (!isServiceReady()) {
|
||||||
|
return ErrorCode::AmneziaServiceNotRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerConfig serverConfigModel = m_serversRepository->server(serverIndex);
|
||||||
|
container = serverConfigModel.defaultContainer();
|
||||||
|
|
||||||
|
if (!isContainerSupported(container)) {
|
||||||
|
return ErrorCode::NotSupportedOnThisPlatform;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainerConfig containerConfigModel = m_serversRepository->containerConfig(serverIndex, container);
|
||||||
|
|
||||||
|
auto dns = serverConfigModel.getDnsPair(m_appSettingsRepository->useAmneziaDns(),
|
||||||
|
m_appSettingsRepository->primaryDns(),
|
||||||
|
m_appSettingsRepository->secondaryDns());
|
||||||
|
|
||||||
|
vpnConfiguration = createConnectionConfiguration(dns, serverConfigModel, containerConfigModel, container);
|
||||||
|
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode ConnectionController::openConnection(int serverIndex)
|
||||||
|
{
|
||||||
|
QJsonObject vpnConfiguration;
|
||||||
|
DockerContainer container;
|
||||||
|
|
||||||
|
ErrorCode errorCode = prepareConnection(serverIndex, vpnConfiguration, container);
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit openConnectionRequested(serverIndex, container, vpnConfiguration);
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionController::closeConnection()
|
||||||
|
{
|
||||||
|
if (m_vpnConnection) {
|
||||||
|
emit closeConnectionRequested();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
void ConnectionController::restoreConnection()
|
||||||
|
{
|
||||||
|
if (m_vpnConnection) {
|
||||||
|
emit restoreConnectionRequested();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void ConnectionController::onKillSwitchModeChanged(bool enabled)
|
||||||
|
{
|
||||||
|
if (m_vpnConnection) {
|
||||||
|
emit killSwitchModeChangedRequested(enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode ConnectionController::lastConnectionError() const
|
||||||
|
{
|
||||||
|
return m_vpnConnection->lastError();
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ConnectionController::createConnectionConfiguration(const QPair<QString, QString> &dns,
|
||||||
|
const ServerConfig &serverConfig,
|
||||||
|
const ContainerConfig &containerConfig,
|
||||||
|
DockerContainer container)
|
||||||
|
{
|
||||||
|
QJsonObject vpnConfiguration {};
|
||||||
|
|
||||||
|
if (ContainerUtils::containerService(container) == ServiceType::Other) {
|
||||||
|
return vpnConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
Proto proto = ContainerUtils::defaultProtocol(container);
|
||||||
|
|
||||||
|
ConnectionSettings connectionSettings = {
|
||||||
|
{ dns.first, dns.second },
|
||||||
|
serverConfig.isApiConfig(),
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->isSitesSplitTunnelingEnabled(),
|
||||||
|
m_appSettingsRepository->routeMode()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto configurator = ConfiguratorBase::create(proto, nullptr);
|
||||||
|
ProtocolConfig processedConfig = configurator->processConfigWithLocalSettings(connectionSettings,
|
||||||
|
containerConfig.protocolConfig);
|
||||||
|
|
||||||
|
QJsonObject vpnConfigData = processedConfig.getClientConfigJson();
|
||||||
|
if (ContainerUtils::isAwgContainer(container) || container == DockerContainer::WireGuard) {
|
||||||
|
if (vpnConfigData[configKey::mtu].toString().isEmpty()) {
|
||||||
|
vpnConfigData[configKey::mtu] =
|
||||||
|
ContainerUtils::isAwgContainer(container) ? protocols::awg::defaultMtu :
|
||||||
|
protocols::wireguard::defaultMtu;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vpnConfiguration.insert(ProtocolUtils::key_proto_config_data(proto), vpnConfigData);
|
||||||
|
vpnConfiguration[configKey::vpnProto] = ProtocolUtils::protoToString(proto);
|
||||||
|
|
||||||
|
vpnConfiguration[configKey::dns1] = dns.first;
|
||||||
|
vpnConfiguration[configKey::dns2] = dns.second;
|
||||||
|
|
||||||
|
vpnConfiguration[configKey::hostName] = serverConfig.hostName();
|
||||||
|
vpnConfiguration[configKey::description] = serverConfig.description();
|
||||||
|
|
||||||
|
vpnConfiguration[configKey::configVersion] = serverConfig.configVersion();
|
||||||
|
|
||||||
|
return vpnConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionController::isServiceReady() const
|
||||||
|
{
|
||||||
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
|
return Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true);
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionController::isContainerSupported(DockerContainer container) const
|
||||||
|
{
|
||||||
|
return ContainerUtils::isSupportedByCurrentPlatform(container);
|
||||||
|
}
|
||||||
78
client/core/controllers/connectionController.h
Normal file
78
client/core/controllers/connectionController.h
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#ifndef CONNECTIONCONTROLLER_H
|
||||||
|
#define CONNECTIONCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QPair>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
#include "core/repositories/secureServersRepository.h"
|
||||||
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
|
#include "core/protocols/vpnProtocol.h"
|
||||||
|
#include "vpnConnection.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
|
||||||
|
class ConnectionController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ConnectionController(SecureServersRepository* serversRepository,
|
||||||
|
SecureAppSettingsRepository* appSettingsRepository,
|
||||||
|
VpnConnection* vpnConnection,
|
||||||
|
QObject* parent = nullptr);
|
||||||
|
~ConnectionController() = default;
|
||||||
|
|
||||||
|
ErrorCode prepareConnection(int serverIndex,
|
||||||
|
QJsonObject& vpnConfiguration,
|
||||||
|
DockerContainer& container);
|
||||||
|
|
||||||
|
ErrorCode openConnection(int serverIndex);
|
||||||
|
|
||||||
|
void closeConnection();
|
||||||
|
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
void restoreConnection();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void onKillSwitchModeChanged(bool enabled);
|
||||||
|
|
||||||
|
ErrorCode lastConnectionError() const;
|
||||||
|
|
||||||
|
bool isConnected() const;
|
||||||
|
void setConnectionState(Vpn::ConnectionState state);
|
||||||
|
|
||||||
|
QJsonObject createConnectionConfiguration(const QPair<QString, QString> &dns,
|
||||||
|
const ServerConfig &serverConfig,
|
||||||
|
const ContainerConfig &containerConfig,
|
||||||
|
DockerContainer container);
|
||||||
|
|
||||||
|
bool isServiceReady() const;
|
||||||
|
|
||||||
|
bool isContainerSupported(DockerContainer container) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void connectionStateChanged(Vpn::ConnectionState state);
|
||||||
|
void openConnectionRequested(int serverIndex, DockerContainer container, const QJsonObject &vpnConfiguration);
|
||||||
|
void closeConnectionRequested();
|
||||||
|
void setConnectionStateRequested(Vpn::ConnectionState state);
|
||||||
|
void killSwitchModeChangedRequested(bool enabled);
|
||||||
|
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
void restoreConnectionRequested();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
SecureServersRepository* m_serversRepository;
|
||||||
|
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||||
|
VpnConnection* m_vpnConnection;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -2,9 +2,18 @@
|
|||||||
|
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
|
#include "core/controllers/selfhosted/installController.h"
|
||||||
|
#include "core/controllers/selfhosted/importController.h"
|
||||||
|
#include "core/controllers/coreSignalHandlers.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "secureQSettings.h"
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID)
|
#if defined(Q_OS_ANDROID)
|
||||||
#include "core/installedAppsImageProvider.h"
|
#include "core/utils/installedAppsImageProvider.h"
|
||||||
#include "platforms/android/android_controller.h"
|
#include "platforms/android/android_controller.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -13,158 +22,196 @@
|
|||||||
#include <AmneziaVPN-Swift.h>
|
#include <AmneziaVPN-Swift.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
CoreController::CoreController(const QSharedPointer<VpnConnection> &vpnConnection, const std::shared_ptr<Settings> &settings,
|
CoreController::CoreController(const QSharedPointer<VpnConnection> &vpnConnection, SecureQSettings* settings,
|
||||||
QQmlApplicationEngine *engine, QObject *parent)
|
QQmlApplicationEngine *engine, QObject *parent)
|
||||||
: QObject(parent), m_vpnConnection(vpnConnection), m_settings(settings), m_engine(engine)
|
: QObject(parent), m_vpnConnection(vpnConnection), m_settings(settings), m_engine(engine)
|
||||||
{
|
{
|
||||||
|
initRepositories();
|
||||||
|
initCoreControllers();
|
||||||
initModels();
|
initModels();
|
||||||
initControllers();
|
initControllers();
|
||||||
initSignalHandlers();
|
initSignalHandlers();
|
||||||
|
|
||||||
initAndroidController();
|
initAndroidController();
|
||||||
initAppleController();
|
initAppleController();
|
||||||
|
initLogging();
|
||||||
|
|
||||||
initNotificationHandler();
|
m_translator = new QTranslator(this);
|
||||||
|
if (m_appSettingsRepository) {
|
||||||
|
updateTranslator(m_appSettingsRepository->getAppLanguage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_translator.reset(new QTranslator());
|
void CoreController::setQmlContextProperty(const QString &name, QObject *value)
|
||||||
updateTranslator(m_settings->getAppLanguage());
|
{
|
||||||
|
if (m_engine) {
|
||||||
|
m_engine->rootContext()->setContextProperty(name, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initModels()
|
void CoreController::initModels()
|
||||||
{
|
{
|
||||||
m_containersModel.reset(new ContainersModel(this));
|
m_containersModel = new ContainersModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
|
setQmlContextProperty("ContainersModel", m_containersModel);
|
||||||
|
|
||||||
m_defaultServerContainersModel.reset(new ContainersModel(this));
|
m_defaultServerContainersModel = new ContainersModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("DefaultServerContainersModel", m_defaultServerContainersModel.get());
|
setQmlContextProperty("DefaultServerContainersModel", m_defaultServerContainersModel);
|
||||||
|
|
||||||
m_serversModel.reset(new ServersModel(m_settings, this));
|
m_serversModel = new ServersModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
|
setQmlContextProperty("ServersModel", m_serversModel);
|
||||||
|
|
||||||
m_languageModel.reset(new LanguageModel(m_settings, this));
|
m_languageModel = new LanguageModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
|
setQmlContextProperty("LanguageModel", m_languageModel);
|
||||||
|
|
||||||
m_sitesModel.reset(new SitesModel(m_settings, this));
|
m_ipSplitTunnelingModel = new IpSplitTunnelingModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
|
setQmlContextProperty("IpSplitTunnelingModel", m_ipSplitTunnelingModel);
|
||||||
|
|
||||||
m_allowedDnsModel.reset(new AllowedDnsModel(m_settings, this));
|
m_allowedDnsModel = new AllowedDnsModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("AllowedDnsModel", m_allowedDnsModel.get());
|
setQmlContextProperty("AllowedDnsModel", m_allowedDnsModel);
|
||||||
|
|
||||||
m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
|
m_appSplitTunnelingModel = new AppSplitTunnelingModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
|
setQmlContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel);
|
||||||
|
|
||||||
m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
|
m_protocolsModel = new ProtocolsModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
|
setQmlContextProperty("ProtocolsModel", m_protocolsModel);
|
||||||
|
|
||||||
m_openVpnConfigModel.reset(new OpenVpnConfigModel(this));
|
m_openVpnConfigModel = new OpenVpnConfigModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get());
|
setQmlContextProperty("OpenVpnConfigModel", m_openVpnConfigModel);
|
||||||
|
|
||||||
m_shadowSocksConfigModel.reset(new ShadowSocksConfigModel(this));
|
m_wireGuardConfigModel = new WireGuardConfigModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get());
|
setQmlContextProperty("WireGuardConfigModel", m_wireGuardConfigModel);
|
||||||
|
|
||||||
m_cloakConfigModel.reset(new CloakConfigModel(this));
|
m_awgConfigModel = new AwgConfigModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get());
|
setQmlContextProperty("AwgConfigModel", m_awgConfigModel);
|
||||||
|
|
||||||
m_wireGuardConfigModel.reset(new WireGuardConfigModel(this));
|
m_xrayConfigModel = new XrayConfigModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get());
|
setQmlContextProperty("XrayConfigModel", m_xrayConfigModel);
|
||||||
|
|
||||||
m_awgConfigModel.reset(new AwgConfigModel(this));
|
m_torConfigModel = new TorConfigModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get());
|
setQmlContextProperty("TorConfigModel", m_torConfigModel);
|
||||||
|
|
||||||
m_xrayConfigModel.reset(new XrayConfigModel(this));
|
|
||||||
m_engine->rootContext()->setContextProperty("XrayConfigModel", m_xrayConfigModel.get());
|
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this));
|
m_ikev2ConfigModel = new Ikev2ConfigModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get());
|
setQmlContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_sftpConfigModel.reset(new SftpConfigModel(this));
|
m_sftpConfigModel = new SftpConfigModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
|
setQmlContextProperty("SftpConfigModel", m_sftpConfigModel);
|
||||||
|
|
||||||
m_socks5ConfigModel.reset(new Socks5ProxyConfigModel(this));
|
m_socks5ConfigModel = new Socks5ProxyConfigModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel.get());
|
setQmlContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel);
|
||||||
|
|
||||||
m_clientManagementModel.reset(new ClientManagementModel(m_settings, this));
|
m_clientManagementModel = new ClientManagementModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get());
|
setQmlContextProperty("ClientManagementModel", m_clientManagementModel);
|
||||||
|
|
||||||
m_apiServicesModel.reset(new ApiServicesModel(this));
|
m_apiServicesModel = new ApiServicesModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("ApiServicesModel", m_apiServicesModel.get());
|
setQmlContextProperty("ApiServicesModel", m_apiServicesModel);
|
||||||
|
|
||||||
m_apiSubscriptionPlansModel.reset(new ApiSubscriptionPlansModel(this));
|
m_apiCountryModel = new ApiCountryModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("ApiSubscriptionPlansModel", m_apiSubscriptionPlansModel.get());
|
setQmlContextProperty("ApiCountryModel", m_apiCountryModel);
|
||||||
|
|
||||||
m_apiBenefitsModel.reset(new ApiBenefitsModel(this));
|
m_apiSubscriptionPlansModel = new ApiSubscriptionPlansModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("ApiBenefitsModel", m_apiBenefitsModel.get());
|
setQmlContextProperty("ApiSubscriptionPlansModel", m_apiSubscriptionPlansModel);
|
||||||
|
|
||||||
m_apiCountryModel.reset(new ApiCountryModel(this));
|
m_apiBenefitsModel = new ApiBenefitsModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("ApiCountryModel", m_apiCountryModel.get());
|
setQmlContextProperty("ApiBenefitsModel", m_apiBenefitsModel);
|
||||||
|
|
||||||
m_apiAccountInfoModel.reset(new ApiAccountInfoModel(this));
|
m_apiAccountInfoModel = new ApiAccountInfoModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("ApiAccountInfoModel", m_apiAccountInfoModel.get());
|
setQmlContextProperty("ApiAccountInfoModel", m_apiAccountInfoModel);
|
||||||
|
|
||||||
m_apiDevicesModel.reset(new ApiDevicesModel(m_settings, this));
|
m_apiDevicesModel = new ApiDevicesModel(this);
|
||||||
m_engine->rootContext()->setContextProperty("ApiDevicesModel", m_apiDevicesModel.get());
|
setQmlContextProperty("ApiDevicesModel", m_apiDevicesModel);
|
||||||
|
|
||||||
m_newsModel.reset(new NewsModel(m_settings, this));
|
m_newsModel = new NewsModel(m_appSettingsRepository, this);
|
||||||
m_engine->rootContext()->setContextProperty("NewsModel", m_newsModel.get());
|
setQmlContextProperty("NewsModel", m_newsModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreController::initRepositories()
|
||||||
|
{
|
||||||
|
m_serversRepository = new SecureServersRepository(m_settings, this);
|
||||||
|
m_appSettingsRepository = new SecureAppSettingsRepository(m_settings, this);
|
||||||
|
|
||||||
|
if (m_vpnConnection) {
|
||||||
|
m_vpnConnection->setRepositories(m_serversRepository, m_appSettingsRepository);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreController::initCoreControllers()
|
||||||
|
{
|
||||||
|
m_serversController = new ServersController(m_serversRepository, m_appSettingsRepository, this);
|
||||||
|
m_appSplitTunnelingController = new AppSplitTunnelingController(m_appSettingsRepository);
|
||||||
|
m_usersController = new UsersController(m_serversRepository, this);
|
||||||
|
m_ipSplitTunnelingController = new IpSplitTunnelingController(m_appSettingsRepository, this);
|
||||||
|
m_allowedDnsController = new AllowedDnsController(m_appSettingsRepository);
|
||||||
|
m_servicesCatalogController = new ServicesCatalogController(m_appSettingsRepository);
|
||||||
|
m_subscriptionController = new SubscriptionController(m_serversRepository, m_appSettingsRepository);
|
||||||
|
m_newsController = new NewsController(m_appSettingsRepository, m_serversController);
|
||||||
|
|
||||||
|
m_installController = new InstallController(m_serversRepository, m_appSettingsRepository, this);
|
||||||
|
m_exportController = new ExportController(m_serversRepository, m_appSettingsRepository, this);
|
||||||
|
m_importCoreController = new ImportController(m_serversRepository, m_appSettingsRepository, this);
|
||||||
|
m_connectionController = new ConnectionController(m_serversRepository, m_appSettingsRepository, m_vpnConnection.get(), this);
|
||||||
|
m_settingsController = new SettingsController(m_serversRepository, m_appSettingsRepository, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initControllers()
|
void CoreController::initControllers()
|
||||||
{
|
{
|
||||||
m_connectionController.reset(
|
m_connectionUiController = new ConnectionUiController(m_connectionController, m_serversController, this);
|
||||||
new ConnectionController(m_serversModel, m_containersModel, m_clientManagementModel, m_vpnConnection, m_settings));
|
setQmlContextProperty("ConnectionController", m_connectionUiController);
|
||||||
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
|
|
||||||
|
|
||||||
m_pageController.reset(new PageController(m_serversModel, m_settings));
|
if (m_engine) {
|
||||||
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
|
m_focusController = new FocusController(m_engine, this);
|
||||||
|
setQmlContextProperty("FocusController", m_focusController);
|
||||||
|
}
|
||||||
|
|
||||||
m_focusController.reset(new FocusController(m_engine, this));
|
m_installUiController = new InstallUiController(m_installController, m_serversController, m_settingsController, m_protocolsModel, m_usersController,
|
||||||
m_engine->rootContext()->setContextProperty("FocusController", m_focusController.get());
|
m_awgConfigModel, m_wireGuardConfigModel, m_openVpnConfigModel, m_xrayConfigModel, m_torConfigModel,
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
m_ikev2ConfigModel,
|
||||||
|
#endif
|
||||||
|
m_sftpConfigModel, m_socks5ConfigModel, this);
|
||||||
|
setQmlContextProperty("InstallController", m_installUiController);
|
||||||
|
|
||||||
m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel, m_settings));
|
m_importController = new ImportUiController(m_importCoreController, this);
|
||||||
m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
|
setQmlContextProperty("ImportController", m_importController);
|
||||||
|
|
||||||
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
|
m_exportUiController = new ExportUiController(m_exportController, this);
|
||||||
&ConnectionController::onCurrentContainerUpdated); // TODO remove this
|
setQmlContextProperty("ExportController", m_exportUiController);
|
||||||
|
|
||||||
connect(m_installController.get(), &InstallController::profileCleared,
|
m_languageUiController = new LanguageUiController(m_settingsController, m_languageModel, this);
|
||||||
m_protocolsModel.get(), &ProtocolsModel::updateModel);
|
setQmlContextProperty("LanguageUiController", m_languageUiController);
|
||||||
|
|
||||||
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
|
m_settingsUiController = new SettingsUiController(m_settingsController, m_serversController, m_languageUiController, this);
|
||||||
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
|
setQmlContextProperty("SettingsController", m_settingsUiController);
|
||||||
|
|
||||||
m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel, m_settings));
|
m_pageController = new PageController(m_serversController, m_settingsController, this);
|
||||||
m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
|
setQmlContextProperty("PageController", m_pageController);
|
||||||
|
|
||||||
m_settingsController.reset(
|
m_serversUiController = new ServersUiController(m_serversController, m_settingsController, m_serversModel, m_containersModel, m_defaultServerContainersModel, this);
|
||||||
new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_appSplitTunnelingModel, m_settings));
|
setQmlContextProperty("ServersUiController", m_serversUiController);
|
||||||
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
|
|
||||||
|
|
||||||
m_sitesController.reset(new SitesController(m_settings, m_sitesModel));
|
m_ipSplitTunnelingUiController = new IpSplitTunnelingUiController(m_ipSplitTunnelingController, m_ipSplitTunnelingModel, this);
|
||||||
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
setQmlContextProperty("IpSplitTunnelingController", m_ipSplitTunnelingUiController);
|
||||||
|
|
||||||
m_allowedDnsController.reset(new AllowedDnsController(m_settings, m_allowedDnsModel));
|
m_allowedDnsUiController = new AllowedDnsUiController(m_allowedDnsController, m_allowedDnsModel, this);
|
||||||
m_engine->rootContext()->setContextProperty("AllowedDnsController", m_allowedDnsController.get());
|
setQmlContextProperty("AllowedDnsController", m_allowedDnsUiController);
|
||||||
|
|
||||||
m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
|
m_appSplitTunnelingUiController = new AppSplitTunnelingUiController(m_appSplitTunnelingController, m_appSplitTunnelingModel, this);
|
||||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
|
setQmlContextProperty("AppSplitTunnelingController", m_appSplitTunnelingUiController);
|
||||||
|
|
||||||
m_systemController.reset(new SystemController(m_settings));
|
m_systemController = new SystemController(this);
|
||||||
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
|
setQmlContextProperty("SystemController", m_systemController);
|
||||||
|
|
||||||
m_apiSettingsController.reset(
|
m_servicesCatalogUiController = new ServicesCatalogUiController(m_servicesCatalogController, m_apiServicesModel, this);
|
||||||
new ApiSettingsController(m_serversModel, m_apiAccountInfoModel, m_apiCountryModel, m_apiDevicesModel, m_settings));
|
setQmlContextProperty("ServicesCatalogUiController", m_servicesCatalogUiController);
|
||||||
m_engine->rootContext()->setContextProperty("ApiSettingsController", m_apiSettingsController.get());
|
|
||||||
|
|
||||||
m_apiConfigsController.reset(
|
m_subscriptionUiController = new SubscriptionUiController(m_serversController, m_apiServicesModel, m_servicesCatalogController, m_subscriptionController,
|
||||||
new ApiConfigsController(m_serversModel, m_apiServicesModel, m_apiSubscriptionPlansModel, m_apiBenefitsModel, m_settings));
|
m_apiSubscriptionPlansModel, m_apiBenefitsModel, m_apiAccountInfoModel,
|
||||||
m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigsController.get());
|
m_apiCountryModel, m_apiDevicesModel, m_settingsController, this);
|
||||||
connect(m_apiConfigsController.get(), &ApiConfigsController::subscriptionRefreshNeeded,
|
setQmlContextProperty("SubscriptionUiController", m_subscriptionUiController);
|
||||||
this, [this]() { m_apiSettingsController->getAccountInfo(false); });
|
|
||||||
|
|
||||||
m_apiNewsController.reset(new ApiNewsController(m_newsModel, m_settings, m_serversModel, this));
|
m_apiNewsUiController = new ApiNewsUiController(m_newsModel, m_newsController, this);
|
||||||
m_engine->rootContext()->setContextProperty("ApiNewsController", m_apiNewsController.get());
|
setQmlContextProperty("ApiNewsController", m_apiNewsUiController);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initAndroidController()
|
void CoreController::initAndroidController()
|
||||||
@@ -173,33 +220,16 @@ void CoreController::initAndroidController()
|
|||||||
if (!AndroidController::initLogging()) {
|
if (!AndroidController::initLogging()) {
|
||||||
qFatal("Android logging initialization failed");
|
qFatal("Android logging initialization failed");
|
||||||
}
|
}
|
||||||
AndroidController::instance()->setSaveLogs(m_settings->isSaveLogs());
|
AndroidController::instance()->setSaveLogs(m_appSettingsRepository->isSaveLogs());
|
||||||
connect(m_settings.get(), &Settings::saveLogsChanged, AndroidController::instance(), &AndroidController::setSaveLogs);
|
AndroidController::instance()->setScreenshotsEnabled(m_appSettingsRepository->isScreenshotsEnabled());
|
||||||
|
|
||||||
AndroidController::instance()->setScreenshotsEnabled(m_settings->isScreenshotsEnabled());
|
|
||||||
connect(m_settings.get(), &Settings::screenshotsEnabledChanged, AndroidController::instance(), &AndroidController::setScreenshotsEnabled);
|
|
||||||
|
|
||||||
connect(m_settings.get(), &Settings::serverRemoved, AndroidController::instance(), &AndroidController::resetLastServer);
|
|
||||||
|
|
||||||
connect(m_settings.get(), &Settings::settingsCleared, []() { AndroidController::instance()->resetLastServer(-1); });
|
|
||||||
|
|
||||||
connect(AndroidController::instance(), &AndroidController::initConnectionState, this, [this](Vpn::ConnectionState state) {
|
|
||||||
m_connectionController->onConnectionStateChanged(state);
|
|
||||||
if (m_vpnConnection)
|
|
||||||
m_vpnConnection->restoreConnection();
|
|
||||||
});
|
|
||||||
if (!AndroidController::instance()->initialize()) {
|
if (!AndroidController::instance()->initialize()) {
|
||||||
qFatal("Android controller initialization failed");
|
qFatal("Android controller initialization failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, this, [this](QString data) {
|
if (m_engine) {
|
||||||
emit m_pageController->goToPageHome();
|
m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider);
|
||||||
m_importController->extractConfigFromData(data);
|
}
|
||||||
data.clear();
|
|
||||||
emit m_pageController->goToPageViewConfig();
|
|
||||||
});
|
|
||||||
|
|
||||||
m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,63 +237,36 @@ void CoreController::initAppleController()
|
|||||||
{
|
{
|
||||||
#ifdef Q_OS_IOS
|
#ifdef Q_OS_IOS
|
||||||
IosController::Instance()->initialize();
|
IosController::Instance()->initialize();
|
||||||
connect(IosController::Instance(), &IosController::importConfigFromOutside, this, [this](QString data) {
|
QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_appSettingsRepository->isScreenshotsEnabled()); });
|
||||||
emit m_pageController->goToPageHome();
|
#endif
|
||||||
m_importController->extractConfigFromData(data);
|
}
|
||||||
emit m_pageController->goToPageViewConfig();
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(IosController::Instance(), &IosController::importBackupFromOutside, this, [this](QString filePath) {
|
void CoreController::initLogging()
|
||||||
emit m_pageController->goToPageHome();
|
{
|
||||||
m_pageController->goToPageSettingsBackup();
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
emit m_settingsController->importBackupFromOutside(filePath);
|
bool enabled = m_appSettingsRepository->isSaveLogs();
|
||||||
});
|
if (enabled) {
|
||||||
|
if (!Logger::init(false)) {
|
||||||
QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); });
|
qWarning() << "Initialization of debug subsystem failed";
|
||||||
|
}
|
||||||
connect(m_settings.get(), &Settings::screenshotsEnabledChanged, [](bool enabled) { AmneziaVPN::toggleScreenshots(enabled); });
|
}
|
||||||
|
Logger::setServiceLogsEnabled(enabled);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initSignalHandlers()
|
void CoreController::initSignalHandlers()
|
||||||
{
|
{
|
||||||
initErrorMessagesHandler();
|
m_signalHandlers = new CoreSignalHandlers(this, this);
|
||||||
|
m_signalHandlers->initAllHandlers();
|
||||||
initApiCountryModelUpdateHandler();
|
|
||||||
initContainerModelUpdateHandler();
|
// Trigger initial update after handlers are connected
|
||||||
initAdminConfigRevokedHandler();
|
m_serversUiController->updateModel();
|
||||||
initPassphraseRequestHandler();
|
|
||||||
initTranslationsUpdatedHandler();
|
|
||||||
initAutoConnectHandler();
|
|
||||||
initAmneziaDnsToggledHandler();
|
|
||||||
initPrepareConfigHandler();
|
|
||||||
initStrictKillSwitchHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initNotificationHandler()
|
|
||||||
{
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
|
||||||
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(),
|
|
||||||
static_cast<void (ConnectionController::*)()>(&ConnectionController::openConnection));
|
|
||||||
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
|
|
||||||
&ConnectionController::closeConnection);
|
|
||||||
connect(this, &CoreController::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
|
|
||||||
|
|
||||||
auto* trayHandler = qobject_cast<SystemTrayNotificationHandler*>(m_notificationHandler.get());
|
|
||||||
connect(this, &CoreController::websiteUrlChanged, trayHandler, &SystemTrayNotificationHandler::updateWebsiteUrl);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::updateTranslator(const QLocale &locale)
|
void CoreController::updateTranslator(const QLocale &locale)
|
||||||
{
|
{
|
||||||
if (!m_translator->isEmpty()) {
|
if (!m_translator->isEmpty()) {
|
||||||
QCoreApplication::removeTranslator(m_translator.get());
|
QCoreApplication::removeTranslator(m_translator);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList availableTranslations;
|
QStringList availableTranslations;
|
||||||
@@ -284,119 +287,31 @@ void CoreController::updateTranslator(const QLocale &locale)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_translator->load(strFileName)) {
|
if (m_translator->load(strFileName)) {
|
||||||
if (QCoreApplication::installTranslator(m_translator.get())) {
|
QCoreApplication::installTranslator(m_translator);
|
||||||
m_settings->setAppLanguage(locale);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
m_settings->setAppLanguage(QLocale::English);
|
if (m_translator->load(QString(":/translations/amneziavpn_en.qm"))) {
|
||||||
|
QCoreApplication::installTranslator(m_translator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_engine->retranslate();
|
if (m_engine) {
|
||||||
|
m_engine->retranslate();
|
||||||
|
}
|
||||||
|
|
||||||
emit translationsUpdated();
|
emit translationsUpdated();
|
||||||
emit websiteUrlChanged(m_languageModel->getCurrentSiteUrl());
|
if (m_languageUiController) {
|
||||||
}
|
emit websiteUrlChanged(m_languageUiController->getCurrentSiteUrl());
|
||||||
|
}
|
||||||
void CoreController::initErrorMessagesHandler()
|
|
||||||
{
|
|
||||||
connect(m_connectionController.get(), &ConnectionController::connectionErrorOccurred, this, [this](ErrorCode errorCode) {
|
|
||||||
emit m_pageController->showErrorMessage(errorCode);
|
|
||||||
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_apiConfigsController.get(), &ApiConfigsController::errorOccurred, m_pageController.get(),
|
|
||||||
qOverload<ErrorCode>(&PageController::showErrorMessage));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::setQmlRoot()
|
void CoreController::setQmlRoot()
|
||||||
{
|
{
|
||||||
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
|
if (m_engine && m_systemController) {
|
||||||
}
|
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
|
||||||
|
|
||||||
void CoreController::initApiCountryModelUpdateHandler()
|
|
||||||
{
|
|
||||||
connect(m_serversModel.get(), &ServersModel::updateApiCountryModel, this, [this]() {
|
|
||||||
m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(),
|
|
||||||
m_serversModel->getProcessedServerData("apiServerCountryCode").toString());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initContainerModelUpdateHandler()
|
|
||||||
{
|
|
||||||
connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(), &ContainersModel::updateModel);
|
|
||||||
connect(m_serversModel.get(), &ServersModel::defaultServerContainersUpdated, m_defaultServerContainersModel.get(),
|
|
||||||
&ContainersModel::updateModel);
|
|
||||||
connect(m_serversModel.get(), &ServersModel::gatewayStacksExpanded, this, [this]() {
|
|
||||||
if (m_serversModel->hasServersFromGatewayApi()) {
|
|
||||||
m_apiNewsController->fetchNews(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
m_serversModel->resetModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initAdminConfigRevokedHandler()
|
|
||||||
{
|
|
||||||
connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked, m_serversModel.get(),
|
|
||||||
&ServersModel::clearCachedProfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initPassphraseRequestHandler()
|
|
||||||
{
|
|
||||||
connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(),
|
|
||||||
&PageController::showPassphraseRequestDrawer);
|
|
||||||
connect(m_pageController.get(), &PageController::passphraseRequestDrawerClosed, m_installController.get(),
|
|
||||||
&InstallController::setEncryptedPassphrase);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initTranslationsUpdatedHandler()
|
|
||||||
{
|
|
||||||
connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &CoreController::updateTranslator);
|
|
||||||
connect(this, &CoreController::translationsUpdated, m_languageModel.get(), &LanguageModel::translationsUpdated);
|
|
||||||
connect(this, &CoreController::translationsUpdated, m_connectionController.get(), &ConnectionController::onTranslationsUpdated);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initAutoConnectHandler()
|
|
||||||
{
|
|
||||||
if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
|
|
||||||
QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initAmneziaDnsToggledHandler()
|
PageController* CoreController::pageController() const
|
||||||
{
|
|
||||||
connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled, m_serversModel.get(), &ServersModel::toggleAmneziaDns);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initPrepareConfigHandler()
|
|
||||||
{
|
|
||||||
connect(m_connectionController.get(), &ConnectionController::prepareConfig, this, [this]() {
|
|
||||||
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Preparing);
|
|
||||||
|
|
||||||
if (!m_apiConfigsController->isConfigValid()) {
|
|
||||||
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_installController->validateConfig();
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_installController.get(), &InstallController::configValidated, this, [this](bool isValid) {
|
|
||||||
if (!isValid) {
|
|
||||||
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_connectionController->openConnection();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreController::initStrictKillSwitchHandler()
|
|
||||||
{
|
|
||||||
connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, m_vpnConnection.get(),
|
|
||||||
&VpnConnection::onKillSwitchModeChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
QSharedPointer<PageController> CoreController::pageController() const
|
|
||||||
{
|
{
|
||||||
return m_pageController;
|
return m_pageController;
|
||||||
}
|
}
|
||||||
@@ -405,9 +320,11 @@ void CoreController::openConnectionByIndex(int serverIndex)
|
|||||||
{
|
{
|
||||||
if (m_serversModel) {
|
if (m_serversModel) {
|
||||||
m_serversModel->setProcessedServerIndex(serverIndex);
|
m_serversModel->setProcessedServerIndex(serverIndex);
|
||||||
m_serversModel->setDefaultServerIndex(serverIndex);
|
|
||||||
}
|
}
|
||||||
m_connectionController->toggleConnection();
|
if (m_serversController) {
|
||||||
|
m_serversController->setDefaultServerIndex(serverIndex);
|
||||||
|
}
|
||||||
|
m_connectionUiController->toggleConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::importConfigFromData(const QString &data)
|
void CoreController::importConfigFromData(const QString &data)
|
||||||
|
|||||||
@@ -6,28 +6,47 @@
|
|||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
#include "ui/systemtray_notificationhandler.h"
|
#include "ui/utils/systemTrayNotificationHandler.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "ui/controllers/api/apiConfigsController.h"
|
#include "ui/controllers/api/subscriptionUiController.h"
|
||||||
#include "ui/controllers/api/apiSettingsController.h"
|
#include "ui/controllers/api/apiNewsUiController.h"
|
||||||
#include "ui/controllers/api/apiNewsController.h"
|
#include "ui/controllers/appSplitTunnelingUiController.h"
|
||||||
#include "ui/controllers/appSplitTunnelingController.h"
|
#include "ui/controllers/allowedDnsUiController.h"
|
||||||
#include "ui/controllers/allowedDnsController.h"
|
#include "ui/controllers/connectionUiController.h"
|
||||||
#include "ui/controllers/connectionController.h"
|
#include "ui/controllers/selfhosted/exportUiController.h"
|
||||||
#include "ui/controllers/exportController.h"
|
#include "core/controllers/selfhosted/exportController.h"
|
||||||
#include "ui/controllers/focusController.h"
|
#include "ui/controllers/qml/focusController.h"
|
||||||
#include "ui/controllers/importController.h"
|
#include "ui/controllers/importUiController.h"
|
||||||
#include "ui/controllers/installController.h"
|
#include "core/controllers/selfhosted/importController.h"
|
||||||
#include "ui/controllers/pageController.h"
|
#include "ui/controllers/selfhosted/installUiController.h"
|
||||||
#include "ui/controllers/settingsController.h"
|
#include "ui/controllers/qml/pageController.h"
|
||||||
#include "ui/controllers/sitesController.h"
|
#include "ui/controllers/settingsUiController.h"
|
||||||
|
#include "ui/controllers/serversUiController.h"
|
||||||
|
#include "ui/controllers/ipSplitTunnelingUiController.h"
|
||||||
#include "ui/controllers/systemController.h"
|
#include "ui/controllers/systemController.h"
|
||||||
|
#include "ui/controllers/languageUiController.h"
|
||||||
|
#include "ui/controllers/api/servicesCatalogUiController.h"
|
||||||
|
|
||||||
#include "ui/models/allowed_dns_model.h"
|
#include "core/controllers/serversController.h"
|
||||||
#include "ui/models/containers_model.h"
|
#include "core/controllers/selfhosted/usersController.h"
|
||||||
|
#include "core/controllers/appSplitTunnelingController.h"
|
||||||
|
#include "core/controllers/ipSplitTunnelingController.h"
|
||||||
|
#include "core/controllers/allowedDnsController.h"
|
||||||
|
#include "core/controllers/api/servicesCatalogController.h"
|
||||||
|
#include "core/controllers/api/subscriptionController.h"
|
||||||
|
#include "core/controllers/api/newsController.h"
|
||||||
|
#include "core/controllers/selfhosted/installController.h"
|
||||||
|
#include "core/controllers/settingsController.h"
|
||||||
|
#include "core/controllers/connectionController.h"
|
||||||
|
|
||||||
|
#include "core/repositories/secureServersRepository.h"
|
||||||
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
|
#include "secureQSettings.h"
|
||||||
|
|
||||||
|
#include "ui/models/allowedDnsModel.h"
|
||||||
|
#include "ui/models/containersModel.h"
|
||||||
#include "ui/models/languageModel.h"
|
#include "ui/models/languageModel.h"
|
||||||
#include "ui/models/protocols/cloakConfigModel.h"
|
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
#include "ui/models/protocols/ikev2ConfigModel.h"
|
#include "ui/models/protocols/ikev2ConfigModel.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -41,117 +60,154 @@
|
|||||||
#include "ui/models/clientManagementModel.h"
|
#include "ui/models/clientManagementModel.h"
|
||||||
#include "ui/models/protocols/awgConfigModel.h"
|
#include "ui/models/protocols/awgConfigModel.h"
|
||||||
#include "ui/models/protocols/openvpnConfigModel.h"
|
#include "ui/models/protocols/openvpnConfigModel.h"
|
||||||
#include "ui/models/protocols/shadowsocksConfigModel.h"
|
|
||||||
#include "ui/models/protocols/wireguardConfigModel.h"
|
#include "ui/models/protocols/wireguardConfigModel.h"
|
||||||
#include "ui/models/protocols/xrayConfigModel.h"
|
#include "ui/models/protocols/xrayConfigModel.h"
|
||||||
#include "ui/models/protocols_model.h"
|
#include "ui/models/protocolsModel.h"
|
||||||
#include "ui/models/servers_model.h"
|
#include "ui/models/services/torConfigModel.h"
|
||||||
|
#include "ui/models/serversModel.h"
|
||||||
#include "ui/models/services/sftpConfigModel.h"
|
#include "ui/models/services/sftpConfigModel.h"
|
||||||
#include "ui/models/services/socks5ProxyConfigModel.h"
|
#include "ui/models/services/socks5ProxyConfigModel.h"
|
||||||
#include "ui/models/sites_model.h"
|
#include "ui/models/ipSplitTunnelingModel.h"
|
||||||
#include "ui/models/newsModel.h"
|
#include "ui/models/newsModel.h"
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
#include "ui/notificationhandler.h"
|
#include "ui/utils/notificationHandler.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class CoreSignalHandlers;
|
||||||
|
class TestMultipleImports;
|
||||||
|
class TestAdminSelfHostedExport;
|
||||||
|
class TestServerEdit;
|
||||||
|
class TestDefaultServerChange;
|
||||||
|
class TestServerEdgeCases;
|
||||||
|
class TestSignalOrder;
|
||||||
|
class TestServersModelSync;
|
||||||
|
class TestGatewayStacks;
|
||||||
|
class TestComplexOperations;
|
||||||
|
class TestSettingsSignals;
|
||||||
|
class TestUiServersModelAndController;
|
||||||
|
class TestSelfHostedServerSetup;
|
||||||
|
|
||||||
class CoreController : public QObject
|
class CoreController : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
friend class CoreSignalHandlers;
|
||||||
|
friend class TestMultipleImports;
|
||||||
|
friend class TestAdminSelfHostedExport;
|
||||||
|
friend class TestServerEdit;
|
||||||
|
friend class TestDefaultServerChange;
|
||||||
|
friend class TestServerEdgeCases;
|
||||||
|
friend class TestSignalOrder;
|
||||||
|
friend class TestServersModelSync;
|
||||||
|
friend class TestGatewayStacks;
|
||||||
|
friend class TestComplexOperations;
|
||||||
|
friend class TestSettingsSignals;
|
||||||
|
friend class TestUiServersModelAndController;
|
||||||
|
friend class TestSelfHostedServerSetup;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CoreController(const QSharedPointer<VpnConnection> &vpnConnection, const std::shared_ptr<Settings> &settings,
|
explicit CoreController(const QSharedPointer<VpnConnection> &vpnConnection, SecureQSettings* settings,
|
||||||
QQmlApplicationEngine *engine, QObject *parent = nullptr);
|
QQmlApplicationEngine *engine, QObject *parent = nullptr);
|
||||||
|
|
||||||
QSharedPointer<PageController> pageController() const;
|
PageController* pageController() const;
|
||||||
void setQmlRoot();
|
void setQmlRoot();
|
||||||
|
|
||||||
void openConnectionByIndex(int serverIndex);
|
void openConnectionByIndex(int serverIndex);
|
||||||
void importConfigFromData(const QString &data);
|
void importConfigFromData(const QString &data);
|
||||||
|
void updateTranslator(const QLocale &locale);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void translationsUpdated();
|
void translationsUpdated();
|
||||||
void websiteUrlChanged(const QString &newUrl);
|
void websiteUrlChanged(const QString &newUrl);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void initRepositories();
|
||||||
|
void initCoreControllers();
|
||||||
void initModels();
|
void initModels();
|
||||||
void initControllers();
|
void initControllers();
|
||||||
void initAndroidController();
|
void initAndroidController();
|
||||||
void initAppleController();
|
void initAppleController();
|
||||||
|
void initLogging();
|
||||||
void initSignalHandlers();
|
void initSignalHandlers();
|
||||||
|
void setQmlContextProperty(const QString &name, QObject *value);
|
||||||
void initNotificationHandler();
|
|
||||||
|
|
||||||
void updateTranslator(const QLocale &locale);
|
|
||||||
|
|
||||||
void initErrorMessagesHandler();
|
|
||||||
|
|
||||||
void initApiCountryModelUpdateHandler();
|
|
||||||
void initContainerModelUpdateHandler();
|
|
||||||
void initAdminConfigRevokedHandler();
|
|
||||||
void initPassphraseRequestHandler();
|
|
||||||
void initTranslationsUpdatedHandler();
|
|
||||||
void initAutoConnectHandler();
|
|
||||||
void initAmneziaDnsToggledHandler();
|
|
||||||
void initPrepareConfigHandler();
|
|
||||||
void initStrictKillSwitchHandler();
|
|
||||||
|
|
||||||
QQmlApplicationEngine *m_engine {}; // TODO use parent child system here?
|
QQmlApplicationEngine *m_engine {}; // TODO use parent child system here?
|
||||||
std::shared_ptr<Settings> m_settings;
|
SecureQSettings* m_settings;
|
||||||
QSharedPointer<VpnConnection> m_vpnConnection;
|
QSharedPointer<VpnConnection> m_vpnConnection;
|
||||||
QSharedPointer<QTranslator> m_translator;
|
QTranslator* m_translator;
|
||||||
|
|
||||||
|
SecureServersRepository* m_serversRepository;
|
||||||
|
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
QScopedPointer<NotificationHandler> m_notificationHandler;
|
NotificationHandler* m_notificationHandler;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QMetaObject::Connection m_reloadConfigErrorOccurredConnection;
|
QMetaObject::Connection m_reloadConfigErrorOccurredConnection;
|
||||||
|
|
||||||
QScopedPointer<ConnectionController> m_connectionController;
|
ConnectionUiController* m_connectionUiController;
|
||||||
QScopedPointer<FocusController> m_focusController;
|
FocusController* m_focusController;
|
||||||
QSharedPointer<PageController> m_pageController; // TODO
|
PageController* m_pageController;
|
||||||
QScopedPointer<InstallController> m_installController;
|
InstallUiController* m_installUiController;
|
||||||
QScopedPointer<ImportController> m_importController;
|
ImportUiController* m_importController;
|
||||||
QScopedPointer<ExportController> m_exportController;
|
ImportController* m_importCoreController;
|
||||||
QScopedPointer<SettingsController> m_settingsController;
|
ExportUiController* m_exportUiController;
|
||||||
QScopedPointer<SitesController> m_sitesController;
|
SettingsUiController* m_settingsUiController;
|
||||||
QScopedPointer<SystemController> m_systemController;
|
ServersUiController* m_serversUiController;
|
||||||
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
|
IpSplitTunnelingUiController* m_ipSplitTunnelingUiController;
|
||||||
QScopedPointer<AllowedDnsController> m_allowedDnsController;
|
SystemController* m_systemController;
|
||||||
|
AppSplitTunnelingUiController* m_appSplitTunnelingUiController;
|
||||||
|
AllowedDnsUiController* m_allowedDnsUiController;
|
||||||
|
LanguageUiController* m_languageUiController;
|
||||||
|
|
||||||
QScopedPointer<ApiSettingsController> m_apiSettingsController;
|
SubscriptionUiController* m_subscriptionUiController;
|
||||||
QScopedPointer<ApiConfigsController> m_apiConfigsController;
|
ApiNewsUiController* m_apiNewsUiController;
|
||||||
QScopedPointer<ApiNewsController> m_apiNewsController;
|
|
||||||
|
ServicesCatalogUiController* m_servicesCatalogUiController;
|
||||||
|
|
||||||
QSharedPointer<ContainersModel> m_containersModel;
|
ServersController* m_serversController;
|
||||||
QSharedPointer<ContainersModel> m_defaultServerContainersModel;
|
UsersController* m_usersController;
|
||||||
QSharedPointer<ServersModel> m_serversModel;
|
AppSplitTunnelingController* m_appSplitTunnelingController;
|
||||||
QSharedPointer<LanguageModel> m_languageModel;
|
IpSplitTunnelingController* m_ipSplitTunnelingController;
|
||||||
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
AllowedDnsController* m_allowedDnsController;
|
||||||
QSharedPointer<SitesModel> m_sitesModel;
|
ServicesCatalogController* m_servicesCatalogController;
|
||||||
QSharedPointer<NewsModel> m_newsModel;
|
SubscriptionController* m_subscriptionController;
|
||||||
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
|
NewsController* m_newsController;
|
||||||
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
|
InstallController* m_installController;
|
||||||
QSharedPointer<ClientManagementModel> m_clientManagementModel;
|
ExportController* m_exportController;
|
||||||
|
ConnectionController* m_connectionController;
|
||||||
|
SettingsController* m_settingsController;
|
||||||
|
|
||||||
QSharedPointer<ApiServicesModel> m_apiServicesModel;
|
ContainersModel* m_containersModel;
|
||||||
QSharedPointer<ApiSubscriptionPlansModel> m_apiSubscriptionPlansModel;
|
ContainersModel* m_defaultServerContainersModel;
|
||||||
QSharedPointer<ApiBenefitsModel> m_apiBenefitsModel;
|
ServersModel* m_serversModel;
|
||||||
QSharedPointer<ApiCountryModel> m_apiCountryModel;
|
LanguageModel* m_languageModel;
|
||||||
QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel;
|
ProtocolsModel* m_protocolsModel;
|
||||||
QSharedPointer<ApiDevicesModel> m_apiDevicesModel;
|
IpSplitTunnelingModel* m_ipSplitTunnelingModel;
|
||||||
|
NewsModel* m_newsModel;
|
||||||
|
AllowedDnsModel* m_allowedDnsModel;
|
||||||
|
AppSplitTunnelingModel* m_appSplitTunnelingModel;
|
||||||
|
ClientManagementModel* m_clientManagementModel;
|
||||||
|
|
||||||
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
|
ApiServicesModel* m_apiServicesModel;
|
||||||
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
|
ApiSubscriptionPlansModel* m_apiSubscriptionPlansModel;
|
||||||
QScopedPointer<CloakConfigModel> m_cloakConfigModel;
|
ApiBenefitsModel* m_apiBenefitsModel;
|
||||||
QScopedPointer<XrayConfigModel> m_xrayConfigModel;
|
ApiCountryModel* m_apiCountryModel;
|
||||||
QScopedPointer<WireGuardConfigModel> m_wireGuardConfigModel;
|
ApiAccountInfoModel* m_apiAccountInfoModel;
|
||||||
QScopedPointer<AwgConfigModel> m_awgConfigModel;
|
ApiDevicesModel* m_apiDevicesModel;
|
||||||
|
|
||||||
|
OpenVpnConfigModel* m_openVpnConfigModel;
|
||||||
|
XrayConfigModel* m_xrayConfigModel;
|
||||||
|
TorConfigModel* m_torConfigModel;
|
||||||
|
WireGuardConfigModel* m_wireGuardConfigModel;
|
||||||
|
AwgConfigModel* m_awgConfigModel;
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
QScopedPointer<Ikev2ConfigModel> m_ikev2ConfigModel;
|
Ikev2ConfigModel* m_ikev2ConfigModel;
|
||||||
#endif
|
#endif
|
||||||
QScopedPointer<SftpConfigModel> m_sftpConfigModel;
|
SftpConfigModel* m_sftpConfigModel;
|
||||||
QScopedPointer<Socks5ProxyConfigModel> m_socks5ConfigModel;
|
Socks5ProxyConfigModel* m_socks5ConfigModel;
|
||||||
|
|
||||||
|
CoreSignalHandlers* m_signalHandlers;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CORECONTROLLER_H
|
#endif // CORECONTROLLER_H
|
||||||
|
|||||||
412
client/core/controllers/coreSignalHandlers.cpp
Normal file
412
client/core/controllers/coreSignalHandlers.cpp
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
#include "coreSignalHandlers.h"
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/controllers/coreController.h"
|
||||||
|
#include "core/repositories/secureServersRepository.h"
|
||||||
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
|
#include "vpnConnection.h"
|
||||||
|
#include "ui/controllers/qml/pageController.h"
|
||||||
|
#include "ui/controllers/connectionUiController.h"
|
||||||
|
#include "ui/controllers/settingsUiController.h"
|
||||||
|
#include "ui/controllers/serversUiController.h"
|
||||||
|
#include "ui/controllers/ipSplitTunnelingUiController.h"
|
||||||
|
#include "ui/controllers/allowedDnsUiController.h"
|
||||||
|
#include "ui/controllers/appSplitTunnelingUiController.h"
|
||||||
|
#include "ui/controllers/languageUiController.h"
|
||||||
|
#include "ui/controllers/selfhosted/installUiController.h"
|
||||||
|
#include "ui/controllers/importUiController.h"
|
||||||
|
#include "ui/controllers/api/subscriptionUiController.h"
|
||||||
|
#include "ui/models/serversModel.h"
|
||||||
|
#include "core/controllers/serversController.h"
|
||||||
|
#include "core/controllers/ipSplitTunnelingController.h"
|
||||||
|
#include "core/controllers/appSplitTunnelingController.h"
|
||||||
|
#include "core/controllers/selfhosted/usersController.h"
|
||||||
|
#include "core/controllers/settingsController.h"
|
||||||
|
#include "core/controllers/selfhosted/installController.h"
|
||||||
|
#include "core/controllers/selfhosted/exportController.h"
|
||||||
|
#include "core/controllers/connectionController.h"
|
||||||
|
#include "ui/models/clientManagementModel.h"
|
||||||
|
#include "ui/controllers/api/apiNewsUiController.h"
|
||||||
|
#include "ui/models/api/apiCountryModel.h"
|
||||||
|
#include "ui/models/containersModel.h"
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
|
||||||
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
|
#include "ui/utils/notificationHandler.h"
|
||||||
|
#include "ui/utils/systemTrayNotificationHandler.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
#include "platforms/android/android_controller.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_IOS
|
||||||
|
#include "platforms/ios/ios_controller.h"
|
||||||
|
#include <AmneziaVPN-Swift.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CoreSignalHandlers::CoreSignalHandlers(CoreController* coreController, QObject* parent)
|
||||||
|
: QObject(parent),
|
||||||
|
m_coreController(coreController)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initAllHandlers()
|
||||||
|
{
|
||||||
|
initErrorMessagesHandler();
|
||||||
|
initSettingsSplitTunnelingHandler();
|
||||||
|
initInstallControllerHandler();
|
||||||
|
initExportControllerHandler();
|
||||||
|
initImportControllerHandler();
|
||||||
|
initApiCountryModelUpdateHandler();
|
||||||
|
initSubscriptionRefreshHandler();
|
||||||
|
initContainerModelUpdateHandler();
|
||||||
|
initAdminConfigRevokedHandler();
|
||||||
|
initPassphraseRequestHandler();
|
||||||
|
initTranslationsUpdatedHandler();
|
||||||
|
initLanguageHandler();
|
||||||
|
initAutoConnectHandler();
|
||||||
|
initAmneziaDnsToggledHandler();
|
||||||
|
initServersModelUpdateHandler();
|
||||||
|
initClientManagementModelUpdateHandler();
|
||||||
|
initSitesModelUpdateHandler();
|
||||||
|
initAllowedDnsModelUpdateHandler();
|
||||||
|
initAppSplitTunnelingModelUpdateHandler();
|
||||||
|
initPrepareConfigHandler();
|
||||||
|
initStrictKillSwitchHandler();
|
||||||
|
initAndroidSettingsHandler();
|
||||||
|
initAndroidConnectionHandler();
|
||||||
|
initIosImportHandler();
|
||||||
|
initIosSettingsHandler();
|
||||||
|
initNotificationHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initErrorMessagesHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_connectionUiController, &ConnectionUiController::connectionErrorOccurred, this, [this](ErrorCode errorCode) {
|
||||||
|
emit m_coreController->m_pageController->showErrorMessage(errorCode);
|
||||||
|
m_coreController->m_connectionController->setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::errorOccurred, m_coreController->m_pageController,
|
||||||
|
qOverload<ErrorCode>(&PageController::showErrorMessage));
|
||||||
|
|
||||||
|
connect(m_coreController->m_settingsUiController, &SettingsUiController::errorOccurred, m_coreController->m_pageController,
|
||||||
|
qOverload<ErrorCode>(&PageController::showErrorMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initSettingsSplitTunnelingHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_settingsController, &SettingsController::siteSplitTunnelingRouteModeChanged, this, [this](RouteMode mode) {
|
||||||
|
m_coreController->m_ipSplitTunnelingController->setRouteMode(mode);
|
||||||
|
});
|
||||||
|
connect(m_coreController->m_settingsController, &SettingsController::siteSplitTunnelingToggled, this, [this](bool enabled) {
|
||||||
|
m_coreController->m_ipSplitTunnelingController->toggleSplitTunneling(enabled);
|
||||||
|
});
|
||||||
|
connect(m_coreController->m_settingsController, &SettingsController::appSplitTunnelingRouteModeChanged, this, [this](AppsRouteMode mode) {
|
||||||
|
m_coreController->m_appSplitTunnelingController->setRouteMode(mode);
|
||||||
|
});
|
||||||
|
connect(m_coreController->m_settingsController, &SettingsController::appSplitTunnelingToggled, this, [this](bool enabled) {
|
||||||
|
m_coreController->m_appSplitTunnelingController->toggleSplitTunneling(enabled);
|
||||||
|
});
|
||||||
|
connect(m_coreController->m_settingsController, &SettingsController::appSplitTunnelingClearAppsList, this, [this]() {
|
||||||
|
m_coreController->m_appSplitTunnelingController->clearAppsList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initInstallControllerHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_installController, &InstallController::serverIsBusy, m_coreController->m_installUiController, &InstallUiController::serverIsBusy);
|
||||||
|
connect(m_coreController->m_installUiController, &InstallUiController::cancelInstallation, m_coreController->m_installController, &InstallController::cancelInstallation);
|
||||||
|
connect(m_coreController->m_installUiController, &InstallUiController::currentContainerUpdated, m_coreController->m_connectionUiController,
|
||||||
|
&ConnectionUiController::onCurrentContainerUpdated);
|
||||||
|
connect(m_coreController->m_serversUiController, &ServersUiController::processedServerIndexChanged,
|
||||||
|
m_coreController->m_installUiController, [this](int index) {
|
||||||
|
if (index >= 0) {
|
||||||
|
m_coreController->m_installUiController->clearProcessedServerCredentials();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initExportControllerHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_exportController, &ExportController::appendClientRequested, this,
|
||||||
|
[this](int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container) {
|
||||||
|
m_coreController->m_usersController->appendClient(serverIndex, clientId, clientName, container);
|
||||||
|
});
|
||||||
|
connect(m_coreController->m_exportController, &ExportController::updateClientsRequested, this,
|
||||||
|
[this](int serverIndex, DockerContainer container) {
|
||||||
|
m_coreController->m_usersController->updateClients(serverIndex, container);
|
||||||
|
});
|
||||||
|
connect(m_coreController->m_exportController, &ExportController::revokeClientRequested, this,
|
||||||
|
[this](int serverIndex, int row, DockerContainer container) {
|
||||||
|
m_coreController->m_usersController->revokeClient(serverIndex, row, container);
|
||||||
|
});
|
||||||
|
connect(m_coreController->m_exportController, &ExportController::renameClientRequested, this,
|
||||||
|
[this](int serverIndex, int row, const QString &clientName, DockerContainer container) {
|
||||||
|
m_coreController->m_usersController->renameClient(serverIndex, row, clientName, container);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initImportControllerHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_importCoreController, &ImportController::importFinished, this, [this]() {
|
||||||
|
if (!m_coreController->m_connectionController->isConnected()) {
|
||||||
|
int newServerIndex = m_coreController->m_serversController->getServersCount() - 1;
|
||||||
|
m_coreController->m_serversController->setDefaultServerIndex(newServerIndex);
|
||||||
|
if (m_coreController->m_serversUiController) {
|
||||||
|
m_coreController->m_serversUiController->setProcessedServerIndex(newServerIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initApiCountryModelUpdateHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_serversUiController, &ServersUiController::updateApiCountryModel, this, [this]() {
|
||||||
|
int processedIndex = m_coreController->m_serversUiController->getProcessedServerIndex();
|
||||||
|
if (processedIndex < 0 || processedIndex >= m_coreController->m_serversRepository->serversCount()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerConfig server = m_coreController->m_serversRepository->server(processedIndex);
|
||||||
|
QJsonArray availableCountries;
|
||||||
|
QString serverCountryCode;
|
||||||
|
|
||||||
|
if (server.isApiV2()) {
|
||||||
|
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
|
||||||
|
if (apiV2) {
|
||||||
|
availableCountries = apiV2->apiConfig.availableCountries;
|
||||||
|
serverCountryCode = apiV2->apiConfig.serverCountryCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_coreController->m_apiCountryModel->updateModel(availableCountries, serverCountryCode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initSubscriptionRefreshHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::subscriptionRefreshNeeded, this, [this]() {
|
||||||
|
const int defaultServerIndex = m_coreController->m_serversController->getDefaultServerIndex();
|
||||||
|
if (defaultServerIndex >= 0) {
|
||||||
|
m_coreController->m_subscriptionUiController->getAccountInfo(defaultServerIndex, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initContainerModelUpdateHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_serversController, &ServersController::gatewayStacksExpanded, this, [this]() {
|
||||||
|
if (m_coreController->m_serversUiController->hasServersFromGatewayApi()) {
|
||||||
|
m_coreController->m_apiNewsUiController->fetchNews(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initAdminConfigRevokedHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_installController, &InstallController::clientRevocationRequested, this,
|
||||||
|
[this](int serverIndex, const ContainerConfig &containerConfig, DockerContainer container) {
|
||||||
|
m_coreController->m_usersController->revokeClient(serverIndex, containerConfig, container);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_coreController->m_installController, &InstallController::clientAppendRequested, this,
|
||||||
|
[this](int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container) {
|
||||||
|
m_coreController->m_usersController->appendClient(serverIndex, clientId, clientName, container);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_coreController->m_usersController, &UsersController::adminConfigRevoked, m_coreController->m_serversController,
|
||||||
|
&ServersController::clearCachedProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initPassphraseRequestHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_installUiController, &InstallUiController::passphraseRequestStarted, m_coreController->m_pageController,
|
||||||
|
&PageController::showPassphraseRequestDrawer);
|
||||||
|
connect(m_coreController->m_pageController, &PageController::passphraseRequestDrawerClosed, m_coreController->m_installUiController,
|
||||||
|
&InstallUiController::setEncryptedPassphrase);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initTranslationsUpdatedHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_languageUiController, &LanguageUiController::updateTranslations, m_coreController, &CoreController::updateTranslator);
|
||||||
|
connect(m_coreController, &CoreController::translationsUpdated, m_coreController->m_languageUiController, &LanguageUiController::translationsUpdated);
|
||||||
|
connect(m_coreController, &CoreController::translationsUpdated, m_coreController->m_connectionUiController, &ConnectionUiController::onTranslationsUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initLanguageHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::appLanguageChanged, m_coreController->m_languageUiController, &LanguageUiController::onAppLanguageChanged);
|
||||||
|
connect(m_coreController->m_settingsUiController, &SettingsUiController::resetLanguageToSystem, m_coreController->m_languageUiController, [this]() {
|
||||||
|
m_coreController->m_languageUiController->changeLanguage(m_coreController->m_languageUiController->getSystemLanguageEnum());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initAutoConnectHandler()
|
||||||
|
{
|
||||||
|
if (m_coreController->m_settingsUiController->isAutoConnectEnabled() && m_coreController->m_serversController->getDefaultServerIndex() >= 0) {
|
||||||
|
QTimer::singleShot(1000, this, [this]() { m_coreController->m_connectionUiController->openConnection(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initAmneziaDnsToggledHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::useAmneziaDnsChanged, m_coreController->m_serversUiController, &ServersUiController::updateModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initServersModelUpdateHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded,
|
||||||
|
m_coreController->m_serversUiController, &ServersUiController::updateModel);
|
||||||
|
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverEdited,
|
||||||
|
m_coreController->m_serversUiController, &ServersUiController::updateModel);
|
||||||
|
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved,
|
||||||
|
m_coreController->m_serversUiController, &ServersUiController::updateModel);
|
||||||
|
connect(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged,
|
||||||
|
m_coreController->m_serversUiController, &ServersUiController::onDefaultServerChanged);
|
||||||
|
|
||||||
|
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded,
|
||||||
|
m_coreController->m_serversController, &ServersController::recomputeGatewayStacks);
|
||||||
|
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverEdited,
|
||||||
|
m_coreController->m_serversController, &ServersController::recomputeGatewayStacks);
|
||||||
|
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved,
|
||||||
|
m_coreController->m_serversController, &ServersController::recomputeGatewayStacks);
|
||||||
|
|
||||||
|
connect(m_coreController->m_settingsUiController, &SettingsUiController::restoreBackupFinished,
|
||||||
|
m_coreController->m_serversUiController, &ServersUiController::updateModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initClientManagementModelUpdateHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_usersController, &UsersController::clientsUpdated,
|
||||||
|
m_coreController->m_clientManagementModel, &ClientManagementModel::updateModel);
|
||||||
|
connect(m_coreController->m_usersController, &UsersController::clientRenamed,
|
||||||
|
m_coreController->m_clientManagementModel, &ClientManagementModel::updateClientName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initSitesModelUpdateHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::sitesChanged, m_coreController->m_ipSplitTunnelingUiController, &IpSplitTunnelingUiController::updateModel);
|
||||||
|
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::sitesSplitTunnelingEnabledChanged, m_coreController->m_ipSplitTunnelingUiController, &IpSplitTunnelingUiController::updateModel);
|
||||||
|
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::routeModeChanged, m_coreController->m_ipSplitTunnelingUiController, &IpSplitTunnelingUiController::updateModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initAllowedDnsModelUpdateHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::allowedDnsServersChanged, m_coreController->m_allowedDnsUiController, &AllowedDnsUiController::updateModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initAppSplitTunnelingModelUpdateHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::appsChanged, m_coreController->m_appSplitTunnelingUiController, &AppSplitTunnelingUiController::updateModel);
|
||||||
|
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::appsSplitTunnelingEnabledChanged, m_coreController->m_appSplitTunnelingUiController, &AppSplitTunnelingUiController::updateModel);
|
||||||
|
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::appsRouteModeChanged, m_coreController->m_appSplitTunnelingUiController, &AppSplitTunnelingUiController::updateModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initPrepareConfigHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_connectionUiController, &ConnectionUiController::prepareConfig, this, [this]() {
|
||||||
|
m_coreController->m_connectionController->setConnectionState(Vpn::ConnectionState::Preparing);
|
||||||
|
|
||||||
|
m_coreController->m_subscriptionUiController->validateConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::configValidated, this, [this](bool isValid) {
|
||||||
|
if (!isValid) {
|
||||||
|
m_coreController->m_connectionController->setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_coreController->m_installUiController->validateConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_coreController->m_installUiController, &InstallUiController::configValidated, this, [this](bool isValid) {
|
||||||
|
if (!isValid) {
|
||||||
|
m_coreController->m_connectionController->setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_coreController->m_connectionUiController->openConnection();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initStrictKillSwitchHandler()
|
||||||
|
{
|
||||||
|
connect(m_coreController->m_settingsUiController, &SettingsUiController::strictKillSwitchEnabledChanged, m_coreController->m_connectionController,
|
||||||
|
&ConnectionController::onKillSwitchModeChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initAndroidSettingsHandler()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::saveLogsChanged, AndroidController::instance(), &AndroidController::setSaveLogs);
|
||||||
|
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::screenshotsEnabledChanged, AndroidController::instance(), &AndroidController::setScreenshotsEnabled);
|
||||||
|
connect(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved, AndroidController::instance(), &AndroidController::resetLastServer);
|
||||||
|
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::settingsCleared, []() { AndroidController::instance()->resetLastServer(-1); });
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initAndroidConnectionHandler()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
connect(AndroidController::instance(), &AndroidController::initConnectionState, this, [this](Vpn::ConnectionState state) {
|
||||||
|
m_coreController->m_connectionUiController->onConnectionStateChanged(state);
|
||||||
|
m_coreController->m_connectionController->restoreConnection();
|
||||||
|
});
|
||||||
|
connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, this, [this](QString data) {
|
||||||
|
emit m_coreController->m_pageController->goToPageHome();
|
||||||
|
m_coreController->m_importController->extractConfigFromData(data);
|
||||||
|
data.clear();
|
||||||
|
emit m_coreController->m_pageController->goToPageViewConfig();
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initIosImportHandler()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_IOS
|
||||||
|
connect(IosController::Instance(), &IosController::importConfigFromOutside, this, [this](QString data) {
|
||||||
|
emit m_coreController->m_pageController->goToPageHome();
|
||||||
|
m_coreController->m_importController->extractConfigFromData(data);
|
||||||
|
emit m_coreController->m_pageController->goToPageViewConfig();
|
||||||
|
});
|
||||||
|
connect(IosController::Instance(), &IosController::importBackupFromOutside, this, [this](QString filePath) {
|
||||||
|
emit m_coreController->m_pageController->goToPageHome();
|
||||||
|
m_coreController->m_pageController->goToPageSettingsBackup();
|
||||||
|
emit m_coreController->m_settingsUiController->importBackupFromOutside(filePath);
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initIosSettingsHandler()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_IOS
|
||||||
|
connect(m_coreController->m_appSettingsRepository, &SecureAppSettingsRepository::screenshotsEnabledChanged, [](bool enabled) { AmneziaVPN::toggleScreenshots(enabled); });
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreSignalHandlers::initNotificationHandler()
|
||||||
|
{
|
||||||
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
|
m_coreController->m_notificationHandler = NotificationHandler::create(m_coreController);
|
||||||
|
|
||||||
|
connect(m_coreController->m_connectionController, &ConnectionController::connectionStateChanged, m_coreController->m_notificationHandler,
|
||||||
|
&NotificationHandler::setConnectionState);
|
||||||
|
|
||||||
|
connect(m_coreController->m_notificationHandler, &NotificationHandler::raiseRequested, m_coreController->m_pageController, &PageController::raiseMainWindow);
|
||||||
|
connect(m_coreController->m_notificationHandler, &NotificationHandler::connectRequested, m_coreController->m_connectionUiController,
|
||||||
|
static_cast<void (ConnectionUiController::*)()>(&ConnectionUiController::openConnection));
|
||||||
|
connect(m_coreController->m_notificationHandler, &NotificationHandler::disconnectRequested, m_coreController->m_connectionUiController,
|
||||||
|
&ConnectionUiController::closeConnection);
|
||||||
|
connect(m_coreController, &CoreController::translationsUpdated, m_coreController->m_notificationHandler, &NotificationHandler::onTranslationsUpdated);
|
||||||
|
|
||||||
|
auto* trayHandler = qobject_cast<SystemTrayNotificationHandler*>(m_coreController->m_notificationHandler);
|
||||||
|
connect(m_coreController, &CoreController::websiteUrlChanged, trayHandler, &SystemTrayNotificationHandler::updateWebsiteUrl);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
48
client/core/controllers/coreSignalHandlers.h
Normal file
48
client/core/controllers/coreSignalHandlers.h
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#ifndef CORESIGNALHANDLERS_H
|
||||||
|
#define CORESIGNALHANDLERS_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include "core/controllers/coreController.h"
|
||||||
|
|
||||||
|
class CoreSignalHandlers : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CoreSignalHandlers(CoreController* coreController, QObject* parent = nullptr);
|
||||||
|
|
||||||
|
void initAllHandlers();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initErrorMessagesHandler();
|
||||||
|
void initSettingsSplitTunnelingHandler();
|
||||||
|
void initInstallControllerHandler();
|
||||||
|
void initExportControllerHandler();
|
||||||
|
void initImportControllerHandler();
|
||||||
|
void initApiCountryModelUpdateHandler();
|
||||||
|
void initSubscriptionRefreshHandler();
|
||||||
|
void initContainerModelUpdateHandler();
|
||||||
|
void initAdminConfigRevokedHandler();
|
||||||
|
void initPassphraseRequestHandler();
|
||||||
|
void initTranslationsUpdatedHandler();
|
||||||
|
void initLanguageHandler();
|
||||||
|
void initAutoConnectHandler();
|
||||||
|
void initAmneziaDnsToggledHandler();
|
||||||
|
void initServersModelUpdateHandler();
|
||||||
|
void initClientManagementModelUpdateHandler();
|
||||||
|
void initSitesModelUpdateHandler();
|
||||||
|
void initAllowedDnsModelUpdateHandler();
|
||||||
|
void initAppSplitTunnelingModelUpdateHandler();
|
||||||
|
void initPrepareConfigHandler();
|
||||||
|
void initStrictKillSwitchHandler();
|
||||||
|
void initAndroidSettingsHandler();
|
||||||
|
void initAndroidConnectionHandler();
|
||||||
|
void initIosImportHandler();
|
||||||
|
void initIosSettingsHandler();
|
||||||
|
void initNotificationHandler();
|
||||||
|
|
||||||
|
CoreController* m_coreController;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CORESIGNALHANDLERS_H
|
||||||
|
|
||||||
@@ -15,27 +15,18 @@
|
|||||||
#include "QBlockCipher.h"
|
#include "QBlockCipher.h"
|
||||||
#include "QRsa.h"
|
#include "QRsa.h"
|
||||||
|
|
||||||
#include "amnezia_application.h"
|
#include "amneziaApplication.h"
|
||||||
#include "core/api/apiUtils.h"
|
#include "core/utils/api/apiUtils.h"
|
||||||
#include "core/networkUtilities.h"
|
#include "core/utils/constants/apiKeys.h"
|
||||||
#include "utilities.h"
|
#include "core/utils/networkUtilities.h"
|
||||||
|
#include "core/utils/utilities.h"
|
||||||
|
|
||||||
#ifdef AMNEZIA_DESKTOP
|
#ifdef AMNEZIA_DESKTOP
|
||||||
#include "core/ipcclient.h"
|
#include "core/utils/ipcClient.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
namespace configKey
|
|
||||||
{
|
|
||||||
constexpr char aesKey[] = "aes_key";
|
|
||||||
constexpr char aesIv[] = "aes_iv";
|
|
||||||
constexpr char aesSalt[] = "aes_salt";
|
|
||||||
|
|
||||||
constexpr char apiPayload[] = "api_payload";
|
|
||||||
constexpr char keyPayload[] = "key_payload";
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr QLatin1String errorResponsePattern1("No active configuration found for");
|
constexpr QLatin1String errorResponsePattern1("No active configuration found for");
|
||||||
constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for");
|
constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for");
|
||||||
constexpr QLatin1String errorResponsePattern3("Account not found.");
|
constexpr QLatin1String errorResponsePattern3("Account not found.");
|
||||||
@@ -99,9 +90,9 @@ GatewayController::EncryptedRequestData GatewayController::prepareRequest(const
|
|||||||
encRequestData.salt = blockCipher.generatePrivateSalt(8);
|
encRequestData.salt = blockCipher.generatePrivateSalt(8);
|
||||||
|
|
||||||
QJsonObject keyPayload;
|
QJsonObject keyPayload;
|
||||||
keyPayload[configKey::aesKey] = QString(encRequestData.key.toBase64());
|
keyPayload[apiDefs::key::aesKey] = QString(encRequestData.key.toBase64());
|
||||||
keyPayload[configKey::aesIv] = QString(encRequestData.iv.toBase64());
|
keyPayload[apiDefs::key::aesIv] = QString(encRequestData.iv.toBase64());
|
||||||
keyPayload[configKey::aesSalt] = QString(encRequestData.salt.toBase64());
|
keyPayload[apiDefs::key::aesSalt] = QString(encRequestData.salt.toBase64());
|
||||||
|
|
||||||
QByteArray encryptedKeyPayload;
|
QByteArray encryptedKeyPayload;
|
||||||
QByteArray encryptedApiPayload;
|
QByteArray encryptedApiPayload;
|
||||||
@@ -133,8 +124,8 @@ GatewayController::EncryptedRequestData GatewayController::prepareRequest(const
|
|||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject requestBody;
|
QJsonObject requestBody;
|
||||||
requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
|
requestBody[apiDefs::key::keyPayload] = QString(encryptedKeyPayload.toBase64());
|
||||||
requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
|
requestBody[apiDefs::key::apiPayload] = QString(encryptedApiPayload.toBase64());
|
||||||
|
|
||||||
encRequestData.requestBody = QJsonDocument(requestBody).toJson();
|
encRequestData.requestBody = QJsonDocument(requestBody).toJson();
|
||||||
return encRequestData;
|
return encRequestData;
|
||||||
@@ -294,6 +285,9 @@ QFuture<QPair<ErrorCode, QByteArray>> GatewayController::postAsync(const QString
|
|||||||
primaryBaseUrls = QString(PROD_S3_ENDPOINT).split(", ", Qt::SkipEmptyParts);
|
primaryBaseUrls = QString(PROD_S3_ENDPOINT).split(", ", Qt::SkipEmptyParts);
|
||||||
fallbackBaseUrls = QString(FALLBACK_S3_ENDPOINT).split(", ", Qt::SkipEmptyParts);
|
fallbackBaseUrls = QString(FALLBACK_S3_ENDPOINT).split(", ", Qt::SkipEmptyParts);
|
||||||
}
|
}
|
||||||
|
std::random_device randomDevice;
|
||||||
|
std::mt19937 generator(randomDevice());
|
||||||
|
std::shuffle(baseUrls.begin(), baseUrls.end(), generator);
|
||||||
|
|
||||||
auto appendStorageUrls = [&serviceType, &userCountryCode](const QStringList &baseUrls, QStringList &target) {
|
auto appendStorageUrls = [&serviceType, &userCountryCode](const QStringList &baseUrls, QStringList &target) {
|
||||||
if (!serviceType.isEmpty()) {
|
if (!serviceType.isEmpty()) {
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
#include <QPromise>
|
#include <QPromise>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
|
||||||
#include "core/defs.h"
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#ifdef Q_OS_IOS
|
||||||
#include "platforms/ios/ios_controller.h"
|
#include "platforms/ios/ios_controller.h"
|
||||||
|
|||||||
245
client/core/controllers/ipSplitTunnelingController.cpp
Normal file
245
client/core/controllers/ipSplitTunnelingController.cpp
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
#include "ipSplitTunnelingController.h"
|
||||||
|
#include "core/utils/networkUtilities.h"
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
IpSplitTunnelingController::IpSplitTunnelingController(SecureAppSettingsRepository* appSettingsRepository, QObject* parent)
|
||||||
|
: QObject(parent),
|
||||||
|
m_appSettingsRepository(appSettingsRepository)
|
||||||
|
{
|
||||||
|
m_currentRouteMode = m_appSettingsRepository->routeMode();
|
||||||
|
if (m_currentRouteMode == RouteMode::VpnAllSites) { // for old split tunneling configs
|
||||||
|
m_appSettingsRepository->setRouteMode(RouteMode::VpnOnlyForwardSites);
|
||||||
|
m_currentRouteMode = RouteMode::VpnOnlyForwardSites;
|
||||||
|
}
|
||||||
|
fillSites();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IpSplitTunnelingController::addSiteInternal(const QString &hostname, const QString &ip)
|
||||||
|
{
|
||||||
|
QVariantMap existing = m_appSettingsRepository->vpnSites(m_currentRouteMode);
|
||||||
|
if (existing.contains(hostname) && ip.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < m_sites.size(); i++) {
|
||||||
|
if (m_sites[i].first == hostname && (m_sites[i].second.isEmpty() && !ip.isEmpty())) {
|
||||||
|
m_sites[i].second = ip;
|
||||||
|
m_appSettingsRepository->addVpnSite(m_currentRouteMode, hostname, ip);
|
||||||
|
return true;
|
||||||
|
} else if (m_sites[i].first == hostname && (m_sites[i].second == ip)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_sites.append(qMakePair(hostname, ip));
|
||||||
|
m_appSettingsRepository->addVpnSite(m_currentRouteMode, hostname, ip);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpSplitTunnelingController::addSites(const QMap<QString, QString> &sites, bool replaceExisting)
|
||||||
|
{
|
||||||
|
if (replaceExisting) {
|
||||||
|
m_sites.clear();
|
||||||
|
}
|
||||||
|
for (auto it = sites.constBegin(); it != sites.constEnd(); ++it) {
|
||||||
|
const QString &hostname = it.key();
|
||||||
|
const QString &ip = it.value();
|
||||||
|
bool found = false;
|
||||||
|
for (int i = 0; i < m_sites.size(); i++) {
|
||||||
|
if (m_sites[i].first == hostname) {
|
||||||
|
if (!ip.isEmpty()) {
|
||||||
|
m_sites[i].second = ip;
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
m_sites.append(qMakePair(hostname, ip));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (replaceExisting) {
|
||||||
|
m_appSettingsRepository->removeAllVpnSites(m_currentRouteMode);
|
||||||
|
}
|
||||||
|
m_appSettingsRepository->addVpnSites(m_currentRouteMode, sites);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IpSplitTunnelingController::addSite(const QString &hostname)
|
||||||
|
{
|
||||||
|
QString normalizedHostname = normalizeHostname(hostname);
|
||||||
|
|
||||||
|
if (!validateHostname(normalizedHostname)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(normalizedHostname)) {
|
||||||
|
processSite(normalizedHostname, "");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addSiteInternal(normalizedHostname, "")) {
|
||||||
|
QHostInfo::lookupHost(normalizedHostname, this, SLOT(onHostResolved(QHostInfo)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IpSplitTunnelingController::removeSite(const QString &hostname)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_sites.size(); i++) {
|
||||||
|
if (m_sites[i].first == hostname) {
|
||||||
|
m_sites.removeAt(i);
|
||||||
|
m_appSettingsRepository->removeVpnSite(m_currentRouteMode, hostname);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpSplitTunnelingController::removeSites()
|
||||||
|
{
|
||||||
|
m_sites.clear();
|
||||||
|
m_appSettingsRepository->removeAllVpnSites(m_currentRouteMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpSplitTunnelingController::setRouteMode(RouteMode routeMode)
|
||||||
|
{
|
||||||
|
m_currentRouteMode = routeMode;
|
||||||
|
fillSites();
|
||||||
|
m_appSettingsRepository->setRouteMode(routeMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpSplitTunnelingController::toggleSplitTunneling(bool enabled)
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->setSitesSplitTunnelingEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
RouteMode IpSplitTunnelingController::getRouteMode() const
|
||||||
|
{
|
||||||
|
return m_currentRouteMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IpSplitTunnelingController::isSplitTunnelingEnabled() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->isSitesSplitTunnelingEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<QPair<QString, QString>> IpSplitTunnelingController::getCurrentSites() const
|
||||||
|
{
|
||||||
|
return m_sites;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpSplitTunnelingController::fillSites()
|
||||||
|
{
|
||||||
|
QVariantMap sitesMap = m_appSettingsRepository->vpnSites(m_currentRouteMode);
|
||||||
|
m_sites.clear();
|
||||||
|
for (auto it = sitesMap.begin(); it != sitesMap.end(); ++it) {
|
||||||
|
m_sites.append(qMakePair(it.key(), it.value().toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString IpSplitTunnelingController::normalizeHostname(const QString &hostname) const
|
||||||
|
{
|
||||||
|
QString normalized = hostname;
|
||||||
|
normalized.replace("https://", "");
|
||||||
|
normalized.replace("http://", "");
|
||||||
|
normalized.replace("ftp://", "");
|
||||||
|
normalized = normalized.split("/", Qt::SkipEmptyParts).first();
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IpSplitTunnelingController::validateHostname(const QString &hostname) const
|
||||||
|
{
|
||||||
|
if (hostname.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!hostname.contains(".") && !NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void IpSplitTunnelingController::onHostResolved(const QHostInfo &hostInfo)
|
||||||
|
{
|
||||||
|
const QList<QHostAddress> &addresses = hostInfo.addresses();
|
||||||
|
QString hostname = hostInfo.hostName();
|
||||||
|
|
||||||
|
for (const QHostAddress &addr : addresses) {
|
||||||
|
if (addr.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol) {
|
||||||
|
processSiteAfterResolve(hostname, addr.toString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpSplitTunnelingController::processSiteAfterResolve(const QString &hostname, const QString &ip)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_sites.size(); i++) {
|
||||||
|
if (m_sites[i].first == hostname && m_sites[i].second.isEmpty()) {
|
||||||
|
m_sites[i].second = ip;
|
||||||
|
m_appSettingsRepository->addVpnSite(m_currentRouteMode, hostname, ip);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpSplitTunnelingController::processSite(const QString &hostname, const QString &ip)
|
||||||
|
{
|
||||||
|
addSiteInternal(hostname, ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IpSplitTunnelingController::importSitesFromJson(const QByteArray& jsonData, bool replaceExisting, QString &errorMessage)
|
||||||
|
{
|
||||||
|
QJsonParseError parseError;
|
||||||
|
QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData, &parseError);
|
||||||
|
|
||||||
|
if (parseError.error != QJsonParseError::NoError) {
|
||||||
|
errorMessage = tr("Failed to parse JSON data: %1").arg(parseError.errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!jsonDocument.isArray()) {
|
||||||
|
errorMessage = tr("The JSON data is not an array");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray jsonArray = jsonDocument.array();
|
||||||
|
QMap<QString, QString> sites;
|
||||||
|
|
||||||
|
for (auto jsonValue : jsonArray) {
|
||||||
|
QJsonObject jsonObject = jsonValue.toObject();
|
||||||
|
QString hostname = jsonObject.value("hostname").toString("");
|
||||||
|
QString ip = jsonObject.value("ip").toString("");
|
||||||
|
|
||||||
|
QString normalizedHostname = normalizeHostname(hostname);
|
||||||
|
|
||||||
|
if (!validateHostname(normalizedHostname)) {
|
||||||
|
qDebug() << normalizedHostname << " not look like ip adress or domain name";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sites.insert(normalizedHostname, ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
addSites(sites, replaceExisting);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray IpSplitTunnelingController::exportSitesToJson() const
|
||||||
|
{
|
||||||
|
QVector<QPair<QString, QString>> sites = getCurrentSites();
|
||||||
|
QJsonArray jsonArray;
|
||||||
|
|
||||||
|
for (const auto &site : sites) {
|
||||||
|
QJsonObject jsonObject;
|
||||||
|
jsonObject["hostname"] = site.first;
|
||||||
|
jsonObject["ip"] = site.second;
|
||||||
|
jsonArray.append(jsonObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument jsonDocument(jsonArray);
|
||||||
|
return jsonDocument.toJson();
|
||||||
|
}
|
||||||
|
|
||||||
58
client/core/controllers/ipSplitTunnelingController.h
Normal file
58
client/core/controllers/ipSplitTunnelingController.h
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#ifndef IPSPLITTUNNELINGCONTROLLER_H
|
||||||
|
#define IPSPLITTUNNELINGCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QPair>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QHostInfo>
|
||||||
|
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
|
||||||
|
class IpSplitTunnelingController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit IpSplitTunnelingController(SecureAppSettingsRepository* appSettingsRepository, QObject* parent = nullptr);
|
||||||
|
|
||||||
|
bool addSite(const QString &hostname);
|
||||||
|
void addSites(const QMap<QString, QString> &sites, bool replaceExisting);
|
||||||
|
bool removeSite(const QString &hostname);
|
||||||
|
void removeSites();
|
||||||
|
void setRouteMode(RouteMode routeMode);
|
||||||
|
void toggleSplitTunneling(bool enabled);
|
||||||
|
|
||||||
|
RouteMode getRouteMode() const;
|
||||||
|
bool isSplitTunnelingEnabled() const;
|
||||||
|
QVector<QPair<QString, QString>> getCurrentSites() const;
|
||||||
|
|
||||||
|
bool importSitesFromJson(const QByteArray& jsonData, bool replaceExisting, QString &errorMessage);
|
||||||
|
QByteArray exportSitesToJson() const;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onHostResolved(const QHostInfo &hostInfo);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void fillSites();
|
||||||
|
bool addSiteInternal(const QString &hostname, const QString &ip);
|
||||||
|
QString normalizeHostname(const QString &hostname) const;
|
||||||
|
bool validateHostname(const QString &hostname) const;
|
||||||
|
void processSiteAfterResolve(const QString &hostname, const QString &ip);
|
||||||
|
void processSite(const QString &hostname, const QString &ip);
|
||||||
|
|
||||||
|
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||||
|
RouteMode m_currentRouteMode;
|
||||||
|
QVector<QPair<QString, QString>> m_sites;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // IPSPLITTUNNELINGCONTROLLER_H
|
||||||
|
|
||||||
337
client/core/controllers/selfhosted/exportController.cpp
Normal file
337
client/core/controllers/selfhosted/exportController.cpp
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
#include "exportController.h"
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
#include "core/configurators/configuratorBase.h"
|
||||||
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
|
#include "core/utils/networkUtilities.h"
|
||||||
|
#include "core/utils/qrCodeUtils.h"
|
||||||
|
#include "core/utils/serialization/serialization.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
|
#include "core/models/containerConfig.h"
|
||||||
|
#include "core/models/protocolConfig.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
|
||||||
|
ExportController::ExportController(SecureServersRepository* serversRepository,
|
||||||
|
SecureAppSettingsRepository* appSettingsRepository,
|
||||||
|
QObject *parent)
|
||||||
|
: QObject(parent),
|
||||||
|
m_serversRepository(serversRepository),
|
||||||
|
m_appSettingsRepository(appSettingsRepository)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ExportController::ExportResult ExportController::generateFullAccessConfig(int serverIndex)
|
||||||
|
{
|
||||||
|
ExportResult result;
|
||||||
|
|
||||||
|
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
|
||||||
|
serverConfig.visit([](auto& arg) {
|
||||||
|
for (auto it = arg.containers.begin(); it != arg.containers.end(); ++it) {
|
||||||
|
it.value().protocolConfig.clearClientConfig();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
QJsonObject serverJson = serverConfig.toJson();
|
||||||
|
QByteArray compressedConfig = QJsonDocument(serverJson).toJson();
|
||||||
|
compressedConfig = qCompress(compressedConfig, 8);
|
||||||
|
result.config = generateVpnUrl(compressedConfig);
|
||||||
|
result.qrCodes = generateQrCodesFromConfig(compressedConfig);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExportController::ExportResult ExportController::generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName)
|
||||||
|
{
|
||||||
|
ExportResult result;
|
||||||
|
|
||||||
|
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
||||||
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
|
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container);
|
||||||
|
|
||||||
|
if (ContainerUtils::containerService(container) != ServiceType::Other) {
|
||||||
|
SshSession sshSession;
|
||||||
|
Proto protocol = ContainerUtils::defaultProtocol(container);
|
||||||
|
|
||||||
|
DnsSettings dnsSettings = {
|
||||||
|
m_appSettingsRepository->primaryDns(),
|
||||||
|
m_appSettingsRepository->secondaryDns()
|
||||||
|
};
|
||||||
|
|
||||||
|
auto configurator = ConfiguratorBase::create(protocol, &sshSession);
|
||||||
|
ProtocolConfig newProtocolConfig = configurator->createConfig(credentials, container, containerConfig, dnsSettings, result.errorCode);
|
||||||
|
if (result.errorCode != ErrorCode::NoError) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
containerConfig.protocolConfig = newProtocolConfig;
|
||||||
|
|
||||||
|
QString clientId = newProtocolConfig.clientId();
|
||||||
|
if (!clientId.isEmpty()) {
|
||||||
|
emit appendClientRequested(serverIndex, clientId, clientName, container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
|
||||||
|
serverConfig.visit([container, containerConfig](auto& arg) {
|
||||||
|
arg.containers.clear();
|
||||||
|
arg.containers[container] = containerConfig;
|
||||||
|
arg.defaultContainer = container;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (serverConfig.isSelfHosted()) {
|
||||||
|
SelfHostedServerConfig* selfHosted = serverConfig.as<SelfHostedServerConfig>();
|
||||||
|
if (selfHosted) {
|
||||||
|
selfHosted->userName.reset();
|
||||||
|
selfHosted->password.reset();
|
||||||
|
selfHosted->port.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dns = serverConfig.getDnsPair(m_appSettingsRepository->useAmneziaDns(),
|
||||||
|
m_appSettingsRepository->primaryDns(),
|
||||||
|
m_appSettingsRepository->secondaryDns());
|
||||||
|
serverConfig.visit([&dns](auto& arg) {
|
||||||
|
arg.dns1 = dns.first;
|
||||||
|
arg.dns2 = dns.second;
|
||||||
|
});
|
||||||
|
|
||||||
|
QJsonObject serverJson = serverConfig.toJson();
|
||||||
|
QByteArray compressedConfig = QJsonDocument(serverJson).toJson();
|
||||||
|
compressedConfig = qCompress(compressedConfig, 8);
|
||||||
|
result.config = generateVpnUrl(compressedConfig);
|
||||||
|
result.qrCodes = generateQrCodesFromConfig(compressedConfig);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExportController::NativeConfigResult ExportController::generateNativeConfig(int serverIndex, DockerContainer container,
|
||||||
|
const ContainerConfig &containerConfig,
|
||||||
|
const QString &clientName)
|
||||||
|
{
|
||||||
|
NativeConfigResult result;
|
||||||
|
|
||||||
|
if (ContainerUtils::containerService(container) == ServiceType::Other) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Proto protocol = ContainerUtils::defaultProtocol(container);
|
||||||
|
|
||||||
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
|
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
|
||||||
|
auto dns = serverConfig.getDnsPair(m_appSettingsRepository->useAmneziaDns(),
|
||||||
|
m_appSettingsRepository->primaryDns(),
|
||||||
|
m_appSettingsRepository->secondaryDns());
|
||||||
|
|
||||||
|
ContainerConfig modifiedContainerConfig = containerConfig;
|
||||||
|
modifiedContainerConfig.container = container;
|
||||||
|
|
||||||
|
DnsSettings dnsSettings = {
|
||||||
|
m_appSettingsRepository->primaryDns(),
|
||||||
|
m_appSettingsRepository->secondaryDns()
|
||||||
|
};
|
||||||
|
|
||||||
|
SshSession sshSession;
|
||||||
|
auto configurator = ConfiguratorBase::create(protocol, &sshSession);
|
||||||
|
|
||||||
|
ProtocolConfig newProtocolConfig = configurator->createConfig(credentials, container, modifiedContainerConfig, dnsSettings, result.errorCode);
|
||||||
|
if (result.errorCode != ErrorCode::NoError) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExportSettings exportSettings = { { dns.first, dns.second } };
|
||||||
|
ProtocolConfig processedConfig = configurator->processConfigWithExportSettings(exportSettings, newProtocolConfig);
|
||||||
|
|
||||||
|
if (protocol == Proto::OpenVpn || protocol == Proto::WireGuard || protocol == Proto::Awg) {
|
||||||
|
result.jsonNativeConfig[configKey::config] = processedConfig.nativeConfig();
|
||||||
|
} else {
|
||||||
|
result.jsonNativeConfig = QJsonDocument::fromJson(processedConfig.nativeConfig().toUtf8()).object();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (protocol == Proto::OpenVpn || protocol == Proto::WireGuard || protocol == Proto::Awg || protocol == Proto::Xray) {
|
||||||
|
QString clientId = newProtocolConfig.clientId();
|
||||||
|
if (!clientId.isEmpty()) {
|
||||||
|
emit appendClientRequested(serverIndex, clientId, clientName, container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExportController::ExportResult ExportController::generateOpenVpnConfig(int serverIndex, const QString &clientName)
|
||||||
|
{
|
||||||
|
ExportResult result;
|
||||||
|
|
||||||
|
DockerContainer container = DockerContainer::OpenVpn;
|
||||||
|
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container);
|
||||||
|
|
||||||
|
auto nativeResult = generateNativeConfig(serverIndex, container, containerConfig, clientName);
|
||||||
|
if (nativeResult.errorCode != ErrorCode::NoError) {
|
||||||
|
result.errorCode = nativeResult.errorCode;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList lines = nativeResult.jsonNativeConfig.value(configKey::config).toString().replace("\r", "").split("\n");
|
||||||
|
for (const QString &line : std::as_const(lines)) {
|
||||||
|
result.config.append(line + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
result.qrCodes = generateQrCodesFromConfig(result.config.toUtf8());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExportController::ExportResult ExportController::generateWireGuardConfig(int serverIndex, const QString &clientName)
|
||||||
|
{
|
||||||
|
ExportResult result;
|
||||||
|
|
||||||
|
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, DockerContainer::WireGuard);
|
||||||
|
|
||||||
|
auto nativeResult = generateNativeConfig(serverIndex, DockerContainer::WireGuard, containerConfig, clientName);
|
||||||
|
if (nativeResult.errorCode != ErrorCode::NoError) {
|
||||||
|
result.errorCode = nativeResult.errorCode;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList lines = nativeResult.jsonNativeConfig.value(configKey::config).toString().replace("\r", "").split("\n");
|
||||||
|
for (const QString &line : std::as_const(lines)) {
|
||||||
|
result.config.append(line + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
result.qrCodes << generateSingleQrCode(result.config.toUtf8());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExportController::ExportResult ExportController::generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName)
|
||||||
|
{
|
||||||
|
ExportResult result;
|
||||||
|
|
||||||
|
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
||||||
|
if (container != DockerContainer::Awg && container != DockerContainer::Awg2) {
|
||||||
|
result.errorCode = ErrorCode::InternalError;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container);
|
||||||
|
|
||||||
|
auto nativeResult = generateNativeConfig(serverIndex, container, containerConfig, clientName);
|
||||||
|
if (nativeResult.errorCode != ErrorCode::NoError) {
|
||||||
|
result.errorCode = nativeResult.errorCode;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList lines = nativeResult.jsonNativeConfig.value(configKey::config).toString().replace("\r", "").split("\n");
|
||||||
|
for (const QString &line : std::as_const(lines)) {
|
||||||
|
result.config.append(line + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
result.qrCodes << generateSingleQrCode(result.config.toUtf8());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ExportController::ExportResult ExportController::generateXrayConfig(int serverIndex, const QString &clientName)
|
||||||
|
{
|
||||||
|
ExportResult result;
|
||||||
|
|
||||||
|
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, DockerContainer::Xray);
|
||||||
|
|
||||||
|
auto nativeResult = generateNativeConfig(serverIndex, DockerContainer::Xray, containerConfig, clientName);
|
||||||
|
if (nativeResult.errorCode != ErrorCode::NoError) {
|
||||||
|
result.errorCode = nativeResult.errorCode;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList lines = QString(QJsonDocument(nativeResult.jsonNativeConfig).toJson()).replace("\r", "").split("\n");
|
||||||
|
for (const QString &line : std::as_const(lines)) {
|
||||||
|
result.config.append(line + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the Xray data to extract VLESS parameters and generate string
|
||||||
|
QJsonObject xrayConfig = nativeResult.jsonNativeConfig;
|
||||||
|
QJsonArray outbounds = xrayConfig.value(amnezia::protocols::xray::outbounds).toArray();
|
||||||
|
|
||||||
|
if (outbounds.isEmpty()) {
|
||||||
|
result.errorCode = ErrorCode::InternalError;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject outbound = outbounds[0].toObject();
|
||||||
|
QJsonObject settings = outbound.value(amnezia::protocols::xray::settings).toObject();
|
||||||
|
QJsonObject streamSettings = outbound.value(amnezia::protocols::xray::streamSettings).toObject();
|
||||||
|
|
||||||
|
QJsonArray vnext = settings.value(amnezia::protocols::xray::vnext).toArray();
|
||||||
|
if (vnext.isEmpty()) {
|
||||||
|
result.errorCode = ErrorCode::InternalError;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject server = vnext[0].toObject();
|
||||||
|
QJsonArray users = server.value(amnezia::protocols::xray::users).toArray();
|
||||||
|
if (users.isEmpty()) {
|
||||||
|
result.errorCode = ErrorCode::InternalError;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject user = users[0].toObject();
|
||||||
|
|
||||||
|
amnezia::serialization::VlessServerObject vlessServer;
|
||||||
|
vlessServer.address = server.value(amnezia::protocols::xray::address).toString();
|
||||||
|
vlessServer.port = server.value(amnezia::protocols::xray::port).toInt();
|
||||||
|
vlessServer.id = user.value(amnezia::protocols::xray::id).toString();
|
||||||
|
vlessServer.flow = user.value(amnezia::protocols::xray::flow).toString("xtls-rprx-vision");
|
||||||
|
vlessServer.encryption = user.value(amnezia::protocols::xray::encryption).toString("none");
|
||||||
|
|
||||||
|
vlessServer.network = streamSettings.value(amnezia::protocols::xray::network).toString("tcp");
|
||||||
|
vlessServer.security = streamSettings.value(amnezia::protocols::xray::security).toString("reality");
|
||||||
|
|
||||||
|
if (vlessServer.security == "reality") {
|
||||||
|
QJsonObject realitySettings = streamSettings.value(amnezia::protocols::xray::realitySettings).toObject();
|
||||||
|
vlessServer.serverName = realitySettings.value(amnezia::protocols::xray::serverName).toString();
|
||||||
|
vlessServer.publicKey = realitySettings.value(amnezia::protocols::xray::publicKey).toString();
|
||||||
|
vlessServer.shortId = realitySettings.value(amnezia::protocols::xray::shortId).toString();
|
||||||
|
vlessServer.fingerprint = realitySettings.value(amnezia::protocols::xray::fingerprint).toString("chrome");
|
||||||
|
vlessServer.spiderX = realitySettings.value(amnezia::protocols::xray::spiderX).toString("");
|
||||||
|
}
|
||||||
|
|
||||||
|
result.nativeConfigString = amnezia::serialization::vless::Serialize(vlessServer, "AmneziaVPN");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExportController::updateClientManagementModel(int serverIndex, int containerIndex)
|
||||||
|
{
|
||||||
|
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
||||||
|
emit updateClientsRequested(serverIndex, container);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExportController::revokeConfig(int row, int serverIndex, int containerIndex)
|
||||||
|
{
|
||||||
|
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
||||||
|
emit revokeClientRequested(serverIndex, row, container);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExportController::renameClient(int row, const QString &clientName, int serverIndex, int containerIndex)
|
||||||
|
{
|
||||||
|
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
||||||
|
emit renameClientRequested(serverIndex, row, clientName, container);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ExportController::generateVpnUrl(const QByteArray &compressedConfig)
|
||||||
|
{
|
||||||
|
return QString("vpn://%1").arg(QString(compressedConfig.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QString> ExportController::generateQrCodesFromConfig(const QByteArray &data)
|
||||||
|
{
|
||||||
|
return qrCodeUtils::generateQrCodeImageSeries(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ExportController::generateSingleQrCode(const QByteArray &data)
|
||||||
|
{
|
||||||
|
auto qr = qrCodeUtils::generateQrCode(data);
|
||||||
|
return qrCodeUtils::svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
|
||||||
|
}
|
||||||
77
client/core/controllers/selfhosted/exportController.h
Normal file
77
client/core/controllers/selfhosted/exportController.h
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#ifndef EXPORTCONTROLLER_H
|
||||||
|
#define EXPORTCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QList>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
#include "core/repositories/secureServersRepository.h"
|
||||||
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
|
|
||||||
|
class SshSession;
|
||||||
|
class VpnConfigurationsController;
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
|
||||||
|
class ExportController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct ExportResult
|
||||||
|
{
|
||||||
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
|
QString config;
|
||||||
|
QString nativeConfigString;
|
||||||
|
QList<QString> qrCodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit ExportController(SecureServersRepository* serversRepository,
|
||||||
|
SecureAppSettingsRepository* appSettingsRepository,
|
||||||
|
QObject *parent = nullptr);
|
||||||
|
|
||||||
|
ExportResult generateFullAccessConfig(int serverIndex);
|
||||||
|
ExportResult generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName);
|
||||||
|
ExportResult generateOpenVpnConfig(int serverIndex, const QString &clientName);
|
||||||
|
ExportResult generateWireGuardConfig(int serverIndex, const QString &clientName);
|
||||||
|
ExportResult generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName);
|
||||||
|
ExportResult generateXrayConfig(int serverIndex, const QString &clientName);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void appendClientRequested(int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container);
|
||||||
|
void updateClientsRequested(int serverIndex, DockerContainer container);
|
||||||
|
void revokeClientRequested(int serverIndex, int row, DockerContainer container);
|
||||||
|
void renameClientRequested(int serverIndex, int row, const QString &clientName, DockerContainer container);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void updateClientManagementModel(int serverIndex, int containerIndex);
|
||||||
|
void revokeConfig(int row, int serverIndex, int containerIndex);
|
||||||
|
void renameClient(int row, const QString &clientName, int serverIndex, int containerIndex);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct NativeConfigResult
|
||||||
|
{
|
||||||
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
|
QJsonObject jsonNativeConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
NativeConfigResult generateNativeConfig(int serverIndex, DockerContainer container,
|
||||||
|
const ContainerConfig &containerConfig,
|
||||||
|
const QString &clientName);
|
||||||
|
|
||||||
|
QString generateVpnUrl(const QByteArray &compressedConfig);
|
||||||
|
QList<QString> generateQrCodesFromConfig(const QByteArray &data);
|
||||||
|
QString generateSingleQrCode(const QByteArray &data);
|
||||||
|
|
||||||
|
SecureServersRepository* m_serversRepository;
|
||||||
|
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // EXPORTCONTROLLER_H
|
||||||
762
client/core/controllers/selfhosted/importController.cpp
Normal file
762
client/core/controllers/selfhosted/importController.cpp
Normal file
@@ -0,0 +1,762 @@
|
|||||||
|
#include "importController.h"
|
||||||
|
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonParseError>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QRegularExpressionMatch>
|
||||||
|
#include <QRegularExpressionMatchIterator>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/api/apiEnums.h"
|
||||||
|
#include "core/utils/constants/apiKeys.h"
|
||||||
|
#include "core/utils/constants/apiConstants.h"
|
||||||
|
#include "core/utils/api/apiUtils.h"
|
||||||
|
#include "core/utils/serialization/serialization.h"
|
||||||
|
#include "core/utils/utilities.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/utils/qrCodeUtils.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
using namespace ProtocolUtils;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
ConfigTypes checkConfigFormat(const QString &config)
|
||||||
|
{
|
||||||
|
const QString openVpnConfigPatternCli = "client";
|
||||||
|
const QString openVpnConfigPatternDriver1 = "dev tun";
|
||||||
|
const QString openVpnConfigPatternDriver2 = "dev tap";
|
||||||
|
|
||||||
|
const QString wireguardConfigPatternSectionInterface = "[Interface]";
|
||||||
|
const QString wireguardConfigPatternSectionPeer = "[Peer]";
|
||||||
|
|
||||||
|
const QString xrayConfigPatternInbound = "inbounds";
|
||||||
|
const QString xrayConfigPatternOutbound = "outbounds";
|
||||||
|
|
||||||
|
const QString amneziaConfigPattern = "containers";
|
||||||
|
const QString amneziaConfigPatternHostName = "hostName";
|
||||||
|
const QString amneziaConfigPatternUserName = "userName";
|
||||||
|
const QString amneziaConfigPatternPassword = "password";
|
||||||
|
const QString amneziaFreeConfigPattern = "api_key";
|
||||||
|
const QString amneziaPremiumConfigPattern = "auth_data";
|
||||||
|
const QString backupPattern = "Servers/serversList";
|
||||||
|
|
||||||
|
if (config.contains(backupPattern)) {
|
||||||
|
return ConfigTypes::Backup;
|
||||||
|
} else if (config.contains(amneziaConfigPattern) || config.contains(amneziaFreeConfigPattern)
|
||||||
|
|| config.contains(amneziaPremiumConfigPattern)
|
||||||
|
|| (config.contains(amneziaConfigPatternHostName) && config.contains(amneziaConfigPatternUserName)
|
||||||
|
&& config.contains(amneziaConfigPatternPassword))) {
|
||||||
|
return ConfigTypes::Amnezia;
|
||||||
|
} else if (config.contains(wireguardConfigPatternSectionInterface) && config.contains(wireguardConfigPatternSectionPeer)) {
|
||||||
|
return ConfigTypes::WireGuard;
|
||||||
|
} else if ((config.contains(xrayConfigPatternInbound)) && (config.contains(xrayConfigPatternOutbound))) {
|
||||||
|
return ConfigTypes::Xray;
|
||||||
|
} else if (config.contains(openVpnConfigPatternCli)
|
||||||
|
&& (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) {
|
||||||
|
return ConfigTypes::OpenVpn;
|
||||||
|
}
|
||||||
|
return ConfigTypes::Invalid;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
ImportController::ImportController(SecureServersRepository* serversRepository,
|
||||||
|
SecureAppSettingsRepository* appSettingsRepository,
|
||||||
|
QObject *parent)
|
||||||
|
: QObject(parent),
|
||||||
|
m_serversRepository(serversRepository),
|
||||||
|
m_appSettingsRepository(appSettingsRepository)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ImportController::ImportResult ImportController::extractConfigFromData(const QString &data, const QString &configFileName)
|
||||||
|
{
|
||||||
|
ImportResult result;
|
||||||
|
result.configFileName = configFileName;
|
||||||
|
result.maliciousWarningText.clear();
|
||||||
|
|
||||||
|
QString config = data;
|
||||||
|
QString prefix;
|
||||||
|
QString errormsg;
|
||||||
|
ConfigTypes configType = ConfigTypes::Invalid;
|
||||||
|
|
||||||
|
if (config.startsWith("vless://")) {
|
||||||
|
configType = ConfigTypes::Xray;
|
||||||
|
result.config = extractXrayConfig(
|
||||||
|
Utils::JsonToString(serialization::vless::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
|
||||||
|
configType, prefix);
|
||||||
|
if (!result.config.empty()) {
|
||||||
|
result.configType = configType;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.startsWith("vmess://") && config.contains("@")) {
|
||||||
|
configType = ConfigTypes::Xray;
|
||||||
|
result.config = extractXrayConfig(
|
||||||
|
Utils::JsonToString(serialization::vmess_new::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
|
||||||
|
configType, prefix);
|
||||||
|
if (!result.config.empty()) {
|
||||||
|
result.configType = configType;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.startsWith("vmess://")) {
|
||||||
|
configType = ConfigTypes::Xray;
|
||||||
|
result.config = extractXrayConfig(
|
||||||
|
Utils::JsonToString(serialization::vmess::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
|
||||||
|
configType, prefix);
|
||||||
|
if (!result.config.empty()) {
|
||||||
|
result.configType = configType;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.startsWith("trojan://")) {
|
||||||
|
configType = ConfigTypes::Xray;
|
||||||
|
result.config = extractXrayConfig(
|
||||||
|
Utils::JsonToString(serialization::trojan::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
|
||||||
|
configType, prefix);
|
||||||
|
if (!result.config.empty()) {
|
||||||
|
result.configType = configType;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.startsWith("ss://") && !config.contains("plugin=")) {
|
||||||
|
configType = ConfigTypes::ShadowSocks;
|
||||||
|
result.config = extractXrayConfig(
|
||||||
|
Utils::JsonToString(serialization::ss::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
|
||||||
|
configType, prefix);
|
||||||
|
if (!result.config.empty()) {
|
||||||
|
result.configType = configType;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.startsWith("ssd://")) {
|
||||||
|
QStringList tmp;
|
||||||
|
QList<std::pair<QString, QJsonObject>> servers = serialization::ssd::Deserialize(config, &prefix, &tmp);
|
||||||
|
configType = ConfigTypes::ShadowSocks;
|
||||||
|
// Took only first config from list
|
||||||
|
if (!servers.isEmpty()) {
|
||||||
|
result.config = extractXrayConfig(servers.first().first, configType);
|
||||||
|
}
|
||||||
|
if (!result.config.empty()) {
|
||||||
|
result.configType = configType;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configType = checkConfigFormat(config);
|
||||||
|
if (configType == ConfigTypes::Invalid) {
|
||||||
|
config.replace("vpn://", "");
|
||||||
|
QByteArray ba = QByteArray::fromBase64(config.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
||||||
|
QByteArray baUncompressed = qUncompress(ba);
|
||||||
|
if (!baUncompressed.isEmpty()) {
|
||||||
|
ba = baUncompressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
config = ba;
|
||||||
|
configType = checkConfigFormat(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.configType = configType;
|
||||||
|
|
||||||
|
switch (configType) {
|
||||||
|
case ConfigTypes::OpenVpn: {
|
||||||
|
result.config = extractOpenVpnConfig(config);
|
||||||
|
if (!result.config.empty()) {
|
||||||
|
checkForMaliciousStrings(result.config, result.maliciousWarningText);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.errorCode = ErrorCode::ImportInvalidConfigError;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
case ConfigTypes::Awg:
|
||||||
|
case ConfigTypes::WireGuard: {
|
||||||
|
result.config = extractWireGuardConfig(config, result.configType);
|
||||||
|
result.isNativeWireGuardConfig = (result.configType == ConfigTypes::WireGuard);
|
||||||
|
if (!result.config.empty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.errorCode = ErrorCode::ImportInvalidConfigError;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
case ConfigTypes::Xray: {
|
||||||
|
result.config = extractXrayConfig(config, configType);
|
||||||
|
if (!result.config.empty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.errorCode = ErrorCode::ImportInvalidConfigError;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
case ConfigTypes::Amnezia: {
|
||||||
|
result.config = QJsonDocument::fromJson(config.toUtf8()).object();
|
||||||
|
|
||||||
|
if (apiUtils::isServerFromApi(result.config)) {
|
||||||
|
auto apiConfig = result.config.value(apiDefs::key::apiConfig).toObject();
|
||||||
|
apiConfig[apiDefs::key::vpnKey] = data;
|
||||||
|
result.config[apiDefs::key::apiConfig] = apiConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
processAmneziaConfig(result.config);
|
||||||
|
if (!result.config.empty()) {
|
||||||
|
checkForMaliciousStrings(result.config, result.maliciousWarningText);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.errorCode = ErrorCode::ImportInvalidConfigError;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
case ConfigTypes::Backup: {
|
||||||
|
result.errorCode = ErrorCode::ImportBackupFileUseRestoreInstead;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
case ConfigTypes::Invalid: {
|
||||||
|
result.errorCode = ErrorCode::ImportInvalidConfigError;
|
||||||
|
result.configFileName.clear();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.errorCode = ErrorCode::ImportInvalidConfigError;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImportController::ImportResult ImportController::extractConfigFromQr(const QByteArray &data)
|
||||||
|
{
|
||||||
|
ImportResult result;
|
||||||
|
|
||||||
|
QString dataStr = QString::fromUtf8(data);
|
||||||
|
ConfigTypes configType = checkConfigFormat(dataStr);
|
||||||
|
if (configType != ConfigTypes::Invalid) {
|
||||||
|
return extractConfigFromData(dataStr, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject dataObj = QJsonDocument::fromJson(data).object();
|
||||||
|
if (!dataObj.isEmpty()) {
|
||||||
|
result.config = dataObj;
|
||||||
|
result.configType = ConfigTypes::Amnezia;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray ba_uncompressed = qUncompress(data);
|
||||||
|
if (!ba_uncompressed.isEmpty()) {
|
||||||
|
result.config = QJsonDocument::fromJson(ba_uncompressed).object();
|
||||||
|
if (result.config.isEmpty()) {
|
||||||
|
result.errorCode = ErrorCode::ImportInvalidConfigError;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.configType = ConfigTypes::Amnezia;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray ba = QByteArray::fromBase64(data, QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
||||||
|
QByteArray baUncompressed = qUncompress(ba);
|
||||||
|
|
||||||
|
if (!baUncompressed.isEmpty()) {
|
||||||
|
ba = baUncompressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ba.isEmpty()) {
|
||||||
|
result.config = QJsonDocument::fromJson(ba).object();
|
||||||
|
if (result.config.isEmpty()) {
|
||||||
|
result.errorCode = ErrorCode::ImportInvalidConfigError;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.configType = ConfigTypes::Amnezia;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.errorCode = ErrorCode::ImportInvalidConfigError;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImportController::startDecodingQr()
|
||||||
|
{
|
||||||
|
m_qrCodeChunks.clear();
|
||||||
|
m_totalQrCodeChunksCount = 0;
|
||||||
|
m_receivedQrCodeChunksCount = 0;
|
||||||
|
m_isQrCodeProcessed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImportController::QrParseResult ImportController::parseQrCodeChunk(const QString &code)
|
||||||
|
{
|
||||||
|
QrParseResult parseResult;
|
||||||
|
parseResult.chunksReceived = m_receivedQrCodeChunksCount;
|
||||||
|
parseResult.chunksTotal = m_totalQrCodeChunksCount;
|
||||||
|
|
||||||
|
if (!m_isQrCodeProcessed) {
|
||||||
|
return parseResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray ba = QByteArray::fromBase64(code.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
||||||
|
QDataStream s(&ba, QIODevice::ReadOnly);
|
||||||
|
qint16 magic;
|
||||||
|
s >> magic;
|
||||||
|
|
||||||
|
if (magic == qrCodeUtils::qrMagicCode) {
|
||||||
|
quint8 chunksCount;
|
||||||
|
s >> chunksCount;
|
||||||
|
if (m_totalQrCodeChunksCount != chunksCount) {
|
||||||
|
m_qrCodeChunks.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_totalQrCodeChunksCount = chunksCount;
|
||||||
|
|
||||||
|
quint8 chunkId;
|
||||||
|
s >> chunkId;
|
||||||
|
s >> m_qrCodeChunks[chunkId];
|
||||||
|
m_receivedQrCodeChunksCount = m_qrCodeChunks.size();
|
||||||
|
parseResult.chunksReceived = m_receivedQrCodeChunksCount;
|
||||||
|
parseResult.chunksTotal = m_totalQrCodeChunksCount;
|
||||||
|
|
||||||
|
if (m_qrCodeChunks.size() == m_totalQrCodeChunksCount) {
|
||||||
|
QByteArray data;
|
||||||
|
for (int i = 0; i < m_totalQrCodeChunksCount; ++i) {
|
||||||
|
data.append(m_qrCodeChunks.value(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImportResult result = extractConfigFromQr(data);
|
||||||
|
if (result.errorCode == ErrorCode::NoError) {
|
||||||
|
parseResult.success = true;
|
||||||
|
parseResult.importResult = result;
|
||||||
|
m_isQrCodeProcessed = false;
|
||||||
|
} else {
|
||||||
|
m_qrCodeChunks.clear();
|
||||||
|
m_totalQrCodeChunksCount = 0;
|
||||||
|
m_receivedQrCodeChunksCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ImportResult result = extractConfigFromQr(code.toUtf8());
|
||||||
|
if (result.errorCode != ErrorCode::NoError) {
|
||||||
|
result = extractConfigFromQr(ba);
|
||||||
|
}
|
||||||
|
if (result.errorCode == ErrorCode::NoError) {
|
||||||
|
parseResult.success = true;
|
||||||
|
parseResult.importResult = result;
|
||||||
|
m_isQrCodeProcessed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImportController::isQrDecodingActive() const
|
||||||
|
{
|
||||||
|
return m_isQrCodeProcessed;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImportController::qrChunksReceived() const
|
||||||
|
{
|
||||||
|
return m_receivedQrCodeChunksCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImportController::qrChunksTotal() const
|
||||||
|
{
|
||||||
|
return m_totalQrCodeChunksCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImportController::importConfig(const QJsonObject &config)
|
||||||
|
{
|
||||||
|
ServerCredentials credentials;
|
||||||
|
credentials.hostName = config.value(configKey::hostName).toString();
|
||||||
|
credentials.port = config.value(configKey::port).toInt();
|
||||||
|
credentials.userName = config.value(configKey::userName).toString();
|
||||||
|
credentials.secretData = config.value(configKey::password).toString();
|
||||||
|
|
||||||
|
if (credentials.isValid() || config.contains(configKey::containers)) {
|
||||||
|
ServerConfig serverConfig = ServerConfig::fromJson(config);
|
||||||
|
m_serversRepository->addServer(serverConfig);
|
||||||
|
emit importFinished();
|
||||||
|
} else if (config.contains(configKey::configVersion)) {
|
||||||
|
quint16 crc = qChecksum(QJsonDocument(config).toJson());
|
||||||
|
if (m_serversRepository->hasServerWithCrc(crc)) {
|
||||||
|
emit importErrorOccurred(ErrorCode::ApiConfigAlreadyAdded, true);
|
||||||
|
} else {
|
||||||
|
QJsonObject configWithCrc = config;
|
||||||
|
configWithCrc.insert(configKey::crc, crc);
|
||||||
|
ServerConfig serverConfig = ServerConfig::fromJson(configWithCrc);
|
||||||
|
m_serversRepository->addServer(serverConfig);
|
||||||
|
emit importFinished();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "Failed to import profile";
|
||||||
|
qDebug().noquote() << QJsonDocument(config).toJson();
|
||||||
|
emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ImportController::processNativeWireGuardConfig(const QJsonObject &config)
|
||||||
|
{
|
||||||
|
QJsonObject result = config;
|
||||||
|
auto containers = result.value(configKey::containers).toArray();
|
||||||
|
if (!containers.isEmpty()) {
|
||||||
|
auto container = containers.at(0).toObject();
|
||||||
|
auto serverProtocolConfig = container.value(ContainerUtils::containerTypeToProtocolString(DockerContainer::WireGuard)).toObject();
|
||||||
|
auto clientProtocolConfig = QJsonDocument::fromJson(serverProtocolConfig.value(configKey::lastConfig).toString().toUtf8()).object();
|
||||||
|
|
||||||
|
QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(4, 7));
|
||||||
|
QString junkPacketMinSize = QString::number(10);
|
||||||
|
QString junkPacketMaxSize = QString::number(50);
|
||||||
|
clientProtocolConfig[configKey::junkPacketCount] = junkPacketCount;
|
||||||
|
clientProtocolConfig[configKey::junkPacketMinSize] = junkPacketMinSize;
|
||||||
|
clientProtocolConfig[configKey::junkPacketMaxSize] = junkPacketMaxSize;
|
||||||
|
clientProtocolConfig[configKey::initPacketJunkSize] = "0";
|
||||||
|
clientProtocolConfig[configKey::responsePacketJunkSize] = "0";
|
||||||
|
clientProtocolConfig[configKey::initPacketMagicHeader] = "1";
|
||||||
|
clientProtocolConfig[configKey::responsePacketMagicHeader] = "2";
|
||||||
|
clientProtocolConfig[configKey::underloadPacketMagicHeader] = "3";
|
||||||
|
clientProtocolConfig[configKey::transportPacketMagicHeader] = "4";
|
||||||
|
|
||||||
|
clientProtocolConfig[configKey::cookieReplyPacketJunkSize] = "0";
|
||||||
|
clientProtocolConfig[configKey::transportPacketJunkSize] = "0";
|
||||||
|
|
||||||
|
clientProtocolConfig[configKey::specialJunk1] = protocols::awg::defaultSpecialJunk1;
|
||||||
|
|
||||||
|
clientProtocolConfig[configKey::isObfuscationEnabled] = true;
|
||||||
|
|
||||||
|
serverProtocolConfig[configKey::lastConfig] = QString(QJsonDocument(clientProtocolConfig).toJson());
|
||||||
|
container[configKey::wireguard] = serverProtocolConfig;
|
||||||
|
containers.replace(0, container);
|
||||||
|
result[configKey::containers] = containers;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigTypes ImportController::checkConfigFormat(const QString &config) const
|
||||||
|
{
|
||||||
|
return ::checkConfigFormat(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ImportController::extractOpenVpnConfig(const QString &data) const
|
||||||
|
{
|
||||||
|
QJsonObject openVpnConfig;
|
||||||
|
openVpnConfig[configKey::config] = data;
|
||||||
|
|
||||||
|
QJsonObject lastConfig;
|
||||||
|
lastConfig[configKey::lastConfig] = QString(QJsonDocument(openVpnConfig).toJson());
|
||||||
|
lastConfig[configKey::isThirdPartyConfig] = true;
|
||||||
|
|
||||||
|
QJsonObject containers;
|
||||||
|
containers.insert(configKey::container, QJsonValue(configKey::amneziaOpenvpn));
|
||||||
|
containers.insert(configKey::openvpn, QJsonValue(lastConfig));
|
||||||
|
|
||||||
|
QJsonArray arr;
|
||||||
|
arr.push_back(containers);
|
||||||
|
|
||||||
|
QString hostName;
|
||||||
|
const static QRegularExpression hostNameRegExp("remote\\s+([^\\s]+)");
|
||||||
|
QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data);
|
||||||
|
if (hostNameMatch.hasMatch()) {
|
||||||
|
hostName = hostNameMatch.captured(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject config;
|
||||||
|
config[configKey::containers] = arr;
|
||||||
|
config[configKey::defaultContainer] = configKey::amneziaOpenvpn;
|
||||||
|
config[configKey::description] = m_appSettingsRepository->nextAvailableServerName();
|
||||||
|
|
||||||
|
const static QRegularExpression dnsRegExp("dhcp-option DNS (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)");
|
||||||
|
QRegularExpressionMatchIterator dnsMatch = dnsRegExp.globalMatch(data);
|
||||||
|
if (dnsMatch.hasNext()) {
|
||||||
|
config[configKey::dns1] = dnsMatch.next().captured(1);
|
||||||
|
}
|
||||||
|
if (dnsMatch.hasNext()) {
|
||||||
|
config[configKey::dns2] = dnsMatch.next().captured(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
config[configKey::hostName] = hostName;
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ImportController::extractWireGuardConfig(const QString &data, ConfigTypes &configType) const
|
||||||
|
{
|
||||||
|
QMap<QString, QString> configMap;
|
||||||
|
auto configByLines = data.split("\n");
|
||||||
|
for (const QString &line : configByLines) {
|
||||||
|
QString trimmedLine = line.trimmed();
|
||||||
|
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
QStringList parts = trimmedLine.split(" = ");
|
||||||
|
if (parts.count() == 2) {
|
||||||
|
configMap[parts.at(0).trimmed()] = parts.at(1).trimmed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject lastConfig;
|
||||||
|
lastConfig[configKey::config] = data;
|
||||||
|
|
||||||
|
auto url { QUrl::fromUserInput(configMap.value(protocols::wireguard::Endpoint)) };
|
||||||
|
QString hostName;
|
||||||
|
QString port;
|
||||||
|
if (!url.host().isEmpty()) {
|
||||||
|
hostName = url.host();
|
||||||
|
} else {
|
||||||
|
qDebug() << "Key parameter" << protocols::wireguard::Endpoint << "is missing or has an invalid format";
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.port() != -1) {
|
||||||
|
port = QString::number(url.port());
|
||||||
|
} else {
|
||||||
|
port = protocols::wireguard::defaultPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastConfig[configKey::hostName] = hostName;
|
||||||
|
lastConfig[configKey::port] = port.toInt();
|
||||||
|
|
||||||
|
if (!configMap.value(protocols::wireguard::PrivateKey).isEmpty()
|
||||||
|
&& !configMap.value(protocols::wireguard::Address).isEmpty()
|
||||||
|
&& !configMap.value(protocols::wireguard::PublicKey).isEmpty()) {
|
||||||
|
lastConfig[configKey::clientPrivKey] = configMap.value(protocols::wireguard::PrivateKey);
|
||||||
|
lastConfig[configKey::clientIp] = configMap.value(protocols::wireguard::Address);
|
||||||
|
|
||||||
|
if (!configMap.value(protocols::wireguard::PresharedKey).isEmpty()) {
|
||||||
|
lastConfig[configKey::pskKey] = configMap.value(protocols::wireguard::PresharedKey);
|
||||||
|
} else if (!configMap.value(protocols::wireguard::PreSharedKey).isEmpty()) {
|
||||||
|
lastConfig[configKey::pskKey] = configMap.value(protocols::wireguard::PreSharedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastConfig[configKey::serverPubKey] = configMap.value(protocols::wireguard::PublicKey);
|
||||||
|
} else {
|
||||||
|
qDebug() << "One of the key parameters is missing (PrivateKey, Address, PublicKey)";
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!configMap.value(protocols::wireguard::MTU).isEmpty()) {
|
||||||
|
lastConfig[configKey::mtu] = configMap.value(protocols::wireguard::MTU);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!configMap.value(protocols::wireguard::PersistentKeepalive).isEmpty()) {
|
||||||
|
lastConfig[configKey::persistentKeepAlive] = configMap.value(protocols::wireguard::PersistentKeepalive);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(
|
||||||
|
configMap.value(protocols::wireguard::AllowedIPs).split(", "));
|
||||||
|
|
||||||
|
lastConfig[configKey::allowedIps] = allowedIpsJsonArray;
|
||||||
|
|
||||||
|
QString protocolName = configKey::wireguard;
|
||||||
|
QString protocolVersion;
|
||||||
|
ConfigTypes detectedType = ConfigTypes::WireGuard;
|
||||||
|
|
||||||
|
const QStringList requiredJunkFields = { configKey::junkPacketCount, configKey::junkPacketMinSize,
|
||||||
|
configKey::junkPacketMaxSize, configKey::initPacketJunkSize,
|
||||||
|
configKey::responsePacketJunkSize, configKey::initPacketMagicHeader,
|
||||||
|
configKey::responsePacketMagicHeader, configKey::underloadPacketMagicHeader,
|
||||||
|
configKey::transportPacketMagicHeader };
|
||||||
|
|
||||||
|
const QStringList optionalJunkFields = { configKey::cookieReplyPacketJunkSize,
|
||||||
|
configKey::transportPacketJunkSize,
|
||||||
|
configKey::specialJunk1, configKey::specialJunk2, configKey::specialJunk3,
|
||||||
|
configKey::specialJunk4, configKey::specialJunk5
|
||||||
|
};
|
||||||
|
|
||||||
|
bool hasAllRequiredFields = std::all_of(requiredJunkFields.begin(), requiredJunkFields.end(),
|
||||||
|
[&configMap](const QString &field) { return !configMap.value(field).isEmpty(); });
|
||||||
|
if (hasAllRequiredFields) {
|
||||||
|
for (const QString &field : requiredJunkFields) {
|
||||||
|
lastConfig[field] = configMap.value(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const QString &field : optionalJunkFields) {
|
||||||
|
if (!configMap.value(field).isEmpty()) {
|
||||||
|
lastConfig[field] = configMap.value(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasCookieReplyPacketJunkSize = !configMap.value(configKey::cookieReplyPacketJunkSize).isEmpty();
|
||||||
|
bool hasTransportPacketJunkSize = !configMap.value(configKey::transportPacketJunkSize).isEmpty();
|
||||||
|
bool hasSpecialJunk = !configMap.value(configKey::specialJunk1).isEmpty() ||
|
||||||
|
!configMap.value(configKey::specialJunk2).isEmpty() ||
|
||||||
|
!configMap.value(configKey::specialJunk3).isEmpty() ||
|
||||||
|
!configMap.value(configKey::specialJunk4).isEmpty() ||
|
||||||
|
!configMap.value(configKey::specialJunk5).isEmpty();
|
||||||
|
|
||||||
|
if (hasCookieReplyPacketJunkSize && hasTransportPacketJunkSize) {
|
||||||
|
protocolVersion = "2";
|
||||||
|
} else if (hasSpecialJunk && !hasCookieReplyPacketJunkSize && !hasTransportPacketJunkSize) {
|
||||||
|
protocolVersion = "1.5";
|
||||||
|
}
|
||||||
|
protocolName = configKey::awg;
|
||||||
|
detectedType = ConfigTypes::Awg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!configMap.value(protocols::wireguard::MTU).isEmpty()) {
|
||||||
|
lastConfig[configKey::mtu] = configMap.value(protocols::wireguard::MTU);
|
||||||
|
} else {
|
||||||
|
lastConfig[configKey::mtu] = (protocolName == configKey::awg)
|
||||||
|
? protocols::awg::defaultMtu
|
||||||
|
: protocols::wireguard::defaultMtu;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject wireguardConfig;
|
||||||
|
wireguardConfig[configKey::lastConfig] = QString(QJsonDocument(lastConfig).toJson());
|
||||||
|
wireguardConfig[configKey::isThirdPartyConfig] = true;
|
||||||
|
wireguardConfig[configKey::port] = port;
|
||||||
|
wireguardConfig[configKey::transportProto] = protocols::openvpn::defaultTransportProto;
|
||||||
|
if (protocolName == configKey::awg && !protocolVersion.isEmpty()) {
|
||||||
|
wireguardConfig[configKey::protocolVersion] = protocolVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject containers;
|
||||||
|
QString containerName = (protocolName == configKey::awg) ? configKey::amneziaAwg : configKey::amneziaWireguard;
|
||||||
|
containers.insert(configKey::container, QJsonValue(containerName));
|
||||||
|
containers.insert(protocolName, QJsonValue(wireguardConfig));
|
||||||
|
|
||||||
|
QJsonArray arr;
|
||||||
|
arr.push_back(containers);
|
||||||
|
|
||||||
|
QJsonObject config;
|
||||||
|
config[configKey::containers] = arr;
|
||||||
|
config[configKey::defaultContainer] = containerName;
|
||||||
|
config[configKey::description] = m_appSettingsRepository->nextAvailableServerName();
|
||||||
|
|
||||||
|
const static QRegularExpression dnsRegExp(
|
||||||
|
"DNS = "
|
||||||
|
"(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b).*(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)");
|
||||||
|
QRegularExpressionMatch dnsMatch = dnsRegExp.match(data);
|
||||||
|
if (dnsMatch.hasMatch()) {
|
||||||
|
config[configKey::dns1] = dnsMatch.captured(1);
|
||||||
|
config[configKey::dns2] = dnsMatch.captured(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
config[configKey::hostName] = hostName;
|
||||||
|
|
||||||
|
configType = detectedType;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ImportController::extractXrayConfig(const QString &data, ConfigTypes configType, const QString &description) const
|
||||||
|
{
|
||||||
|
QJsonParseError parserErr;
|
||||||
|
QJsonDocument jsonConf = QJsonDocument::fromJson(data.toLocal8Bit(), &parserErr);
|
||||||
|
|
||||||
|
QJsonObject xrayVpnConfig;
|
||||||
|
xrayVpnConfig[configKey::config] = jsonConf.toJson().constData();
|
||||||
|
QJsonObject lastConfig;
|
||||||
|
lastConfig[configKey::lastConfig] = jsonConf.toJson().constData();
|
||||||
|
lastConfig[configKey::isThirdPartyConfig] = true;
|
||||||
|
|
||||||
|
QJsonObject containers;
|
||||||
|
if (configType == ConfigTypes::ShadowSocks) {
|
||||||
|
containers.insert(configKey::ssxray, QJsonValue(lastConfig));
|
||||||
|
containers.insert(configKey::container, QJsonValue(configKey::amneziaSsxray));
|
||||||
|
} else {
|
||||||
|
containers.insert(configKey::container, QJsonValue(configKey::amneziaXray));
|
||||||
|
containers.insert(configKey::xray, QJsonValue(lastConfig));
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray arr;
|
||||||
|
arr.push_back(containers);
|
||||||
|
|
||||||
|
QString hostName;
|
||||||
|
|
||||||
|
const static QRegularExpression hostNameRegExp("\"address\":\\s*\"([^\"]+)");
|
||||||
|
QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data);
|
||||||
|
if (hostNameMatch.hasMatch()) {
|
||||||
|
hostName = hostNameMatch.captured(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject config;
|
||||||
|
config[configKey::containers] = arr;
|
||||||
|
config[configKey::defaultContainer] = (configType == ConfigTypes::ShadowSocks)
|
||||||
|
? configKey::amneziaSsxray
|
||||||
|
: configKey::amneziaXray;
|
||||||
|
if (description.isEmpty()) {
|
||||||
|
config[configKey::description] = m_appSettingsRepository->nextAvailableServerName();
|
||||||
|
} else {
|
||||||
|
config[configKey::description] = description;
|
||||||
|
}
|
||||||
|
config[configKey::hostName] = hostName;
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig, QString &warningText) const
|
||||||
|
{
|
||||||
|
const QJsonArray &containers = serverConfig.value(configKey::containers).toArray();
|
||||||
|
for (const QJsonValue &container : containers) {
|
||||||
|
auto containerConfig = container.toObject();
|
||||||
|
auto containerName = containerConfig[configKey::container].toString();
|
||||||
|
if (containerName == ContainerUtils::containerToString(DockerContainer::OpenVpn)) {
|
||||||
|
|
||||||
|
QString protocolConfig =
|
||||||
|
containerConfig[ProtocolUtils::protoToString(Proto::OpenVpn)].toObject()[configKey::lastConfig].toString();
|
||||||
|
QString protocolConfigJson = QJsonDocument::fromJson(protocolConfig.toUtf8()).object()[configKey::config].toString();
|
||||||
|
|
||||||
|
// https://github.com/OpenVPN/openvpn/blob/master/doc/man-sections/script-options.rst
|
||||||
|
QStringList dangerousTags {
|
||||||
|
"up", "tls-verify", "ipchange", "client-connect", "route-up", "route-pre-down", "client-disconnect", "down", "learn-address", "auth-user-pass-verify"
|
||||||
|
};
|
||||||
|
|
||||||
|
QStringList maliciousStrings;
|
||||||
|
QStringList lines = protocolConfigJson.split('\n', Qt::SkipEmptyParts);
|
||||||
|
|
||||||
|
for (const QString &rawLine : lines) {
|
||||||
|
QString line = rawLine.trimmed();
|
||||||
|
|
||||||
|
QString command = line.section(' ', 0, 0, QString::SectionSkipEmpty);
|
||||||
|
if (dangerousTags.contains(command, Qt::CaseInsensitive)) {
|
||||||
|
maliciousStrings << rawLine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
warningText = "This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious "
|
||||||
|
"scripts, so only add it if you fully trust the provider of this config. ";
|
||||||
|
|
||||||
|
if (!maliciousStrings.isEmpty()) {
|
||||||
|
warningText += "<br>In the imported configuration, potentially dangerous lines were found:";
|
||||||
|
for (const auto &string : maliciousStrings) {
|
||||||
|
warningText += QString("<br><i>%1</i>").arg(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImportController::processAmneziaConfig(QJsonObject &config) const
|
||||||
|
{
|
||||||
|
auto containers = config.value(configKey::containers).toArray();
|
||||||
|
for (auto i = 0; i < containers.size(); i++) {
|
||||||
|
auto container = containers.at(i).toObject();
|
||||||
|
auto dockerContainer = ContainerUtils::containerFromString(container.value(configKey::container).toString());
|
||||||
|
if (ContainerUtils::isAwgContainer(dockerContainer) || dockerContainer == DockerContainer::WireGuard) {
|
||||||
|
auto containerConfig = container.value(ContainerUtils::containerTypeToProtocolString(dockerContainer)).toObject();
|
||||||
|
auto protocolConfig = containerConfig.value(configKey::lastConfig).toString();
|
||||||
|
if (protocolConfig.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject jsonConfig = QJsonDocument::fromJson(protocolConfig.toUtf8()).object();
|
||||||
|
jsonConfig[configKey::mtu] =
|
||||||
|
ContainerUtils::isAwgContainer(dockerContainer) ? protocols::awg::defaultMtu : protocols::wireguard::defaultMtu;
|
||||||
|
|
||||||
|
containerConfig[configKey::lastConfig] = QString(QJsonDocument(jsonConfig).toJson());
|
||||||
|
|
||||||
|
container[ContainerUtils::containerTypeToProtocolString(dockerContainer)] = containerConfig;
|
||||||
|
containers.replace(i, container);
|
||||||
|
config.insert(configKey::containers, containers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
91
client/core/controllers/selfhosted/importController.h
Normal file
91
client/core/controllers/selfhosted/importController.h
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#ifndef IMPORTCONTROLLER_H
|
||||||
|
#define IMPORTCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
#include "core/repositories/secureServersRepository.h"
|
||||||
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
enum class ConfigTypes {
|
||||||
|
Amnezia,
|
||||||
|
OpenVpn,
|
||||||
|
WireGuard,
|
||||||
|
Awg,
|
||||||
|
Xray,
|
||||||
|
ShadowSocks,
|
||||||
|
Backup,
|
||||||
|
Invalid
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
|
||||||
|
class ImportController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct ImportResult
|
||||||
|
{
|
||||||
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
|
QJsonObject config;
|
||||||
|
QString configFileName;
|
||||||
|
QString maliciousWarningText;
|
||||||
|
ConfigTypes configType = ConfigTypes::Invalid;
|
||||||
|
bool isNativeWireGuardConfig = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit ImportController(SecureServersRepository* serversRepository,
|
||||||
|
SecureAppSettingsRepository* appSettingsRepository,
|
||||||
|
QObject *parent = nullptr);
|
||||||
|
|
||||||
|
struct QrParseResult {
|
||||||
|
bool success = false;
|
||||||
|
ImportResult importResult;
|
||||||
|
int chunksReceived = 0;
|
||||||
|
int chunksTotal = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
ImportResult extractConfigFromData(const QString &data, const QString &configFileName = "");
|
||||||
|
ImportResult extractConfigFromQr(const QByteArray &data);
|
||||||
|
|
||||||
|
void startDecodingQr();
|
||||||
|
QrParseResult parseQrCodeChunk(const QString &code);
|
||||||
|
bool isQrDecodingActive() const;
|
||||||
|
int qrChunksReceived() const;
|
||||||
|
int qrChunksTotal() const;
|
||||||
|
|
||||||
|
void importConfig(const QJsonObject &config);
|
||||||
|
QJsonObject processNativeWireGuardConfig(const QJsonObject &config);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void importFinished();
|
||||||
|
void importErrorOccurred(ErrorCode errorCode, bool goToPageHome);
|
||||||
|
void restoreAppConfig(const QByteArray &data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConfigTypes checkConfigFormat(const QString &config) const;
|
||||||
|
QJsonObject extractOpenVpnConfig(const QString &data) const;
|
||||||
|
QJsonObject extractWireGuardConfig(const QString &data, ConfigTypes &configType) const;
|
||||||
|
QJsonObject extractXrayConfig(const QString &data, ConfigTypes configType, const QString &description = "") const;
|
||||||
|
void checkForMaliciousStrings(const QJsonObject &serverConfig, QString &warningText) const;
|
||||||
|
void processAmneziaConfig(QJsonObject &config) const;
|
||||||
|
|
||||||
|
SecureServersRepository* m_serversRepository;
|
||||||
|
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||||
|
|
||||||
|
QMap<int, QByteArray> m_qrCodeChunks;
|
||||||
|
bool m_isQrCodeProcessed = false;
|
||||||
|
int m_totalQrCodeChunksCount = 0;
|
||||||
|
int m_receivedQrCodeChunksCount = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // IMPORTCONTROLLER_H
|
||||||
1179
client/core/controllers/selfhosted/installController.cpp
Normal file
1179
client/core/controllers/selfhosted/installController.cpp
Normal file
File diff suppressed because it is too large
Load Diff
117
client/core/controllers/selfhosted/installController.h
Normal file
117
client/core/controllers/selfhosted/installController.h
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
#ifndef INSTALLCONTROLLER_H
|
||||||
|
#define INSTALLCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
#include <QProcess>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
#include "core/models/containerConfig.h"
|
||||||
|
#include "core/repositories/secureServersRepository.h"
|
||||||
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
|
|
||||||
|
class SshSession;
|
||||||
|
class InstallerBase;
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
|
||||||
|
class InstallController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit InstallController(SecureServersRepository* serversRepository,
|
||||||
|
SecureAppSettingsRepository* appSettingsRepository,
|
||||||
|
QObject *parent = nullptr);
|
||||||
|
~InstallController();
|
||||||
|
|
||||||
|
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, bool isUpdate = false);
|
||||||
|
ErrorCode updateContainer(int serverIndex, DockerContainer container, const ContainerConfig &oldConfig, ContainerConfig &newConfig);
|
||||||
|
|
||||||
|
ErrorCode rebootServer(int serverIndex);
|
||||||
|
ErrorCode removeAllContainers(int serverIndex);
|
||||||
|
ErrorCode removeContainer(int serverIndex, DockerContainer container);
|
||||||
|
|
||||||
|
ContainerConfig generateConfig(DockerContainer container, int port, TransportProto transportProto);
|
||||||
|
ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials, QMap<DockerContainer, ContainerConfig> &installedContainers, SshSession &sshSession);
|
||||||
|
|
||||||
|
ErrorCode scanServerForInstalledContainers(int serverIndex);
|
||||||
|
|
||||||
|
ErrorCode installContainer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto, ContainerConfig &config);
|
||||||
|
|
||||||
|
ErrorCode installServer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto,
|
||||||
|
bool &wasContainerInstalled);
|
||||||
|
ErrorCode installContainer(int serverIndex, DockerContainer container, int port, TransportProto transportProto,
|
||||||
|
bool &wasContainerInstalled);
|
||||||
|
|
||||||
|
bool isUpdateDockerContainerRequired(DockerContainer container, const ContainerConfig &oldConfig, const ContainerConfig &newConfig);
|
||||||
|
|
||||||
|
ErrorCode checkSshConnection(const ServerCredentials &credentials, QString &output, std::function<QString()> passphraseCallback = nullptr);
|
||||||
|
|
||||||
|
bool isServerAlreadyExists(const ServerCredentials &credentials, int &existingServerIndex);
|
||||||
|
|
||||||
|
ErrorCode mountSftpDrive(const ServerCredentials &credentials, const QString &port, const QString &password, const QString &username);
|
||||||
|
void stopAllSftpMounts();
|
||||||
|
|
||||||
|
void cancelInstallation();
|
||||||
|
|
||||||
|
void clearCachedProfile(int serverIndex, DockerContainer container);
|
||||||
|
|
||||||
|
ErrorCode validateAndPrepareConfig(int serverIndex);
|
||||||
|
|
||||||
|
void validateConfig(int serverIndex);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void configValidated(bool isValid);
|
||||||
|
void validationErrorOccurred(ErrorCode errorCode);
|
||||||
|
|
||||||
|
void serverIsBusy(const bool isBusy);
|
||||||
|
void cancelInstallationRequested();
|
||||||
|
void clientRevocationRequested(int serverIndex, const ContainerConfig &containerConfig, DockerContainer container);
|
||||||
|
void clientAppendRequested(int serverIndex, const QString &clientId, const QString &clientName, DockerContainer container);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container, SshSession &sshSession);
|
||||||
|
ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, SshSession &sshSession);
|
||||||
|
ErrorCode buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config, SshSession &sshSession);
|
||||||
|
ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, SshSession &sshSession);
|
||||||
|
ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, SshSession &sshSession);
|
||||||
|
ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config, SshSession &sshSession);
|
||||||
|
|
||||||
|
ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config, SshSession &sshSession);
|
||||||
|
ErrorCode isUserInSudo(const ServerCredentials &credentials, SshSession &sshSession);
|
||||||
|
ErrorCode isServerDpkgBusy(const ServerCredentials &credentials, SshSession &sshSession);
|
||||||
|
ErrorCode setupServerFirewall(const ServerCredentials &credentials, SshSession &sshSession);
|
||||||
|
bool isReinstallContainerRequired(DockerContainer container, const ContainerConfig &oldConfig, const ContainerConfig &newConfig);
|
||||||
|
|
||||||
|
ErrorCode prepareContainerConfig(DockerContainer container, const ServerCredentials &credentials, ContainerConfig &containerConfig, SshSession &sshSession);
|
||||||
|
|
||||||
|
ErrorCode processContainerForAdmin(DockerContainer container, ContainerConfig &containerConfig,
|
||||||
|
const ServerCredentials &credentials, SshSession &sshSession,
|
||||||
|
int serverIndex, const QString &clientName);
|
||||||
|
|
||||||
|
void adminAppendRequested(int serverIndex, DockerContainer container,
|
||||||
|
const ContainerConfig &containerConfig, const QString &clientName);
|
||||||
|
|
||||||
|
static void updateContainerConfigAfterInstallation(DockerContainer container, ContainerConfig &containerConfig, const QString &stdOut);
|
||||||
|
|
||||||
|
QScopedPointer<InstallerBase> createInstaller(DockerContainer container);
|
||||||
|
|
||||||
|
SecureServersRepository* m_serversRepository;
|
||||||
|
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||||
|
bool m_cancelInstallation = false;
|
||||||
|
|
||||||
|
#ifndef Q_OS_IOS
|
||||||
|
QList<QSharedPointer<QProcess>> m_sftpMountProcesses;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INSTALLCONTROLLER_H
|
||||||
|
|
||||||
807
client/core/controllers/selfhosted/usersController.cpp
Normal file
807
client/core/controllers/selfhosted/usersController.cpp
Normal file
@@ -0,0 +1,807 @@
|
|||||||
|
#include "usersController.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
|
#include "core/utils/selfhosted/scriptsRegistry.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
|
#include "core/models/containerConfig.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
Logger logger("UsersController");
|
||||||
|
}
|
||||||
|
|
||||||
|
UsersController::UsersController(SecureServersRepository* serversRepository, QObject *parent)
|
||||||
|
: QObject(parent),
|
||||||
|
m_serversRepository(serversRepository)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UsersController::isClientExists(const QString &clientId, const QJsonArray &clientsTable)
|
||||||
|
{
|
||||||
|
for (const QJsonValue &value : std::as_const(clientsTable)) {
|
||||||
|
if (value.isObject()) {
|
||||||
|
QJsonObject obj = value.toObject();
|
||||||
|
if (obj.contains(configKey::clientId) && obj[configKey::clientId].toString() == clientId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int UsersController::clientIndexById(const QString &clientId, const QJsonArray &clientsTable)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < clientsTable.size(); ++i) {
|
||||||
|
if (clientsTable.at(i).isObject()) {
|
||||||
|
QJsonObject obj = clientsTable.at(i).toObject();
|
||||||
|
if (obj.contains(configKey::clientId) && obj[configKey::clientId].toString() == clientId) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UsersController::migration(const QByteArray &clientsTableString, QJsonArray &clientsTable)
|
||||||
|
{
|
||||||
|
QJsonObject clientsTableObj = QJsonDocument::fromJson(clientsTableString).object();
|
||||||
|
|
||||||
|
for (auto &clientId : clientsTableObj.keys()) {
|
||||||
|
QJsonObject client;
|
||||||
|
client[configKey::clientId] = clientId;
|
||||||
|
|
||||||
|
QJsonObject userData;
|
||||||
|
userData[configKey::clientName] = clientsTableObj.value(clientId).toObject().value(configKey::clientName);
|
||||||
|
client[configKey::userData] = userData;
|
||||||
|
|
||||||
|
clientsTable.push_back(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode UsersController::wgShow(const DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, std::vector<WgShowData> &data)
|
||||||
|
{
|
||||||
|
if (container != DockerContainer::WireGuard && !ContainerUtils::isAwgContainer(container)) {
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode error = ErrorCode::NoError;
|
||||||
|
QString stdOut;
|
||||||
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
};
|
||||||
|
|
||||||
|
QString showBin = (container == DockerContainer::Awg2)
|
||||||
|
? QStringLiteral("awg")
|
||||||
|
: QStringLiteral("wg");
|
||||||
|
const QString command = QString("sudo docker exec -i $CONTAINER_NAME bash -c '%1 show all'").arg(showBin);
|
||||||
|
|
||||||
|
QString script = sshSession->replaceVars(command, amnezia::genBaseVars(credentials, container, QString(), QString()));
|
||||||
|
error = sshSession->runScript(credentials, script, cbReadStdOut);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << QString("Failed to execute %1 show command").arg(showBin);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdOut.isEmpty()) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto getStrValue = [](const auto str) { return str.mid(str.indexOf(":") + 1).trimmed(); };
|
||||||
|
|
||||||
|
const auto parts = stdOut.split('\n');
|
||||||
|
const auto peerList = parts.filter("peer:");
|
||||||
|
const auto latestHandshakeList = parts.filter("latest handshake:");
|
||||||
|
const auto transferredDataList = parts.filter("transfer:");
|
||||||
|
const auto allowedIpsList = parts.filter("allowed ips:");
|
||||||
|
|
||||||
|
if (allowedIpsList.isEmpty() || latestHandshakeList.isEmpty() || transferredDataList.isEmpty() || peerList.isEmpty()) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto changeHandshakeFormat = [](QString &latestHandshake) {
|
||||||
|
const std::vector<std::pair<QString, QString>> replaceMap = { { " days", "d" }, { " hours", "h" }, { " minutes", "m" },
|
||||||
|
{ " seconds", "s" }, { " day", "d" }, { " hour", "h" },
|
||||||
|
{ " minute", "m" }, { " second", "s" } };
|
||||||
|
|
||||||
|
for (const auto &item : replaceMap) {
|
||||||
|
latestHandshake.replace(item.first, item.second);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < peerList.size() && i < transferredDataList.size() && i < latestHandshakeList.size() && i < allowedIpsList.size(); ++i) {
|
||||||
|
|
||||||
|
const auto transferredData = getStrValue(transferredDataList[i]).split(",");
|
||||||
|
auto latestHandshake = getStrValue(latestHandshakeList[i]);
|
||||||
|
auto serverBytesReceived = transferredData.front().trimmed();
|
||||||
|
auto serverBytesSent = transferredData.back().trimmed();
|
||||||
|
auto allowedIps = getStrValue(allowedIpsList[i]);
|
||||||
|
|
||||||
|
changeHandshakeFormat(latestHandshake);
|
||||||
|
|
||||||
|
serverBytesReceived.chop(QStringLiteral(" received").length());
|
||||||
|
serverBytesSent.chop(QStringLiteral(" sent").length());
|
||||||
|
|
||||||
|
data.push_back({ getStrValue(peerList[i]), latestHandshake, serverBytesSent, serverBytesReceived, allowedIps });
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode UsersController::getOpenVpnClients(const DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, int &count, QJsonArray &clientsTable)
|
||||||
|
{
|
||||||
|
ErrorCode error = ErrorCode::NoError;
|
||||||
|
QString stdOut;
|
||||||
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
};
|
||||||
|
|
||||||
|
const QString getOpenVpnClientsList = "sudo docker exec -i $CONTAINER_NAME bash -c 'ls /opt/amnezia/openvpn/pki/issued'";
|
||||||
|
QString script = sshSession->replaceVars(getOpenVpnClientsList, amnezia::genBaseVars(credentials, container, QString(), QString()));
|
||||||
|
error = sshSession->runScript(credentials, script, cbReadStdOut);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to retrieve the list of issued certificates on the server";
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stdOut.isEmpty()) {
|
||||||
|
QStringList certsIds = stdOut.split("\n", Qt::SkipEmptyParts);
|
||||||
|
certsIds.removeAll("AmneziaReq.crt");
|
||||||
|
|
||||||
|
for (auto &openvpnCertId : certsIds) {
|
||||||
|
openvpnCertId.replace(".crt", "");
|
||||||
|
if (!isClientExists(openvpnCertId, clientsTable)) {
|
||||||
|
QJsonObject client;
|
||||||
|
client[configKey::clientId] = openvpnCertId;
|
||||||
|
|
||||||
|
QJsonObject userData;
|
||||||
|
userData[configKey::clientName] = QString("Client %1").arg(count);
|
||||||
|
client[configKey::userData] = userData;
|
||||||
|
|
||||||
|
clientsTable.push_back(client);
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode UsersController::getWireGuardClients(const DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, int &count, QJsonArray &clientsTable)
|
||||||
|
{
|
||||||
|
ErrorCode error = ErrorCode::NoError;
|
||||||
|
|
||||||
|
QString configPath;
|
||||||
|
if (container == DockerContainer::Awg) {
|
||||||
|
configPath = QString::fromLatin1(protocols::awg::serverLegacyConfigPath);
|
||||||
|
} else if (container == DockerContainer::Awg2) {
|
||||||
|
configPath = QString::fromLatin1(protocols::awg::serverConfigPath);
|
||||||
|
} else {
|
||||||
|
configPath = QString::fromLatin1(protocols::wireguard::serverConfigPath);
|
||||||
|
}
|
||||||
|
const QString wireguardConfigString = sshSession->getTextFileFromContainer(container, credentials, configPath, error);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to get the wg conf file from the server";
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto configLines = wireguardConfigString.split("\n", Qt::SkipEmptyParts);
|
||||||
|
QStringList wireguardKeys;
|
||||||
|
for (const auto &line : configLines) {
|
||||||
|
auto configPair = line.split(" = ", Qt::SkipEmptyParts);
|
||||||
|
if (configPair.front() == "PublicKey") {
|
||||||
|
wireguardKeys.push_back(configPair.back());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &wireguardKey : wireguardKeys) {
|
||||||
|
if (!isClientExists(wireguardKey, clientsTable)) {
|
||||||
|
QJsonObject client;
|
||||||
|
client[configKey::clientId] = wireguardKey;
|
||||||
|
|
||||||
|
QJsonObject userData;
|
||||||
|
userData[configKey::clientName] = QString("Client %1").arg(count);
|
||||||
|
client[configKey::userData] = userData;
|
||||||
|
|
||||||
|
clientsTable.push_back(client);
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode UsersController::getXrayClients(const DockerContainer container, const ServerCredentials& credentials,
|
||||||
|
SshSession* sshSession, int &count, QJsonArray &clientsTable)
|
||||||
|
{
|
||||||
|
ErrorCode error = ErrorCode::NoError;
|
||||||
|
|
||||||
|
const QString serverConfigPath = amnezia::protocols::xray::serverConfigPath;
|
||||||
|
const QString configString = sshSession->getTextFileFromContainer(container, credentials, serverConfigPath, error);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to get the xray server config file from the server";
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument serverConfig = QJsonDocument::fromJson(configString.toUtf8());
|
||||||
|
if (serverConfig.isNull()) {
|
||||||
|
logger.error() << "Failed to parse xray server config JSON";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!serverConfig.object().contains(protocols::xray::inbounds) || serverConfig.object()[protocols::xray::inbounds].toArray().isEmpty()) {
|
||||||
|
logger.error() << "Invalid xray server config structure";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QJsonObject inbound = serverConfig.object()[protocols::xray::inbounds].toArray()[0].toObject();
|
||||||
|
if (!inbound.contains(protocols::xray::settings)) {
|
||||||
|
logger.error() << "Missing settings in xray inbound config";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QJsonObject settings = inbound[protocols::xray::settings].toObject();
|
||||||
|
if (!settings.contains(protocols::xray::clients)) {
|
||||||
|
logger.error() << "Missing clients in xray settings config";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QJsonArray clients = settings[protocols::xray::clients].toArray();
|
||||||
|
for (const auto &clientValue : clients) {
|
||||||
|
const QJsonObject clientObj = clientValue.toObject();
|
||||||
|
if (!clientObj.contains(protocols::xray::id)) {
|
||||||
|
logger.error() << "Missing id in xray client config";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QString clientId = clientObj[protocols::xray::id].toString();
|
||||||
|
|
||||||
|
QString xrayDefaultUuid = sshSession->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::uuidPath, error);
|
||||||
|
xrayDefaultUuid.replace("\n", "");
|
||||||
|
|
||||||
|
if (!isClientExists(clientId, clientsTable) && clientId != xrayDefaultUuid) {
|
||||||
|
QJsonObject client;
|
||||||
|
client[configKey::clientId] = clientId;
|
||||||
|
|
||||||
|
QJsonObject userData;
|
||||||
|
userData[configKey::clientName] = QString("Client %1").arg(count);
|
||||||
|
client[configKey::userData] = userData;
|
||||||
|
|
||||||
|
clientsTable.push_back(client);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode UsersController::updateClients(int serverIndex, const DockerContainer container)
|
||||||
|
{
|
||||||
|
ErrorCode error = ErrorCode::NoError;
|
||||||
|
SshSession sshSession;
|
||||||
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
|
|
||||||
|
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
|
||||||
|
if (container == DockerContainer::OpenVpn) {
|
||||||
|
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(DockerContainer::OpenVpn));
|
||||||
|
} else {
|
||||||
|
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(container));
|
||||||
|
}
|
||||||
|
|
||||||
|
const QByteArray clientsTableString = sshSession.getTextFileFromContainer(container, credentials, clientsTableFile, error);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to get the clientsTable file from the server";
|
||||||
|
emit clientsUpdated(QJsonArray());
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_clientsTable = QJsonDocument::fromJson(clientsTableString).array();
|
||||||
|
|
||||||
|
if (m_clientsTable.isEmpty()) {
|
||||||
|
migration(clientsTableString, m_clientsTable);
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
if (container == DockerContainer::OpenVpn) {
|
||||||
|
error = getOpenVpnClients(container, credentials, &sshSession, count, m_clientsTable);
|
||||||
|
} else if (container == DockerContainer::WireGuard || ContainerUtils::isAwgContainer(container)) {
|
||||||
|
error = getWireGuardClients(container, credentials, &sshSession, count, m_clientsTable);
|
||||||
|
} else if (container == DockerContainer::Xray) {
|
||||||
|
error = getXrayClients(container, credentials, &sshSession, count, m_clientsTable);
|
||||||
|
}
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
emit clientsUpdated(QJsonArray());
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QByteArray newClientsTableString = QJsonDocument(m_clientsTable).toJson();
|
||||||
|
if (clientsTableString != newClientsTableString) {
|
||||||
|
error = sshSession.uploadTextFileToContainer(container, credentials, newClientsTableString, clientsTableFile);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to upload the clientsTable file to the server";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<WgShowData> data;
|
||||||
|
wgShow(container, credentials, &sshSession, data);
|
||||||
|
|
||||||
|
for (const auto &client : data) {
|
||||||
|
int i = 0;
|
||||||
|
for (const auto &it : std::as_const(m_clientsTable)) {
|
||||||
|
if (it.isObject()) {
|
||||||
|
QJsonObject obj = it.toObject();
|
||||||
|
if (obj.contains(configKey::clientId) && obj[configKey::clientId].toString() == client.clientId) {
|
||||||
|
QJsonObject userData = obj[configKey::userData].toObject();
|
||||||
|
|
||||||
|
if (!client.latestHandshake.isEmpty()) {
|
||||||
|
userData[configKey::latestHandshake] = client.latestHandshake;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client.dataReceived.isEmpty()) {
|
||||||
|
userData[configKey::dataReceived] = client.dataReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client.dataSent.isEmpty()) {
|
||||||
|
userData[configKey::dataSent] = client.dataSent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client.allowedIps.isEmpty()) {
|
||||||
|
userData[configKey::allowedIps] = client.allowedIps;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj[configKey::userData] = userData;
|
||||||
|
m_clientsTable.replace(i, obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit clientsUpdated(m_clientsTable);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ErrorCode UsersController::appendClient(int serverIndex, const QString &clientId, const QString &clientName, const DockerContainer container)
|
||||||
|
{
|
||||||
|
ErrorCode error = ErrorCode::NoError;
|
||||||
|
SshSession sshSession;
|
||||||
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
|
|
||||||
|
error = updateClients(serverIndex, container);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int existingIndex = clientIndexById(clientId, m_clientsTable);
|
||||||
|
if (existingIndex >= 0) {
|
||||||
|
return renameClient(serverIndex, existingIndex, clientName, container, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject client;
|
||||||
|
client[configKey::clientId] = clientId;
|
||||||
|
|
||||||
|
QJsonObject userData;
|
||||||
|
userData[configKey::clientName] = clientName;
|
||||||
|
userData[configKey::creationDate] = QDateTime::currentDateTime().toString();
|
||||||
|
client[configKey::userData] = userData;
|
||||||
|
m_clientsTable.push_back(client);
|
||||||
|
|
||||||
|
const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson();
|
||||||
|
|
||||||
|
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
|
||||||
|
if (container == DockerContainer::OpenVpn) {
|
||||||
|
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(DockerContainer::OpenVpn));
|
||||||
|
} else {
|
||||||
|
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(container));
|
||||||
|
}
|
||||||
|
|
||||||
|
error = sshSession.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to upload the clientsTable file to the server";
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit clientAdded(client);
|
||||||
|
emit clientsUpdated(m_clientsTable);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode UsersController::renameClient(int serverIndex, const int row, const QString &clientName,
|
||||||
|
const DockerContainer container, bool addTimeStamp)
|
||||||
|
{
|
||||||
|
if (row < 0 || row >= m_clientsTable.size()) {
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshSession sshSession;
|
||||||
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
|
|
||||||
|
auto client = m_clientsTable.at(row).toObject();
|
||||||
|
auto userData = client[configKey::userData].toObject();
|
||||||
|
userData[configKey::clientName] = clientName;
|
||||||
|
if (addTimeStamp) {
|
||||||
|
userData[configKey::creationDate] = QDateTime::currentDateTime().toString();
|
||||||
|
}
|
||||||
|
client[configKey::userData] = userData;
|
||||||
|
|
||||||
|
m_clientsTable.replace(row, client);
|
||||||
|
|
||||||
|
const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson();
|
||||||
|
|
||||||
|
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
|
||||||
|
if (container == DockerContainer::OpenVpn) {
|
||||||
|
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(DockerContainer::OpenVpn));
|
||||||
|
} else {
|
||||||
|
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(container));
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode error = sshSession.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to upload the clientsTable file to the server";
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addTimeStamp) {
|
||||||
|
emit clientsUpdated(m_clientsTable);
|
||||||
|
} else {
|
||||||
|
emit clientRenamed(row, clientName);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode UsersController::revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
const int serverIndex, SshSession* sshSession, QJsonArray &clientsTable)
|
||||||
|
{
|
||||||
|
if (row < 0 || row >= clientsTable.size()) {
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto client = clientsTable.at(row).toObject();
|
||||||
|
QString clientId = client.value(configKey::clientId).toString();
|
||||||
|
|
||||||
|
const QString getOpenVpnCertData = QString("sudo docker exec -i $CONTAINER_NAME bash -c '"
|
||||||
|
"cd /opt/amnezia/openvpn ;\\"
|
||||||
|
"easyrsa revoke %1 ;\\"
|
||||||
|
"easyrsa gen-crl ;\\"
|
||||||
|
"chmod 666 pki/crl.pem ;\\"
|
||||||
|
"cp pki/crl.pem .'")
|
||||||
|
.arg(clientId);
|
||||||
|
|
||||||
|
const QString script = sshSession->replaceVars(getOpenVpnCertData, amnezia::genBaseVars(credentials, container, QString(), QString()));
|
||||||
|
ErrorCode error = sshSession->runScript(credentials, script);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to revoke the certificate";
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
clientsTable.removeAt(row);
|
||||||
|
|
||||||
|
const QByteArray clientsTableString = QJsonDocument(clientsTable).toJson();
|
||||||
|
|
||||||
|
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
|
||||||
|
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(DockerContainer::OpenVpn));
|
||||||
|
error = sshSession->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to upload the clientsTable file to the server";
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode UsersController::revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, QJsonArray &clientsTable)
|
||||||
|
{
|
||||||
|
if (row < 0 || row >= clientsTable.size()) {
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode error = ErrorCode::NoError;
|
||||||
|
|
||||||
|
QString configPath;
|
||||||
|
if (container == DockerContainer::Awg) {
|
||||||
|
configPath = QString::fromLatin1(protocols::awg::serverLegacyConfigPath);
|
||||||
|
} else if (container == DockerContainer::Awg2) {
|
||||||
|
configPath = QString::fromLatin1(protocols::awg::serverConfigPath);
|
||||||
|
} else {
|
||||||
|
configPath = QString::fromLatin1(protocols::wireguard::serverConfigPath);
|
||||||
|
}
|
||||||
|
const QString wireguardConfigString = sshSession->getTextFileFromContainer(container, credentials, configPath, error);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to get the wg conf file from the server";
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto client = clientsTable.at(row).toObject();
|
||||||
|
QString clientId = client.value(configKey::clientId).toString();
|
||||||
|
|
||||||
|
auto configSections = wireguardConfigString.split("[", Qt::SkipEmptyParts);
|
||||||
|
for (auto §ion : configSections) {
|
||||||
|
if (section.contains(clientId)) {
|
||||||
|
configSections.removeOne(section);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QString newWireGuardConfig = configSections.join("[");
|
||||||
|
newWireGuardConfig.insert(0, "[");
|
||||||
|
error = sshSession->uploadTextFileToContainer(container, credentials, newWireGuardConfig, configPath);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to upload the wg conf file to the server";
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
clientsTable.removeAt(row);
|
||||||
|
|
||||||
|
const QByteArray clientsTableString = QJsonDocument(clientsTable).toJson();
|
||||||
|
|
||||||
|
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
|
||||||
|
if (container == DockerContainer::OpenVpn) {
|
||||||
|
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(DockerContainer::OpenVpn));
|
||||||
|
} else {
|
||||||
|
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(container));
|
||||||
|
}
|
||||||
|
error = sshSession->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to upload the clientsTable file to the server";
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isAwg2 = (container == DockerContainer::Awg2);
|
||||||
|
QString command = isAwg2 ? QStringLiteral("awg") : QStringLiteral("wg");
|
||||||
|
QString iface = isAwg2 ? QStringLiteral("awg0") : QStringLiteral("wg0");
|
||||||
|
QString script = QString(
|
||||||
|
"sudo docker exec -i $CONTAINER_NAME bash -c '%1 syncconf %2 <(%1-quick strip %3)'"
|
||||||
|
).arg(command, iface, configPath);
|
||||||
|
error = sshSession->runScript(
|
||||||
|
credentials,
|
||||||
|
sshSession->replaceVars(script, amnezia::genBaseVars(credentials, container, QString(), QString()))
|
||||||
|
);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << QString("Failed to execute command '%1 syncconf %2' on the server").arg(command, iface);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode UsersController::revokeXray(const int row,
|
||||||
|
const DockerContainer container,
|
||||||
|
const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, QJsonArray &clientsTable)
|
||||||
|
{
|
||||||
|
if (row < 0 || row >= clientsTable.size()) {
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode error = ErrorCode::NoError;
|
||||||
|
|
||||||
|
const QString serverConfigPath = amnezia::protocols::xray::serverConfigPath;
|
||||||
|
const QString configString = sshSession->getTextFileFromContainer(container, credentials, serverConfigPath, error);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to get the xray server config file";
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument serverConfig = QJsonDocument::fromJson(configString.toUtf8());
|
||||||
|
if (serverConfig.isNull()) {
|
||||||
|
logger.error() << "Failed to parse xray server config JSON";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto client = clientsTable.at(row).toObject();
|
||||||
|
QString clientId = client.value(configKey::clientId).toString();
|
||||||
|
|
||||||
|
QJsonObject configObj = serverConfig.object();
|
||||||
|
if (!configObj.contains(protocols::xray::inbounds)) {
|
||||||
|
logger.error() << "Missing inbounds in xray config";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray inbounds = configObj[protocols::xray::inbounds].toArray();
|
||||||
|
if (inbounds.isEmpty()) {
|
||||||
|
logger.error() << "Empty inbounds array in xray config";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject inbound = inbounds[0].toObject();
|
||||||
|
if (!inbound.contains(protocols::xray::settings)) {
|
||||||
|
logger.error() << "Missing settings in xray inbound config";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject settings = inbound[protocols::xray::settings].toObject();
|
||||||
|
if (!settings.contains(protocols::xray::clients)) {
|
||||||
|
logger.error() << "Missing clients in xray settings";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray clients = settings[protocols::xray::clients].toArray();
|
||||||
|
if (clients.isEmpty()) {
|
||||||
|
logger.error() << "Empty clients array in xray config";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < clients.size(); ++i) {
|
||||||
|
QJsonObject clientObj = clients[i].toObject();
|
||||||
|
if (clientObj.contains(protocols::xray::id) && clientObj[protocols::xray::id].toString() == clientId) {
|
||||||
|
clients.removeAt(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
settings[protocols::xray::clients] = clients;
|
||||||
|
inbound[protocols::xray::settings] = settings;
|
||||||
|
inbounds[0] = inbound;
|
||||||
|
configObj[protocols::xray::inbounds] = inbounds;
|
||||||
|
|
||||||
|
error = sshSession->uploadTextFileToContainer(
|
||||||
|
container,
|
||||||
|
credentials,
|
||||||
|
QJsonDocument(configObj).toJson(),
|
||||||
|
serverConfigPath
|
||||||
|
);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to upload updated xray config";
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
clientsTable.removeAt(row);
|
||||||
|
|
||||||
|
const QByteArray clientsTableString = QJsonDocument(clientsTable).toJson();
|
||||||
|
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable")
|
||||||
|
.arg(ContainerUtils::containerTypeToString(container));
|
||||||
|
|
||||||
|
error = sshSession->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to upload the clientsTable file";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString restartScript = QString("sudo docker restart $CONTAINER_NAME");
|
||||||
|
error = sshSession->runScript(
|
||||||
|
credentials,
|
||||||
|
sshSession->replaceVars(restartScript, amnezia::genBaseVars(credentials, container, QString(), QString()))
|
||||||
|
);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
logger.error() << "Failed to restart xray container";
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode UsersController::revokeClient(int serverIndex, const int index, const DockerContainer container)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= m_clientsTable.size()) {
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
SshSession sshSession;
|
||||||
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
|
|
||||||
|
QString clientId = m_clientsTable.at(index).toObject().value(configKey::clientId).toString();
|
||||||
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
|
|
||||||
|
switch(container)
|
||||||
|
{
|
||||||
|
case DockerContainer::OpenVpn: {
|
||||||
|
errorCode = revokeOpenVpn(index, container, credentials, serverIndex, &sshSession, m_clientsTable);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DockerContainer::WireGuard:
|
||||||
|
case DockerContainer::Awg:
|
||||||
|
case DockerContainer::Awg2: {
|
||||||
|
errorCode = revokeWireGuard(index, container, credentials, &sshSession, m_clientsTable);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DockerContainer::Xray: {
|
||||||
|
errorCode = revokeXray(index, container, credentials, &sshSession, m_clientsTable);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
logger.error() << "Internal error: received unexpected container type";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorCode == ErrorCode::NoError) {
|
||||||
|
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
|
||||||
|
ContainerConfig containerCfg = m_serversRepository->containerConfig(serverIndex, container);
|
||||||
|
QString containerClientId = containerCfg.protocolConfig.clientId();
|
||||||
|
|
||||||
|
if (!clientId.isEmpty() && !containerClientId.isEmpty() && containerClientId.contains(clientId)) {
|
||||||
|
emit adminConfigRevoked(serverIndex, container);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit clientRevoked(index);
|
||||||
|
emit clientsUpdated(m_clientsTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode UsersController::revokeClient(int serverIndex, const ContainerConfig &containerConfig, const DockerContainer container)
|
||||||
|
{
|
||||||
|
SshSession sshSession;
|
||||||
|
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||||
|
|
||||||
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
|
errorCode = updateClients(serverIndex, container);
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
Proto protocol = containerConfig.getProtocolType();
|
||||||
|
|
||||||
|
switch(container)
|
||||||
|
{
|
||||||
|
case DockerContainer::OpenVpn:
|
||||||
|
case DockerContainer::WireGuard:
|
||||||
|
case DockerContainer::Awg:
|
||||||
|
case DockerContainer::Awg2:
|
||||||
|
case DockerContainer::Xray: {
|
||||||
|
protocol = ContainerUtils::defaultProtocol(container);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
logger.error() << "Internal error: received unexpected container type";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString clientId = containerConfig.protocolConfig.clientId();
|
||||||
|
|
||||||
|
int row = clientIndexById(clientId, m_clientsTable);
|
||||||
|
if (row < 0) {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (container)
|
||||||
|
{
|
||||||
|
case DockerContainer::OpenVpn: {
|
||||||
|
errorCode = revokeOpenVpn(row, container, credentials, serverIndex, &sshSession, m_clientsTable);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DockerContainer::WireGuard:
|
||||||
|
case DockerContainer::Awg:
|
||||||
|
case DockerContainer::Awg2: {
|
||||||
|
errorCode = revokeWireGuard(row, container, credentials, &sshSession, m_clientsTable);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DockerContainer::Xray: {
|
||||||
|
errorCode = revokeXray(row, container, credentials, &sshSession, m_clientsTable);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
logger.error() << "Internal error: received unexpected container type";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorCode == ErrorCode::NoError) {
|
||||||
|
emit adminConfigRevoked(serverIndex, container);
|
||||||
|
emit clientRevoked(row);
|
||||||
|
emit clientsUpdated(m_clientsTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
76
client/core/controllers/selfhosted/usersController.h
Normal file
76
client/core/controllers/selfhosted/usersController.h
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#ifndef USERSCONTROLLER_H
|
||||||
|
#define USERSCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
#include "core/repositories/secureServersRepository.h"
|
||||||
|
#include "core/models/containerConfig.h"
|
||||||
|
#include "core/models/protocolConfig.h"
|
||||||
|
|
||||||
|
class UsersController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct WgShowData
|
||||||
|
{
|
||||||
|
QString clientId;
|
||||||
|
QString latestHandshake;
|
||||||
|
QString dataReceived;
|
||||||
|
QString dataSent;
|
||||||
|
QString allowedIps;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit UsersController(SecureServersRepository* serversRepository, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void clientsUpdated(const QJsonArray &clients);
|
||||||
|
void clientAdded(const QJsonObject &client);
|
||||||
|
void clientRenamed(int row, const QString &newName);
|
||||||
|
void clientRevoked(int row);
|
||||||
|
void adminConfigRevoked(int serverIndex, DockerContainer container);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
ErrorCode updateClients(int serverIndex, const DockerContainer container);
|
||||||
|
ErrorCode appendClient(int serverIndex, const QString &clientId, const QString &clientName, const DockerContainer container);
|
||||||
|
ErrorCode renameClient(int serverIndex, const int row, const QString &userName, const DockerContainer container, bool addTimeStamp = false);
|
||||||
|
ErrorCode revokeClient(int serverIndex, const int index, const DockerContainer container);
|
||||||
|
ErrorCode revokeClient(int serverIndex, const ContainerConfig &containerConfig, const DockerContainer container);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool isClientExists(const QString &clientId, const QJsonArray &clientsTable);
|
||||||
|
int clientIndexById(const QString &clientId, const QJsonArray &clientsTable);
|
||||||
|
void migration(const QByteArray &clientsTableString, QJsonArray &clientsTable);
|
||||||
|
|
||||||
|
ErrorCode revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials, const int serverIndex,
|
||||||
|
SshSession* sshSession, QJsonArray &clientsTable);
|
||||||
|
ErrorCode revokeWireGuard(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, QJsonArray &clientsTable);
|
||||||
|
ErrorCode revokeXray(const int row, const DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, QJsonArray &clientsTable);
|
||||||
|
|
||||||
|
ErrorCode getOpenVpnClients(const DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, int &count, QJsonArray &clientsTable);
|
||||||
|
ErrorCode getWireGuardClients(const DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, int &count, QJsonArray &clientsTable);
|
||||||
|
ErrorCode getXrayClients(const DockerContainer container, const ServerCredentials& credentials,
|
||||||
|
SshSession* sshSession, int &count, QJsonArray &clientsTable);
|
||||||
|
|
||||||
|
ErrorCode wgShow(const DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, std::vector<WgShowData> &data);
|
||||||
|
|
||||||
|
SecureServersRepository* m_serversRepository;
|
||||||
|
QJsonArray m_clientsTable;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // USERSCONTROLLER_H
|
||||||
|
|
||||||
@@ -1,887 +0,0 @@
|
|||||||
#include "serverController.h"
|
|
||||||
|
|
||||||
#include <QCryptographicHash>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QEventLoop>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QLoggingCategory>
|
|
||||||
#include <QPointer>
|
|
||||||
#include <QTemporaryFile>
|
|
||||||
#include <QThread>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QtConcurrent>
|
|
||||||
|
|
||||||
#include <filesystem>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
|
||||||
#include "core/networkUtilities.h"
|
|
||||||
#include "core/scripts_registry.h"
|
|
||||||
#include "core/server_defs.h"
|
|
||||||
#include "logger.h"
|
|
||||||
#include "settings.h"
|
|
||||||
#include "utilities.h"
|
|
||||||
#include "vpnConfigurationController.h"
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
Logger logger("ServerController");
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerController::ServerController(std::shared_ptr<Settings> settings, QObject *parent) : m_settings(settings)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
|
|
||||||
auto error = m_sshClient.connectToHost(credentials);
|
|
||||||
if (error != ErrorCode::NoError) {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
script.replace("\r", "");
|
|
||||||
|
|
||||||
qDebug() << "ServerController::Run script";
|
|
||||||
|
|
||||||
QString totalLine;
|
|
||||||
const QStringList &lines = script.split("\n", Qt::SkipEmptyParts);
|
|
||||||
for (int i = 0; i < lines.count(); i++) {
|
|
||||||
QString currentLine = lines.at(i);
|
|
||||||
|
|
||||||
if (totalLine.isEmpty()) {
|
|
||||||
totalLine = currentLine;
|
|
||||||
} else {
|
|
||||||
totalLine = totalLine + "\n" + currentLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString lineToExec;
|
|
||||||
if (currentLine.endsWith("\\")) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
lineToExec = totalLine;
|
|
||||||
totalLine.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lineToExec.startsWith("#")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug().noquote() << lineToExec;
|
|
||||||
|
|
||||||
error = m_sshClient.executeCommand(lineToExec, cbReadStdOut, cbReadStdErr);
|
|
||||||
if (error != ErrorCode::NoError) {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug().noquote() << "ServerController::runScript finished\n";
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
|
|
||||||
|
|
||||||
ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName);
|
|
||||||
if (e)
|
|
||||||
return e;
|
|
||||||
|
|
||||||
QString runner =
|
|
||||||
QString("sudo docker exec -i $CONTAINER_NAME %2 %1 ").arg(fileName, (container == DockerContainer::Socks5Proxy ? "sh" : "bash"));
|
|
||||||
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);
|
|
||||||
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials, const QString &file,
|
|
||||||
const QString &path, libssh::ScpOverwriteMode 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;
|
|
||||||
|
|
||||||
QString stdOut;
|
|
||||||
auto cbReadStd = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data + "\n";
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) {
|
|
||||||
e = runScript(credentials,
|
|
||||||
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, path),
|
|
||||||
genVarsForScript(credentials, container)),
|
|
||||||
cbReadStd, cbReadStd);
|
|
||||||
|
|
||||||
if (e)
|
|
||||||
return e;
|
|
||||||
} else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) {
|
|
||||||
e = runScript(credentials,
|
|
||||||
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, tmpFileName),
|
|
||||||
genVarsForScript(credentials, container)),
|
|
||||||
cbReadStd, cbReadStd);
|
|
||||||
|
|
||||||
if (e)
|
|
||||||
return e;
|
|
||||||
|
|
||||||
e = runScript(credentials,
|
|
||||||
replaceVars(QStringLiteral("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName, path),
|
|
||||||
genVarsForScript(credentials, container)),
|
|
||||||
cbReadStd, cbReadStd);
|
|
||||||
|
|
||||||
if (e)
|
|
||||||
return e;
|
|
||||||
} else
|
|
||||||
return ErrorCode::NotImplementedError;
|
|
||||||
|
|
||||||
if (stdOut.contains("Error") && stdOut.contains("No such container")) {
|
|
||||||
return ErrorCode::ServerContainerMissingError;
|
|
||||||
}
|
|
||||||
|
|
||||||
runScript(credentials, replaceVars(QString("sudo shred -u %1").arg(tmpFileName), genVarsForScript(credentials, container)));
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray ServerController::getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials, const QString &path,
|
|
||||||
ErrorCode &errorCode)
|
|
||||||
{
|
|
||||||
|
|
||||||
errorCode = ErrorCode::NoError;
|
|
||||||
|
|
||||||
QString script = QStringLiteral("sudo docker exec -i %1 sh -c \"xxd -p '%2'\"").arg(ContainerProps::containerToString(container), path);
|
|
||||||
|
|
||||||
QString stdOut;
|
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data;
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
|
|
||||||
errorCode = runScript(credentials, script, cbReadStdOut);
|
|
||||||
return QByteArray::fromHex(stdOut.toUtf8());
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath,
|
|
||||||
libssh::ScpOverwriteMode overwriteMode)
|
|
||||||
{
|
|
||||||
auto error = m_sshClient.connectToHost(credentials);
|
|
||||||
if (error != ErrorCode::NoError) {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
QTemporaryFile localFile;
|
|
||||||
localFile.open();
|
|
||||||
localFile.write(data);
|
|
||||||
localFile.close();
|
|
||||||
|
|
||||||
error = m_sshClient.scpFileCopy(overwriteMode, localFile.fileName(), remotePath, "non_desc");
|
|
||||||
|
|
||||||
if (error != ErrorCode::NoError) {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode ServerController::rebootServer(const ServerCredentials &credentials)
|
|
||||||
{
|
|
||||||
QString script = QString("sudo reboot");
|
|
||||||
|
|
||||||
QString stdOut;
|
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data;
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data + "\n";
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
|
|
||||||
return runScript(credentials, script, cbReadStdOut, cbReadStdErr);
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode ServerController::removeAllContainers(const ServerCredentials &credentials)
|
|
||||||
{
|
|
||||||
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)));
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode ServerController::setupContainer(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config, bool isUpdate)
|
|
||||||
{
|
|
||||||
qDebug().noquote() << "ServerController::setupContainer" << ContainerProps::containerToString(container);
|
|
||||||
ErrorCode e = ErrorCode::NoError;
|
|
||||||
|
|
||||||
e = isUserInSudo(credentials, container);
|
|
||||||
if (e)
|
|
||||||
return e;
|
|
||||||
|
|
||||||
e = isServerDpkgBusy(credentials, container);
|
|
||||||
if (e)
|
|
||||||
return e;
|
|
||||||
|
|
||||||
e = installDockerWorker(credentials, container);
|
|
||||||
if (e)
|
|
||||||
return e;
|
|
||||||
qDebug().noquote() << "ServerController::setupContainer installDockerWorker finished";
|
|
||||||
|
|
||||||
if (!isUpdate) {
|
|
||||||
e = isServerPortBusy(credentials, container, config);
|
|
||||||
if (e)
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isUpdate) {
|
|
||||||
e = isServerPortBusy(credentials, container, config);
|
|
||||||
if (e)
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
e = prepareHostWorker(credentials, container, config);
|
|
||||||
if (e)
|
|
||||||
return e;
|
|
||||||
qDebug().noquote() << "ServerController::setupContainer prepareHostWorker finished";
|
|
||||||
|
|
||||||
removeContainer(credentials, container);
|
|
||||||
qDebug().noquote() << "ServerController::setupContainer removeContainer finished";
|
|
||||||
|
|
||||||
qDebug().noquote() << "buildContainerWorker start";
|
|
||||||
e = buildContainerWorker(credentials, container, config);
|
|
||||||
if (e)
|
|
||||||
return e;
|
|
||||||
qDebug().noquote() << "ServerController::setupContainer buildContainerWorker finished";
|
|
||||||
|
|
||||||
e = runContainerWorker(credentials, container, config);
|
|
||||||
if (e)
|
|
||||||
return e;
|
|
||||||
qDebug().noquote() << "ServerController::setupContainer runContainerWorker finished";
|
|
||||||
|
|
||||||
e = configureContainerWorker(credentials, container, config);
|
|
||||||
if (e)
|
|
||||||
return e;
|
|
||||||
qDebug().noquote() << "ServerController::setupContainer configureContainerWorker finished";
|
|
||||||
|
|
||||||
setupServerFirewall(credentials);
|
|
||||||
qDebug().noquote() << "ServerController::setupContainer setupServerFirewall finished";
|
|
||||||
|
|
||||||
return startupContainerWorker(credentials, container, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode ServerController::updateContainer(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &oldConfig,
|
|
||||||
QJsonObject &newConfig)
|
|
||||||
{
|
|
||||||
bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig);
|
|
||||||
qDebug() << "ServerController::updateContainer for container" << container << "reinstall required is" << reinstallRequired;
|
|
||||||
|
|
||||||
if (reinstallRequired) {
|
|
||||||
return setupContainer(credentials, container, newConfig, true);
|
|
||||||
} else {
|
|
||||||
ErrorCode e = configureContainerWorker(credentials, container, newConfig);
|
|
||||||
if (e)
|
|
||||||
return e;
|
|
||||||
|
|
||||||
return startupContainerWorker(credentials, container, newConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ServerController::isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig)
|
|
||||||
{
|
|
||||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
|
||||||
|
|
||||||
const QJsonObject &oldProtoConfig = oldConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
|
|
||||||
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::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 (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 (ContainerProps::isAwgContainer(container)) {
|
|
||||||
if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)
|
|
||||||
!= newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress))
|
|
||||||
|| (oldProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort)
|
|
||||||
!= newProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort))
|
|
||||||
|| (oldProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount)
|
|
||||||
!= newProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount))
|
|
||||||
|| (oldProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize)
|
|
||||||
!= newProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize))
|
|
||||||
|| (oldProtoConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize)
|
|
||||||
!= newProtoConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize))
|
|
||||||
|| (oldProtoConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize)
|
|
||||||
!= newProtoConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize))
|
|
||||||
|| (oldProtoConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize)
|
|
||||||
!= newProtoConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize))
|
|
||||||
|| (oldProtoConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader)
|
|
||||||
!= newProtoConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader))
|
|
||||||
|| (oldProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader)
|
|
||||||
!= newProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader))
|
|
||||||
|| (oldProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader)
|
|
||||||
!= newProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader))
|
|
||||||
|| (oldProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader))
|
|
||||||
!= newProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader)
|
|
||||||
|| (oldProtoConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize)
|
|
||||||
!= newProtoConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize))
|
|
||||||
|| (oldProtoConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize)
|
|
||||||
!= newProtoConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize)))
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (container == DockerContainer::WireGuard) {
|
|
||||||
if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)
|
|
||||||
!= newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress))
|
|
||||||
|| (oldProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)
|
|
||||||
!= newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (container == DockerContainer::Socks5Proxy) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (container == DockerContainer::Xray) {
|
|
||||||
if (oldProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort)
|
|
||||||
!= newProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode ServerController::installDockerWorker(const ServerCredentials &credentials, DockerContainer container)
|
|
||||||
{
|
|
||||||
QString stdOut;
|
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &client) {
|
|
||||||
stdOut += data + "\n";
|
|
||||||
|
|
||||||
if (data.contains("Automatically restart Docker daemon?")) {
|
|
||||||
return client.writeResponse("yes");
|
|
||||||
}
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data + "\n";
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
|
|
||||||
ErrorCode error =
|
|
||||||
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::install_docker), genVarsForScript(credentials)),
|
|
||||||
cbReadStdOut, cbReadStdErr);
|
|
||||||
|
|
||||||
qDebug().noquote() << "ServerController::installDockerWorker" << stdOut;
|
|
||||||
if (container == DockerContainer::Awg2) {
|
|
||||||
QRegularExpression regex(R"(Linux\s+(\d+)\.(\d+)[^\d]*)");
|
|
||||||
QRegularExpressionMatch match = regex.match(stdOut);
|
|
||||||
if (match.hasMatch()) {
|
|
||||||
int majorVersion = match.captured(1).toInt();
|
|
||||||
int minorVersion = match.captured(2).toInt();
|
|
||||||
|
|
||||||
if (majorVersion < 4 || (majorVersion == 4 && minorVersion < 14)) {
|
|
||||||
return ErrorCode::ServerLinuxKernelTooOld;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
// create folder on host
|
|
||||||
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::prepare_host), genVarsForScript(credentials, container)));
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode ServerController::buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
|
||||||
{
|
|
||||||
QString dockerFilePath = amnezia::server::getDockerfileFolder(container) + "/Dockerfile";
|
|
||||||
QString scriptString = QString("sudo rm %1").arg(dockerFilePath);
|
|
||||||
ErrorCode errorCode = runScript(credentials, replaceVars(scriptString, genVarsForScript(credentials, container)));
|
|
||||||
if (errorCode)
|
|
||||||
return errorCode;
|
|
||||||
|
|
||||||
errorCode = uploadFileToHost(credentials, amnezia::scriptData(ProtocolScriptType::dockerfile, container).toUtf8(), dockerFilePath);
|
|
||||||
|
|
||||||
if (errorCode)
|
|
||||||
return errorCode;
|
|
||||||
|
|
||||||
QString stdOut;
|
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data + "\n";
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data + "\n";
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
|
|
||||||
ErrorCode error =
|
|
||||||
runScript(credentials,
|
|
||||||
replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)),
|
|
||||||
cbReadStdOut, cbReadStdErr);
|
|
||||||
|
|
||||||
if (stdOut.contains("doesn't work on cgroups v2"))
|
|
||||||
return ErrorCode::ServerDockerOnCgroupsV2;
|
|
||||||
if (stdOut.contains("cgroup mountpoint does not exist"))
|
|
||||||
return ErrorCode::ServerCgroupMountpoint;
|
|
||||||
if (stdOut.contains("have reached") && stdOut.contains("pull rate limit"))
|
|
||||||
return ErrorCode::DockerPullRateLimit;
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
ErrorCode e = runScript(credentials,
|
|
||||||
replaceVars(amnezia::scriptData(ProtocolScriptType::run_container, container),
|
|
||||||
genVarsForScript(credentials, container, config)),
|
|
||||||
cbReadStdOut);
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
QString stdOut;
|
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data + "\n";
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data + "\n";
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
|
|
||||||
ErrorCode e = runContainerScript(credentials, container,
|
|
||||||
replaceVars(amnezia::scriptData(ProtocolScriptType::configure_container, container),
|
|
||||||
genVarsForScript(credentials, container, config)),
|
|
||||||
cbReadStdOut, cbReadStdErr);
|
|
||||||
|
|
||||||
VpnConfigurationsController::updateContainerConfigAfterInstallation(container, config, stdOut);
|
|
||||||
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode ServerController::startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
|
||||||
{
|
|
||||||
QString script = amnezia::scriptData(ProtocolScriptType::container_startup, container);
|
|
||||||
|
|
||||||
if (script.isEmpty()) {
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode e = uploadTextFileToContainer(container, credentials, 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)));
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
const QJsonObject &ssConfig = config.value(ProtocolProps::protoToString(Proto::ShadowSocks)).toObject();
|
|
||||||
const QJsonObject &wireguarConfig = config.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject();
|
|
||||||
const QJsonObject &amneziaWireguarConfig = config.value(ProtocolProps::protoToString(Proto::Awg)).toObject();
|
|
||||||
const QJsonObject &xrayConfig = config.value(ProtocolProps::protoToString(Proto::Xray)).toObject();
|
|
||||||
const QJsonObject &sftpConfig = config.value(ProtocolProps::protoToString(Proto::Sftp)).toObject();
|
|
||||||
const QJsonObject &socks5ProxyConfig = config.value(ProtocolProps::protoToString(Proto::Socks5Proxy)).toObject();
|
|
||||||
|
|
||||||
Vars vars;
|
|
||||||
|
|
||||||
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_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_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 : "" } });
|
|
||||||
if (!isTlsAuth) {
|
|
||||||
// erase $OPENVPN_TA_KEY, so it will not set in OpenVpnConfigurator::genOpenVpnConfig
|
|
||||||
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) } });
|
|
||||||
|
|
||||||
// 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({ { "$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) } });
|
|
||||||
|
|
||||||
// Xray vars
|
|
||||||
vars.append({ { "$XRAY_SITE_NAME", xrayConfig.value(config_key::site).toString(protocols::xray::defaultSite) } });
|
|
||||||
vars.append({ { "$XRAY_SERVER_PORT", xrayConfig.value(config_key::port).toString(protocols::xray::defaultPort) } });
|
|
||||||
|
|
||||||
// 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_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_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_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() } });
|
|
||||||
|
|
||||||
// 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() } });
|
|
||||||
|
|
||||||
// Amnezia wireguard vars
|
|
||||||
vars.append({ { "$AWG_SUBNET_IP",
|
|
||||||
amneziaWireguarConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) } });
|
|
||||||
vars.append({ { "$AWG_SERVER_PORT", amneziaWireguarConfig.value(config_key::port).toString(protocols::awg::defaultPort) } });
|
|
||||||
|
|
||||||
vars.append({ { "$JUNK_PACKET_COUNT", amneziaWireguarConfig.value(config_key::junkPacketCount).toString() } });
|
|
||||||
vars.append({ { "$JUNK_PACKET_MIN_SIZE", amneziaWireguarConfig.value(config_key::junkPacketMinSize).toString() } });
|
|
||||||
vars.append({ { "$JUNK_PACKET_MAX_SIZE", amneziaWireguarConfig.value(config_key::junkPacketMaxSize).toString() } });
|
|
||||||
vars.append({ { "$INIT_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::initPacketJunkSize).toString() } });
|
|
||||||
vars.append({ { "$RESPONSE_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::responsePacketJunkSize).toString() } });
|
|
||||||
vars.append({ { "$INIT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::initPacketMagicHeader).toString() } });
|
|
||||||
vars.append({ { "$RESPONSE_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::responsePacketMagicHeader).toString() } });
|
|
||||||
vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } });
|
|
||||||
vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } });
|
|
||||||
|
|
||||||
vars.append({ { "$COOKIE_REPLY_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::cookieReplyPacketJunkSize).toString() } });
|
|
||||||
vars.append({ { "$TRANSPORT_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::transportPacketJunkSize).toString() } });
|
|
||||||
vars.append({ { "$SPECIAL_JUNK_1", amneziaWireguarConfig.value(config_key::specialJunk1).toString() } });
|
|
||||||
vars.append({ { "$SPECIAL_JUNK_2", amneziaWireguarConfig.value(config_key::specialJunk2).toString() } });
|
|
||||||
vars.append({ { "$SPECIAL_JUNK_3", amneziaWireguarConfig.value(config_key::specialJunk3).toString() } });
|
|
||||||
vars.append({ { "$SPECIAL_JUNK_4", amneziaWireguarConfig.value(config_key::specialJunk4).toString() } });
|
|
||||||
vars.append({ { "$SPECIAL_JUNK_5", amneziaWireguarConfig.value(config_key::specialJunk5).toString() } });
|
|
||||||
|
|
||||||
// Socks5 proxy vars
|
|
||||||
vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } });
|
|
||||||
auto username = socks5ProxyConfig.value(config_key::userName).toString();
|
|
||||||
auto password = socks5ProxyConfig.value(config_key::password).toString();
|
|
||||||
QString socks5user = (!username.isEmpty() && !password.isEmpty()) ? QString("users %1:CL:%2").arg(username, password) : "";
|
|
||||||
vars.append({ { "$SOCKS5_USER", socks5user } });
|
|
||||||
vars.append({ { "$SOCKS5_AUTH_TYPE", socks5user.isEmpty() ? "none" : "strong" } });
|
|
||||||
|
|
||||||
QString serverIp = (!ContainerProps::isAwgContainer(container) &&
|
|
||||||
container != DockerContainer::WireGuard && container != DockerContainer::Xray)
|
|
||||||
? NetworkUtilities::getIPAddress(credentials.hostName)
|
|
||||||
: credentials.hostName;
|
|
||||||
if (!serverIp.isEmpty()) {
|
|
||||||
vars.append({ { "$SERVER_IP_ADDRESS", serverIp } });
|
|
||||||
} else {
|
|
||||||
qWarning() << "ServerController::genVarsForScript unable to resolve address for credentials.hostName";
|
|
||||||
}
|
|
||||||
|
|
||||||
return vars;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ServerController::checkSshConnection(const ServerCredentials &credentials, ErrorCode &errorCode)
|
|
||||||
{
|
|
||||||
QString stdOut;
|
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data + "\n";
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data + "\n";
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
|
|
||||||
errorCode = runScript(credentials, amnezia::scriptData(SharedScriptType::check_connection), cbReadStdOut, cbReadStdErr);
|
|
||||||
|
|
||||||
return stdOut;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServerController::cancelInstallation()
|
|
||||||
{
|
|
||||||
m_cancelInstallation = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials)
|
|
||||||
{
|
|
||||||
return runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::setup_host_firewall), genVarsForScript(credentials)));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ServerController::replaceVars(const QString &script, const Vars &vars)
|
|
||||||
{
|
|
||||||
QString s = script;
|
|
||||||
for (const QPair<QString, QString> &var : vars) {
|
|
||||||
s.replace(var.first, var.second);
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
|
||||||
{
|
|
||||||
if (container == DockerContainer::Dns) {
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString stdOut;
|
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data + "\n";
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data + "\n";
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Proto protocol = ContainerProps::defaultProtocol(container);
|
|
||||||
const QString containerString = ProtocolProps::protoToString(protocol);
|
|
||||||
const QJsonObject containerConfig = config.value(containerString).toObject();
|
|
||||||
|
|
||||||
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 transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto);
|
|
||||||
|
|
||||||
// TODO reimplement with netstat
|
|
||||||
QString script = QString("which lsof > /dev/null 2>&1 || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
|
|
||||||
for (auto &port : fixedPorts) {
|
|
||||||
script = script.append("|:%1").arg(port);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (transportProto == "tcpandudp") {
|
|
||||||
QString tcpProtoScript = script;
|
|
||||||
QString udpProtoScript = script;
|
|
||||||
tcpProtoScript.append("' | grep -i tcp");
|
|
||||||
udpProtoScript.append("' | grep -i udp");
|
|
||||||
tcpProtoScript.append(" | grep LISTEN");
|
|
||||||
|
|
||||||
ErrorCode errorCode =
|
|
||||||
runScript(credentials, replaceVars(tcpProtoScript, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
errorCode = runScript(credentials, replaceVars(udpProtoScript, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stdOut.isEmpty()) {
|
|
||||||
return ErrorCode::ServerPortAlreadyAllocatedError;
|
|
||||||
}
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
script = script.append("' | grep -i %1").arg(transportProto);
|
|
||||||
|
|
||||||
if (transportProto == "tcp") {
|
|
||||||
script = script.append(" | grep LISTEN");
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode errorCode = runScript(credentials, replaceVars(script, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stdOut.isEmpty()) {
|
|
||||||
return ErrorCode::ServerPortAlreadyAllocatedError;
|
|
||||||
}
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, DockerContainer container)
|
|
||||||
{
|
|
||||||
QString stdOut;
|
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data + "\n";
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data + "\n";
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
|
|
||||||
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo);
|
|
||||||
ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
|
||||||
|
|
||||||
if (credentials.userName != "root" && stdOut.contains("sudo:") && !stdOut.contains("uname:") && stdOut.contains("not found"))
|
|
||||||
return ErrorCode::ServerSudoPackageIsNotPreinstalled;
|
|
||||||
if (credentials.userName != "root" && !stdOut.contains("sudo") && !stdOut.contains("wheel"))
|
|
||||||
return ErrorCode::ServerUserNotInSudo;
|
|
||||||
if (stdOut.contains("can't cd to") || stdOut.contains("Permission denied") || stdOut.contains("No such file or directory"))
|
|
||||||
return ErrorCode::ServerUserDirectoryNotAccessible;
|
|
||||||
if (stdOut.contains("sudoers") || stdOut.contains("is not allowed to run sudo on"))
|
|
||||||
return ErrorCode::ServerUserNotAllowedInSudoers;
|
|
||||||
if (stdOut.contains("password is required"))
|
|
||||||
return ErrorCode::ServerUserPasswordRequired;
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container)
|
|
||||||
{
|
|
||||||
m_cancelInstallation = false;
|
|
||||||
QString stdOut;
|
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data + "\n";
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
|
||||||
stdOut += data + "\n";
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
};
|
|
||||||
|
|
||||||
QFutureWatcher<ErrorCode> watcher;
|
|
||||||
|
|
||||||
QFuture<ErrorCode> future = QtConcurrent::run([this, &stdOut, &cbReadStdOut, &cbReadStdErr, &credentials]() {
|
|
||||||
// max 100 attempts
|
|
||||||
for (int i = 0; i < 30; ++i) {
|
|
||||||
if (m_cancelInstallation) {
|
|
||||||
return ErrorCode::ServerCancelInstallation;
|
|
||||||
}
|
|
||||||
stdOut.clear();
|
|
||||||
runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::check_server_is_busy), genVarsForScript(credentials)),
|
|
||||||
cbReadStdOut, cbReadStdErr);
|
|
||||||
|
|
||||||
if (stdOut.contains("Packet manager not found"))
|
|
||||||
return ErrorCode::ServerPacketManagerError;
|
|
||||||
if (stdOut.contains("fuser not installed") || stdOut.contains("cat not installed"))
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
|
|
||||||
if (stdOut.isEmpty()) {
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
} else {
|
|
||||||
#ifdef MZ_DEBUG
|
|
||||||
qDebug().noquote() << stdOut;
|
|
||||||
#endif
|
|
||||||
emit serverIsBusy(true);
|
|
||||||
QThread::msleep(10000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ErrorCode::ServerPacketManagerError;
|
|
||||||
});
|
|
||||||
|
|
||||||
QEventLoop wait;
|
|
||||||
QObject::connect(&watcher, &QFutureWatcher<ErrorCode>::finished, &wait, &QEventLoop::quit);
|
|
||||||
watcher.setFuture(future);
|
|
||||||
wait.exec();
|
|
||||||
|
|
||||||
emit serverIsBusy(false);
|
|
||||||
|
|
||||||
return future.result();
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode ServerController::getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey,
|
|
||||||
const std::function<QString()> &callback)
|
|
||||||
{
|
|
||||||
auto error = m_sshClient.getDecryptedPrivateKey(credentials, decryptedPrivateKey, callback);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
#ifndef SERVERCONTROLLER_H
|
|
||||||
#define SERVERCONTROLLER_H
|
|
||||||
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
|
||||||
#include "core/defs.h"
|
|
||||||
#include "core/sshclient.h"
|
|
||||||
|
|
||||||
class Settings;
|
|
||||||
class VpnConfigurator;
|
|
||||||
|
|
||||||
using namespace amnezia;
|
|
||||||
|
|
||||||
class ServerController : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
ServerController(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
|
||||||
~ServerController();
|
|
||||||
|
|
||||||
typedef QList<QPair<QString, QString>> Vars;
|
|
||||||
|
|
||||||
ErrorCode rebootServer(const ServerCredentials &credentials);
|
|
||||||
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 updateContainer(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &oldConfig,
|
|
||||||
QJsonObject &newConfig);
|
|
||||||
|
|
||||||
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::ScpOverwriteMode overwriteMode = libssh::ScpOverwriteMode::ScpOverwriteExisting);
|
|
||||||
QByteArray getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials, const QString &path,
|
|
||||||
ErrorCode &errorCode);
|
|
||||||
|
|
||||||
QString replaceVars(const QString &script, const Vars &vars);
|
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
void cancelInstallation();
|
|
||||||
|
|
||||||
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 runContainerWorker(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 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::ScpOverwriteMode overwriteMode = libssh::ScpOverwriteMode::ScpOverwriteExisting);
|
|
||||||
|
|
||||||
ErrorCode setupServerFirewall(const ServerCredentials &credentials);
|
|
||||||
|
|
||||||
std::shared_ptr<Settings> m_settings;
|
|
||||||
std::shared_ptr<VpnConfigurator> m_configurator;
|
|
||||||
|
|
||||||
bool m_cancelInstallation = false;
|
|
||||||
libssh::Client m_sshClient;
|
|
||||||
signals:
|
|
||||||
void serverIsBusy(const bool isBusy);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SERVERCONTROLLER_H
|
|
||||||
205
client/core/controllers/serversController.cpp
Normal file
205
client/core/controllers/serversController.cpp
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
#include "serversController.h"
|
||||||
|
#include "core/utils/networkUtilities.h"
|
||||||
|
#include "core/utils/api/apiEnums.h"
|
||||||
|
#include "core/utils/constants/apiKeys.h"
|
||||||
|
#include "core/utils/constants/apiConstants.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
|
#include "core/models/containerConfig.h"
|
||||||
|
|
||||||
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
|
#include <AmneziaVPN-Swift.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
ServersController::ServersController(SecureServersRepository* serversRepository,
|
||||||
|
SecureAppSettingsRepository* appSettingsRepository,
|
||||||
|
QObject *parent)
|
||||||
|
: QObject(parent), m_serversRepository(serversRepository), m_appSettingsRepository(appSettingsRepository)
|
||||||
|
{
|
||||||
|
recomputeGatewayStacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServersController::addServer(const ServerConfig &server)
|
||||||
|
{
|
||||||
|
m_serversRepository->addServer(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServersController::editServer(int index, const ServerConfig &server)
|
||||||
|
{
|
||||||
|
m_serversRepository->editServer(index, server);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServersController::removeServer(int index)
|
||||||
|
{
|
||||||
|
m_serversRepository->removeServer(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServersController::setDefaultServerIndex(int index)
|
||||||
|
{
|
||||||
|
m_serversRepository->setDefaultServer(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServersController::setDefaultContainer(int serverIndex, DockerContainer container)
|
||||||
|
{
|
||||||
|
m_serversRepository->setDefaultContainer(serverIndex, container);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServersController::updateContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config)
|
||||||
|
{
|
||||||
|
m_serversRepository->setContainerConfig(serverIndex, container, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServersController::clearCachedProfile(int serverIndex, DockerContainer container)
|
||||||
|
{
|
||||||
|
m_serversRepository->clearLastConnectionConfig(serverIndex, container);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray ServersController::getServersArray() const
|
||||||
|
{
|
||||||
|
QJsonArray result;
|
||||||
|
QVector<ServerConfig> servers = m_serversRepository->servers();
|
||||||
|
for (const ServerConfig& server : servers) {
|
||||||
|
result.append(server.toJson());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<ServerConfig> ServersController::getServers() const
|
||||||
|
{
|
||||||
|
return m_serversRepository->servers();
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainerConfig ServersController::getContainerConfig(int serverIndex, DockerContainer container) const
|
||||||
|
{
|
||||||
|
return m_serversRepository->containerConfig(serverIndex, container);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ServersController::getDefaultServerIndex() const
|
||||||
|
{
|
||||||
|
return m_serversRepository->defaultServerIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ServersController::getServersCount() const
|
||||||
|
{
|
||||||
|
return m_serversRepository->serversCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerConfig ServersController::getServerConfig(int serverIndex) const
|
||||||
|
{
|
||||||
|
return m_serversRepository->server(serverIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerCredentials ServersController::getServerCredentials(int serverIndex) const
|
||||||
|
{
|
||||||
|
return m_serversRepository->serverCredentials(serverIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPair<QString, QString> ServersController::getDnsPair(int serverIndex, bool isAmneziaDnsEnabled) const
|
||||||
|
{
|
||||||
|
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
|
||||||
|
return serverConfig.getDnsPair(isAmneziaDnsEnabled,
|
||||||
|
m_appSettingsRepository->primaryDns(),
|
||||||
|
m_appSettingsRepository->secondaryDns());
|
||||||
|
}
|
||||||
|
|
||||||
|
ServersController::GatewayStacksData ServersController::gatewayStacks() const
|
||||||
|
{
|
||||||
|
return m_gatewayStacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServersController::recomputeGatewayStacks()
|
||||||
|
{
|
||||||
|
GatewayStacksData computed;
|
||||||
|
bool hasNewTags = false;
|
||||||
|
QVector<ServerConfig> servers = m_serversRepository->servers();
|
||||||
|
|
||||||
|
for (const ServerConfig& serverConfig : servers) {
|
||||||
|
if (serverConfig.isApiV2()) {
|
||||||
|
const ApiV2ServerConfig* apiV2 = serverConfig.as<ApiV2ServerConfig>();
|
||||||
|
if (!apiV2) continue;
|
||||||
|
const QString userCountryCode = apiV2->apiConfig.userCountryCode;
|
||||||
|
const QString serviceType = apiV2->serviceType();
|
||||||
|
|
||||||
|
if (!userCountryCode.isEmpty()) {
|
||||||
|
if (!m_gatewayStacks.userCountryCodes.contains(userCountryCode)) {
|
||||||
|
hasNewTags = true;
|
||||||
|
}
|
||||||
|
computed.userCountryCodes.insert(userCountryCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!serviceType.isEmpty()) {
|
||||||
|
if (!m_gatewayStacks.serviceTypes.contains(serviceType)) {
|
||||||
|
hasNewTags = true;
|
||||||
|
}
|
||||||
|
computed.serviceTypes.insert(serviceType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_gatewayStacks = std::move(computed);
|
||||||
|
if (hasNewTags) {
|
||||||
|
emit gatewayStacksExpanded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ServersController::GatewayStacksData::operator==(const GatewayStacksData &other) const
|
||||||
|
{
|
||||||
|
return userCountryCodes == other.userCountryCodes && serviceTypes == other.serviceTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ServersController::GatewayStacksData::toJson() const
|
||||||
|
{
|
||||||
|
QJsonObject json;
|
||||||
|
|
||||||
|
QJsonArray userCountryCodesArray;
|
||||||
|
for (const QString &code : userCountryCodes) {
|
||||||
|
userCountryCodesArray.append(code);
|
||||||
|
}
|
||||||
|
json[apiDefs::key::userCountryCode] = userCountryCodesArray;
|
||||||
|
|
||||||
|
QJsonArray serviceTypesArray;
|
||||||
|
for (const QString &type : serviceTypes) {
|
||||||
|
serviceTypesArray.append(type);
|
||||||
|
}
|
||||||
|
json[apiDefs::key::serviceType] = serviceTypesArray;
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ServersController::isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol) const
|
||||||
|
{
|
||||||
|
QVector<ServerConfig> servers = m_serversRepository->servers();
|
||||||
|
for (const ServerConfig& serverConfig : servers) {
|
||||||
|
if (serverConfig.isApiV2()) {
|
||||||
|
const ApiV2ServerConfig* apiV2 = serverConfig.as<ApiV2ServerConfig>();
|
||||||
|
if (!apiV2) return false;
|
||||||
|
if (apiV2->apiConfig.userCountryCode == userCountryCode
|
||||||
|
&& apiV2->serviceType() == serviceType
|
||||||
|
&& apiV2->serviceProtocol() == serviceProtocol) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ServersController::hasInstalledContainers(int serverIndex) const
|
||||||
|
{
|
||||||
|
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
|
||||||
|
QMap<DockerContainer, ContainerConfig> containers = serverConfig.containers();
|
||||||
|
for (auto it = containers.begin(); it != containers.end(); ++it) {
|
||||||
|
DockerContainer container = it.key();
|
||||||
|
if (ContainerUtils::containerService(container) == ServiceType::Vpn) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (container == DockerContainer::SSXray) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
96
client/core/controllers/serversController.h
Normal file
96
client/core/controllers/serversController.h
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#ifndef SERVERSCONTROLLER_H
|
||||||
|
#define SERVERSCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
#include <QPair>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
#include "core/repositories/secureServersRepository.h"
|
||||||
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
|
#include "core/models/serverConfig.h"
|
||||||
|
#include "core/models/containerConfig.h"
|
||||||
|
|
||||||
|
class SshSession;
|
||||||
|
class InstallController;
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Core business logic controller for server operations
|
||||||
|
*
|
||||||
|
* This controller contains pure business logic for managing servers.
|
||||||
|
*/
|
||||||
|
class ServersController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct GatewayStacksData
|
||||||
|
{
|
||||||
|
QSet<QString> userCountryCodes;
|
||||||
|
QSet<QString> serviceTypes;
|
||||||
|
|
||||||
|
bool isEmpty() const { return userCountryCodes.isEmpty() && serviceTypes.isEmpty(); }
|
||||||
|
bool operator==(const GatewayStacksData &other) const;
|
||||||
|
QJsonObject toJson() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ServersController(SecureServersRepository* serversRepository,
|
||||||
|
SecureAppSettingsRepository* appSettingsRepository = nullptr,
|
||||||
|
QObject *parent = nullptr);
|
||||||
|
~ServersController() = default;
|
||||||
|
|
||||||
|
// Server management
|
||||||
|
void addServer(const ServerConfig &server);
|
||||||
|
void editServer(int index, const ServerConfig &server);
|
||||||
|
void removeServer(int index);
|
||||||
|
void setDefaultServerIndex(int index);
|
||||||
|
|
||||||
|
// Container management
|
||||||
|
void setDefaultContainer(int serverIndex, DockerContainer container);
|
||||||
|
void updateContainerConfig(int serverIndex, DockerContainer container, const ContainerConfig &config);
|
||||||
|
|
||||||
|
// Cache management
|
||||||
|
void clearCachedProfile(int serverIndex, DockerContainer container);
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
QJsonArray getServersArray() const;
|
||||||
|
QVector<ServerConfig> getServers() const;
|
||||||
|
int getDefaultServerIndex() const;
|
||||||
|
int getServersCount() const;
|
||||||
|
ServerConfig getServerConfig(int serverIndex) const;
|
||||||
|
ServerCredentials getServerCredentials(int serverIndex) const;
|
||||||
|
ContainerConfig getContainerConfig(int serverIndex, DockerContainer container) const;
|
||||||
|
QPair<QString, QString> getDnsPair(int serverIndex, bool isAmneziaDnsEnabled) const;
|
||||||
|
|
||||||
|
GatewayStacksData gatewayStacks() const;
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
bool isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol) const;
|
||||||
|
bool hasInstalledContainers(int serverIndex) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void gatewayStacksExpanded();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void recomputeGatewayStacks();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SecureServersRepository* m_serversRepository;
|
||||||
|
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||||
|
GatewayStacksData m_gatewayStacks;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SERVERSCONTROLLER_H
|
||||||
|
|
||||||
366
client/core/controllers/settingsController.cpp
Normal file
366
client/core/controllers/settingsController.cpp
Normal file
@@ -0,0 +1,366 @@
|
|||||||
|
#include "settingsController.h"
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QOperatingSystemVersion>
|
||||||
|
|
||||||
|
#include "version.h"
|
||||||
|
#include "ui/utils/qAutoStart.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
#include "platforms/android/android_controller.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QString getPlatformName()
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_WINDOWS)
|
||||||
|
return "Windows";
|
||||||
|
#elif defined(Q_OS_ANDROID)
|
||||||
|
return "Android";
|
||||||
|
#elif defined(Q_OS_LINUX)
|
||||||
|
return "Linux";
|
||||||
|
#elif defined(Q_OS_MACX)
|
||||||
|
return "MacOS";
|
||||||
|
#elif defined(Q_OS_IOS)
|
||||||
|
return "iOS";
|
||||||
|
#else
|
||||||
|
return "Unknown";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsController::SettingsController(SecureServersRepository* serversRepository,
|
||||||
|
SecureAppSettingsRepository* appSettingsRepository,
|
||||||
|
QObject* parent)
|
||||||
|
: QObject(parent),
|
||||||
|
m_serversRepository(serversRepository),
|
||||||
|
m_appSettingsRepository(appSettingsRepository)
|
||||||
|
{
|
||||||
|
m_appVersion = QString("%1 (%2, %3)").arg(QString(APP_VERSION), __DATE__, GIT_COMMIT_HASH);
|
||||||
|
m_isDevModeEnabled = m_appSettingsRepository->isDevGatewayEnv();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::toggleAmneziaDns(bool enable)
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->setUseAmneziaDns(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsController::isAmneziaDnsEnabled() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->useAmneziaDns();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SettingsController::getPrimaryDns() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->primaryDns();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::setPrimaryDns(const QString &dns)
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->setPrimaryDns(dns);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SettingsController::getSecondaryDns() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->secondaryDns();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::setSecondaryDns(const QString &dns)
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->setSecondaryDns(dns);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsController::isLoggingEnabled() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->isSaveLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::toggleLogging(bool enable)
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->setSaveLogs(enable);
|
||||||
|
#ifndef Q_OS_ANDROID
|
||||||
|
if (!enable) {
|
||||||
|
Logger::deInit();
|
||||||
|
} else {
|
||||||
|
if (!Logger::init(false)) {
|
||||||
|
qWarning() << "Initialization of debug subsystem failed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
Logger::setServiceLogsEnabled(enable);
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
m_appSettingsRepository->setLogEnableDate(QDateTime::currentDateTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::clearLogs()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
AndroidController::instance()->clearLogs();
|
||||||
|
#else
|
||||||
|
Logger::clearLogs(false);
|
||||||
|
Logger::clearServiceLogs();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SettingsController::backupAppConfig() const
|
||||||
|
{
|
||||||
|
QByteArray data = m_appSettingsRepository->backupAppConfig();
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||||
|
QJsonObject config = doc.object();
|
||||||
|
|
||||||
|
config["AppPlatform"] = getPlatform();
|
||||||
|
config["Conf/autoStart"] = isAutoStartEnabled();
|
||||||
|
config["Conf/killSwitchEnabled"] = isKillSwitchEnabled();
|
||||||
|
config["Conf/strictKillSwitchEnabled"] = isStrictKillSwitchEnabled();
|
||||||
|
config["Conf/useAmneziaDns"] = isAmneziaDnsEnabled();
|
||||||
|
|
||||||
|
return QJsonDocument(config).toJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode SettingsController::restoreAppConfigFromData(const QByteArray &data)
|
||||||
|
{
|
||||||
|
if (!m_appSettingsRepository->restoreAppConfig(data)) {
|
||||||
|
return ErrorCode::RestoreBackupInvalidError;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_serversRepository->invalidateCache();
|
||||||
|
|
||||||
|
QJsonObject newConfigData = QJsonDocument::fromJson(data).object();
|
||||||
|
|
||||||
|
#if defined(Q_OS_WINDOWS) || defined(Q_OS_LINUX) || defined(Q_OS_MACX)
|
||||||
|
bool autoStart = false;
|
||||||
|
if (newConfigData.contains("Conf/autoStart")) {
|
||||||
|
autoStart = newConfigData["Conf/autoStart"].toBool();
|
||||||
|
}
|
||||||
|
toggleAutoStart(autoStart);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
|
||||||
|
int appSplitTunnelingRouteMode = newConfigData.value("Conf/appsRouteMode").toInt();
|
||||||
|
bool appSplittunnelingEnabled =
|
||||||
|
newConfigData.value("Conf/appsSplitTunnelingEnabled").toVariant().toString().toLower() == "true";
|
||||||
|
emit appSplitTunnelingRouteModeChanged(static_cast<AppsRouteMode>(appSplitTunnelingRouteMode));
|
||||||
|
|
||||||
|
#if defined(Q_OS_WINDOWS)
|
||||||
|
emit appSplitTunnelingRouteModeChanged(AppsRouteMode::VpnAllExceptApps);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (newConfigData.contains("AppPlatform")) {
|
||||||
|
if (newConfigData.value("AppPlatform").toString() != getPlatform()) {
|
||||||
|
emit appSplitTunnelingClearAppsList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit appSplitTunnelingToggled(appSplittunnelingEnabled);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt();
|
||||||
|
bool siteSplittunnelingEnabled =
|
||||||
|
newConfigData.value("Conf/sitesSplitTunnelingEnabled").toVariant().toString().toLower() == "true";
|
||||||
|
emit siteSplitTunnelingRouteModeChanged(static_cast<RouteMode>(siteSplitTunnelingRouteMode));
|
||||||
|
emit siteSplitTunnelingToggled(siteSplittunnelingEnabled);
|
||||||
|
|
||||||
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||||
|
m_appSettingsRepository->setAutoConnect(false);
|
||||||
|
m_appSettingsRepository->setStartMinimized(false);
|
||||||
|
m_appSettingsRepository->setKillSwitchEnabled(false);
|
||||||
|
m_appSettingsRepository->setStrictKillSwitchEnabled(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SettingsController::getAppVersion() const
|
||||||
|
{
|
||||||
|
return m_appVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::clearSettings()
|
||||||
|
{
|
||||||
|
int serverCount = m_serversRepository->serversCount();
|
||||||
|
|
||||||
|
m_appSettingsRepository->clearSettings();
|
||||||
|
|
||||||
|
m_serversRepository->setServersArray(QJsonArray());
|
||||||
|
m_serversRepository->setDefaultServer(0);
|
||||||
|
|
||||||
|
emit siteSplitTunnelingRouteModeChanged(RouteMode::VpnOnlyForwardSites);
|
||||||
|
emit siteSplitTunnelingToggled(false);
|
||||||
|
|
||||||
|
emit appSplitTunnelingRouteModeChanged(AppsRouteMode::VpnAllExceptApps);
|
||||||
|
emit appSplitTunnelingToggled(false);
|
||||||
|
|
||||||
|
toggleAutoStart(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsController::isAutoConnectEnabled() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->isAutoConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::toggleAutoConnect(bool enable)
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->setAutoConnect(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsController::isAutoStartEnabled() const
|
||||||
|
{
|
||||||
|
return Autostart::isAutostart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::toggleAutoStart(bool enable)
|
||||||
|
{
|
||||||
|
Autostart::setAutostart(enable);
|
||||||
|
if (!enable) {
|
||||||
|
toggleStartMinimized(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsController::isStartMinimizedEnabled() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->isStartMinimized();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::toggleStartMinimized(bool enable)
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->setStartMinimized(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsController::isScreenshotsEnabled() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->isScreenshotsEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::toggleScreenshotsEnabled(bool enable)
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->setScreenshotsEnabled(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsController::isNewsNotificationsEnabled() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->isNewsNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::toggleNewsNotificationsEnabled(bool enable)
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->setNewsNotifications(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsController::isKillSwitchEnabled() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->isKillSwitchEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::toggleKillSwitch(bool enable)
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->setKillSwitchEnabled(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsController::isStrictKillSwitchEnabled() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->isStrictKillSwitchEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::toggleStrictKillSwitch(bool enable)
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->setStrictKillSwitchEnabled(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SettingsController::getInstallationUuid(bool createIfNotExists) const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->getInstallationUuid(createIfNotExists);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::enableDevMode()
|
||||||
|
{
|
||||||
|
m_isDevModeEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsController::isDevModeEnabled() const
|
||||||
|
{
|
||||||
|
return m_isDevModeEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::resetGatewayEndpoint()
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->resetGatewayEndpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::setGatewayEndpoint(const QString &endpoint)
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->setGatewayEndpoint(endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SettingsController::getGatewayEndpoint() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->isDevGatewayEnv() ? "Dev endpoint" : m_appSettingsRepository->getGatewayEndpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsController::isDevGatewayEnv() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->isDevGatewayEnv();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::toggleDevGatewayEnv(bool enabled)
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->toggleDevGatewayEnv(enabled);
|
||||||
|
if (enabled) {
|
||||||
|
m_appSettingsRepository->setDevGatewayEndpoint();
|
||||||
|
} else {
|
||||||
|
m_appSettingsRepository->resetGatewayEndpoint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsController::isHomeAdLabelVisible() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->isHomeAdLabelVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::disableHomeAdLabel()
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->disableHomeAdLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::checkIfNeedDisableLogs()
|
||||||
|
{
|
||||||
|
if (m_appSettingsRepository->isSaveLogs()) {
|
||||||
|
m_loggingDisableDate = m_appSettingsRepository->getLogEnableDate().addDays(14);
|
||||||
|
if (m_loggingDisableDate <= QDateTime::currentDateTime()) {
|
||||||
|
toggleLogging(false);
|
||||||
|
clearLogs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SettingsController::getPlatform() const
|
||||||
|
{
|
||||||
|
return getPlatformName();
|
||||||
|
}
|
||||||
|
|
||||||
|
QLocale SettingsController::getAppLanguage() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->getAppLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::setAppLanguage(const QLocale &locale)
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->setAppLanguage(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsController::isPremV1MigrationReminderActive() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->isPremV1MigrationReminderActive();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::disablePremV1MigrationReminder()
|
||||||
|
{
|
||||||
|
m_appSettingsRepository->disablePremV1MigrationReminder();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SettingsController::nextAvailableServerName() const
|
||||||
|
{
|
||||||
|
return m_appSettingsRepository->nextAvailableServerName();
|
||||||
|
}
|
||||||
|
|
||||||
112
client/core/controllers/settingsController.h
Normal file
112
client/core/controllers/settingsController.h
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
#ifndef SETTINGSCONTROLLER_H
|
||||||
|
#define SETTINGSCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
#include "core/repositories/secureServersRepository.h"
|
||||||
|
#include "core/repositories/secureAppSettingsRepository.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
|
||||||
|
class SettingsController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit SettingsController(SecureServersRepository* serversRepository,
|
||||||
|
SecureAppSettingsRepository* appSettingsRepository,
|
||||||
|
QObject* parent = nullptr);
|
||||||
|
~SettingsController() = default;
|
||||||
|
|
||||||
|
void toggleAmneziaDns(bool enable);
|
||||||
|
bool isAmneziaDnsEnabled() const;
|
||||||
|
|
||||||
|
QString getPrimaryDns() const;
|
||||||
|
void setPrimaryDns(const QString &dns);
|
||||||
|
|
||||||
|
QString getSecondaryDns() const;
|
||||||
|
void setSecondaryDns(const QString &dns);
|
||||||
|
|
||||||
|
bool isLoggingEnabled() const;
|
||||||
|
void toggleLogging(bool enable);
|
||||||
|
|
||||||
|
void clearLogs();
|
||||||
|
|
||||||
|
QByteArray backupAppConfig() const;
|
||||||
|
ErrorCode restoreAppConfigFromData(const QByteArray &data);
|
||||||
|
|
||||||
|
QString getAppVersion() const;
|
||||||
|
|
||||||
|
void clearSettings();
|
||||||
|
|
||||||
|
bool isAutoConnectEnabled() const;
|
||||||
|
void toggleAutoConnect(bool enable);
|
||||||
|
|
||||||
|
bool isAutoStartEnabled() const;
|
||||||
|
void toggleAutoStart(bool enable);
|
||||||
|
|
||||||
|
bool isStartMinimizedEnabled() const;
|
||||||
|
void toggleStartMinimized(bool enable);
|
||||||
|
|
||||||
|
bool isScreenshotsEnabled() const;
|
||||||
|
void toggleScreenshotsEnabled(bool enable);
|
||||||
|
|
||||||
|
bool isNewsNotificationsEnabled() const;
|
||||||
|
void toggleNewsNotificationsEnabled(bool enable);
|
||||||
|
|
||||||
|
bool isKillSwitchEnabled() const;
|
||||||
|
void toggleKillSwitch(bool enable);
|
||||||
|
|
||||||
|
bool isStrictKillSwitchEnabled() const;
|
||||||
|
void toggleStrictKillSwitch(bool enable);
|
||||||
|
|
||||||
|
QString getInstallationUuid(bool createIfNotExists = true) const;
|
||||||
|
|
||||||
|
void enableDevMode();
|
||||||
|
|
||||||
|
bool isPremV1MigrationReminderActive() const;
|
||||||
|
void disablePremV1MigrationReminder();
|
||||||
|
|
||||||
|
QString nextAvailableServerName() const;
|
||||||
|
bool isDevModeEnabled() const;
|
||||||
|
|
||||||
|
void resetGatewayEndpoint();
|
||||||
|
void setGatewayEndpoint(const QString &endpoint);
|
||||||
|
QString getGatewayEndpoint() const;
|
||||||
|
bool isDevGatewayEnv() const;
|
||||||
|
void toggleDevGatewayEnv(bool enabled);
|
||||||
|
|
||||||
|
bool isHomeAdLabelVisible() const;
|
||||||
|
void disableHomeAdLabel();
|
||||||
|
|
||||||
|
void checkIfNeedDisableLogs();
|
||||||
|
|
||||||
|
QLocale getAppLanguage() const;
|
||||||
|
void setAppLanguage(const QLocale &locale);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void siteSplitTunnelingRouteModeChanged(RouteMode mode);
|
||||||
|
void siteSplitTunnelingToggled(bool enabled);
|
||||||
|
void appSplitTunnelingRouteModeChanged(AppsRouteMode mode);
|
||||||
|
void appSplitTunnelingToggled(bool enabled);
|
||||||
|
void appSplitTunnelingClearAppsList();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString getPlatform() const;
|
||||||
|
|
||||||
|
SecureServersRepository* m_serversRepository;
|
||||||
|
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||||
|
|
||||||
|
QString m_appVersion;
|
||||||
|
QDateTime m_loggingDisableDate;
|
||||||
|
bool m_isDevModeEnabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
#include "vpnConfigurationController.h"
|
|
||||||
|
|
||||||
#include "configurators/awg_configurator.h"
|
|
||||||
#include "configurators/cloak_configurator.h"
|
|
||||||
#include "configurators/ikev2_configurator.h"
|
|
||||||
#include "configurators/openvpn_configurator.h"
|
|
||||||
#include "configurators/shadowsocks_configurator.h"
|
|
||||||
#include "configurators/wireguard_configurator.h"
|
|
||||||
#include "configurators/xray_configurator.h"
|
|
||||||
|
|
||||||
VpnConfigurationsController::VpnConfigurationsController(const std::shared_ptr<Settings> &settings,
|
|
||||||
QSharedPointer<ServerController> serverController, QObject *parent)
|
|
||||||
: QObject { parent }, m_settings(settings), m_serverController(serverController)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QScopedPointer<ConfiguratorBase> VpnConfigurationsController::createConfigurator(const Proto protocol)
|
|
||||||
{
|
|
||||||
switch (protocol) {
|
|
||||||
case Proto::OpenVpn: return QScopedPointer<ConfiguratorBase>(new OpenVpnConfigurator(m_settings, m_serverController));
|
|
||||||
case Proto::ShadowSocks: return QScopedPointer<ConfiguratorBase>(new ShadowSocksConfigurator(m_settings, m_serverController));
|
|
||||||
case Proto::Cloak: return QScopedPointer<ConfiguratorBase>(new CloakConfigurator(m_settings, m_serverController));
|
|
||||||
case Proto::WireGuard: return QScopedPointer<ConfiguratorBase>(new WireguardConfigurator(m_settings, m_serverController, false));
|
|
||||||
case Proto::Awg: return QScopedPointer<ConfiguratorBase>(new AwgConfigurator(m_settings, m_serverController));
|
|
||||||
case Proto::Ikev2: return QScopedPointer<ConfiguratorBase>(new Ikev2Configurator(m_settings, m_serverController));
|
|
||||||
case Proto::Xray: return QScopedPointer<ConfiguratorBase>(new XrayConfigurator(m_settings, m_serverController));
|
|
||||||
case Proto::SSXray: return QScopedPointer<ConfiguratorBase>(new XrayConfigurator(m_settings, m_serverController));
|
|
||||||
default: return QScopedPointer<ConfiguratorBase>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode VpnConfigurationsController::createProtocolConfigForContainer(const ServerCredentials &credentials,
|
|
||||||
const DockerContainer container, QJsonObject &containerConfig)
|
|
||||||
{
|
|
||||||
ErrorCode errorCode = ErrorCode::NoError;
|
|
||||||
|
|
||||||
if (ContainerProps::containerService(container) == ServiceType::Other) {
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
|
|
||||||
QJsonObject protocolConfig = containerConfig.value(ProtocolProps::protoToString(protocol)).toObject();
|
|
||||||
|
|
||||||
auto configurator = createConfigurator(protocol);
|
|
||||||
QString protocolConfigString = configurator->createConfig(credentials, container, containerConfig, errorCode);
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
protocolConfig.insert(config_key::last_config, protocolConfigString);
|
|
||||||
containerConfig.insert(ProtocolProps::protoToString(protocol), protocolConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode VpnConfigurationsController::createProtocolConfigString(const bool isApiConfig, const QPair<QString, QString> &dns,
|
|
||||||
const ServerCredentials &credentials, const DockerContainer container,
|
|
||||||
const QJsonObject &containerConfig, const Proto protocol,
|
|
||||||
QString &protocolConfigString)
|
|
||||||
{
|
|
||||||
ErrorCode errorCode = ErrorCode::NoError;
|
|
||||||
|
|
||||||
if (ContainerProps::containerService(container) == ServiceType::Other) {
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto configurator = createConfigurator(protocol);
|
|
||||||
|
|
||||||
protocolConfigString = configurator->createConfig(credentials, container, containerConfig, errorCode);
|
|
||||||
if (errorCode != ErrorCode::NoError) {
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
protocolConfigString = configurator->processConfigWithExportSettings(dns, isApiConfig, protocolConfigString);
|
|
||||||
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject VpnConfigurationsController::createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig,
|
|
||||||
const QJsonObject &containerConfig, const DockerContainer container)
|
|
||||||
{
|
|
||||||
QJsonObject vpnConfiguration {};
|
|
||||||
|
|
||||||
if (ContainerProps::containerService(container) == ServiceType::Other) {
|
|
||||||
return vpnConfiguration;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isApiConfig = serverConfig.value(config_key::configVersion).toInt();
|
|
||||||
|
|
||||||
for (ProtocolEnumNS::Proto proto : ContainerProps::protocolsForContainer(container)) {
|
|
||||||
if (isApiConfig && container == DockerContainer::Cloak && proto == ProtocolEnumNS::Proto::ShadowSocks) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString protocolConfigString =
|
|
||||||
containerConfig.value(ProtocolProps::protoToString(proto)).toObject().value(config_key::last_config).toString();
|
|
||||||
|
|
||||||
auto configurator = createConfigurator(proto);
|
|
||||||
protocolConfigString = configurator->processConfigWithLocalSettings(dns, isApiConfig, protocolConfigString);
|
|
||||||
|
|
||||||
QJsonObject vpnConfigData = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
|
||||||
if (ContainerProps::isAwgContainer(container) || container == DockerContainer::WireGuard) {
|
|
||||||
// add mtu for old configs
|
|
||||||
if (vpnConfigData[config_key::mtu].toString().isEmpty()) {
|
|
||||||
vpnConfigData[config_key::mtu] =
|
|
||||||
ContainerProps::isAwgContainer(container) ? protocols::awg::defaultMtu :
|
|
||||||
protocols::wireguard::defaultMtu;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vpnConfiguration.insert(ProtocolProps::key_proto_config_data(proto), vpnConfigData);
|
|
||||||
}
|
|
||||||
|
|
||||||
Proto proto = ContainerProps::defaultProtocol(container);
|
|
||||||
vpnConfiguration[config_key::vpnproto] = ProtocolProps::protoToString(proto);
|
|
||||||
|
|
||||||
vpnConfiguration[config_key::dns1] = dns.first;
|
|
||||||
vpnConfiguration[config_key::dns2] = dns.second;
|
|
||||||
|
|
||||||
vpnConfiguration[config_key::hostName] = serverConfig.value(config_key::hostName).toString();
|
|
||||||
vpnConfiguration[config_key::description] = serverConfig.value(config_key::description).toString();
|
|
||||||
|
|
||||||
vpnConfiguration[config_key::configVersion] = serverConfig.value(config_key::configVersion).toInt();
|
|
||||||
// TODO: try to get hostName, port, description for 3rd party configs
|
|
||||||
// vpnConfiguration[config_key::port] = ...;
|
|
||||||
|
|
||||||
return vpnConfiguration;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VpnConfigurationsController::updateContainerConfigAfterInstallation(const DockerContainer container, QJsonObject &containerConfig,
|
|
||||||
const QString &stdOut)
|
|
||||||
{
|
|
||||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
|
||||||
|
|
||||||
if (container == DockerContainer::TorWebSite) {
|
|
||||||
QJsonObject protocol = containerConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
|
|
||||||
|
|
||||||
qDebug() << "amnezia-tor onions" << stdOut;
|
|
||||||
|
|
||||||
QString onion = stdOut;
|
|
||||||
onion.replace("\n", "");
|
|
||||||
protocol.insert(config_key::site, onion);
|
|
||||||
|
|
||||||
containerConfig.insert(ProtocolProps::protoToString(mainProto), protocol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#ifndef VPNCONFIGIRATIONSCONTROLLER_H
|
|
||||||
#define VPNCONFIGIRATIONSCONTROLLER_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include "configurators/configurator_base.h"
|
|
||||||
#include "containers/containers_defs.h"
|
|
||||||
#include "core/defs.h"
|
|
||||||
#include "settings.h"
|
|
||||||
|
|
||||||
class VpnConfigurationsController : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit VpnConfigurationsController(const std::shared_ptr<Settings> &settings, QSharedPointer<ServerController> serverController,
|
|
||||||
QObject *parent = nullptr);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
ErrorCode createProtocolConfigForContainer(const ServerCredentials &credentials, const DockerContainer container,
|
|
||||||
QJsonObject &containerConfig);
|
|
||||||
ErrorCode createProtocolConfigString(const bool isApiConfig, const QPair<QString, QString> &dns, const ServerCredentials &credentials,
|
|
||||||
const DockerContainer container, const QJsonObject &containerConfig, const Proto protocol,
|
|
||||||
QString &protocolConfigString);
|
|
||||||
QJsonObject createVpnConfiguration(const QPair<QString, QString> &dns, const QJsonObject &serverConfig,
|
|
||||||
const QJsonObject &containerConfig, const DockerContainer container);
|
|
||||||
|
|
||||||
static void updateContainerConfigAfterInstallation(const DockerContainer container, QJsonObject &containerConfig, const QString &stdOut);
|
|
||||||
signals:
|
|
||||||
|
|
||||||
private:
|
|
||||||
QScopedPointer<ConfiguratorBase> createConfigurator(const Proto protocol);
|
|
||||||
|
|
||||||
std::shared_ptr<Settings> m_settings;
|
|
||||||
QSharedPointer<ServerController> m_serverController;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // VPNCONFIGIRATIONSCONTROLLER_H
|
|
||||||
200
client/core/installers/awgInstaller.cpp
Normal file
200
client/core/installers/awgInstaller.cpp
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
#include "awgInstaller.h"
|
||||||
|
|
||||||
|
#include <QPair>
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
|
#include "core/utils/utilities.h"
|
||||||
|
#include "core/models/protocols/awgProtocolConfig.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
using namespace ProtocolUtils;
|
||||||
|
|
||||||
|
AwgInstaller::AwgInstaller(QObject *parent)
|
||||||
|
: InstallerBase(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainerConfig AwgInstaller::generateConfig(DockerContainer container, int port, TransportProto transportProto)
|
||||||
|
{
|
||||||
|
ContainerConfig config = createBaseConfig(container, port, transportProto);
|
||||||
|
|
||||||
|
bool isAwg2 = (container == DockerContainer::Awg2);
|
||||||
|
|
||||||
|
if (auto* awgConfig = config.getAwgProtocolConfig()) {
|
||||||
|
generateAwgParameters(awgConfig->serverConfig, isAwg2);
|
||||||
|
|
||||||
|
if (isAwg2) {
|
||||||
|
awgConfig->serverConfig.protocolVersion = "2";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AwgInstaller::generateAwgParameters(AwgServerConfig &serverConfig, bool isAwg2)
|
||||||
|
{
|
||||||
|
QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(4, 7));
|
||||||
|
QString junkPacketMinSize = QString::number(10);
|
||||||
|
QString junkPacketMaxSize = QString::number(50);
|
||||||
|
|
||||||
|
int s1 = QRandomGenerator::global()->bounded(15, 150);
|
||||||
|
int s2 = QRandomGenerator::global()->bounded(15, 150);
|
||||||
|
int s3 = QRandomGenerator::global()->bounded(0, 64);
|
||||||
|
int s4 = QRandomGenerator::global()->bounded(0, 20);
|
||||||
|
|
||||||
|
// Ensure all values are unique and don't create equal packet sizes
|
||||||
|
QSet<int> usedValues;
|
||||||
|
usedValues.insert(s1);
|
||||||
|
|
||||||
|
while (usedValues.contains(s2) || s1 + amnezia::AwgConstant::messageInitiationSize == s2 + amnezia::AwgConstant::messageResponseSize) {
|
||||||
|
s2 = QRandomGenerator::global()->bounded(15, 150);
|
||||||
|
}
|
||||||
|
usedValues.insert(s2);
|
||||||
|
|
||||||
|
while (usedValues.contains(s3) || s1 + amnezia::AwgConstant::messageInitiationSize == s3 + amnezia::AwgConstant::messageCookieReplySize
|
||||||
|
|| s2 + amnezia::AwgConstant::messageResponseSize == s3 + amnezia::AwgConstant::messageCookieReplySize) {
|
||||||
|
s3 = QRandomGenerator::global()->bounded(0, 64);
|
||||||
|
}
|
||||||
|
usedValues.insert(s3);
|
||||||
|
|
||||||
|
while (usedValues.contains(s4)) {
|
||||||
|
s4 = QRandomGenerator::global()->bounded(0, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString initPacketJunkSize = QString::number(s1);
|
||||||
|
QString responsePacketJunkSize = QString::number(s2);
|
||||||
|
QString cookieReplyPacketJunkSize = QString::number(s3);
|
||||||
|
QString transportPacketJunkSize = QString::number(s4);
|
||||||
|
|
||||||
|
QString initPacketMagicHeader;
|
||||||
|
QString responsePacketMagicHeader;
|
||||||
|
QString underloadPacketMagicHeader;
|
||||||
|
QString transportPacketMagicHeader;
|
||||||
|
|
||||||
|
if (isAwg2) {
|
||||||
|
// AWG 2.0: use range format for magic headers
|
||||||
|
QVector<QPair<QString, QString>> headersValue;
|
||||||
|
int min = 5;
|
||||||
|
auto max = (std::numeric_limits<qint32>::max)();
|
||||||
|
while (headersValue.size() != 4) {
|
||||||
|
auto first = QRandomGenerator::global()->bounded(min, max);
|
||||||
|
auto second = QRandomGenerator::global()->bounded(first, max);
|
||||||
|
min = second;
|
||||||
|
headersValue.push_back(QPair<QString, QString>(QString::number(first), QString::number(second)));
|
||||||
|
}
|
||||||
|
|
||||||
|
initPacketMagicHeader = headersValue.at(0).first + "-" + headersValue.at(0).second;
|
||||||
|
responsePacketMagicHeader = headersValue.at(1).first + "-" + headersValue.at(1).second;
|
||||||
|
underloadPacketMagicHeader = headersValue.at(2).first + "-" + headersValue.at(2).second;
|
||||||
|
transportPacketMagicHeader = headersValue.at(3).first + "-" + headersValue.at(3).second;
|
||||||
|
} else {
|
||||||
|
// AWG legacy: use single values for magic headers
|
||||||
|
QSet<QString> headersValue;
|
||||||
|
while (headersValue.size() != 4) {
|
||||||
|
auto max = (std::numeric_limits<qint32>::max)();
|
||||||
|
headersValue.insert(QString::number(QRandomGenerator::global()->bounded(5, max)));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto headersValueList = headersValue.values();
|
||||||
|
initPacketMagicHeader = headersValueList.at(0);
|
||||||
|
responsePacketMagicHeader = headersValueList.at(1);
|
||||||
|
underloadPacketMagicHeader = headersValueList.at(2);
|
||||||
|
transportPacketMagicHeader = headersValueList.at(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
serverConfig.junkPacketCount = junkPacketCount;
|
||||||
|
serverConfig.junkPacketMinSize = junkPacketMinSize;
|
||||||
|
serverConfig.junkPacketMaxSize = junkPacketMaxSize;
|
||||||
|
serverConfig.initPacketJunkSize = initPacketJunkSize;
|
||||||
|
serverConfig.responsePacketJunkSize = responsePacketJunkSize;
|
||||||
|
serverConfig.initPacketMagicHeader = initPacketMagicHeader;
|
||||||
|
serverConfig.responsePacketMagicHeader = responsePacketMagicHeader;
|
||||||
|
serverConfig.underloadPacketMagicHeader = underloadPacketMagicHeader;
|
||||||
|
serverConfig.transportPacketMagicHeader = transportPacketMagicHeader;
|
||||||
|
|
||||||
|
serverConfig.cookieReplyPacketJunkSize = cookieReplyPacketJunkSize;
|
||||||
|
serverConfig.transportPacketJunkSize = transportPacketJunkSize;
|
||||||
|
|
||||||
|
serverConfig.specialJunk1 = protocols::awg::defaultSpecialJunk1;
|
||||||
|
serverConfig.specialJunk2 = protocols::awg::defaultSpecialJunk2;
|
||||||
|
serverConfig.specialJunk3 = protocols::awg::defaultSpecialJunk3;
|
||||||
|
serverConfig.specialJunk4 = protocols::awg::defaultSpecialJunk4;
|
||||||
|
serverConfig.specialJunk5 = protocols::awg::defaultSpecialJunk5;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode AwgInstaller::extractConfigFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, ContainerConfig &config)
|
||||||
|
{
|
||||||
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
|
|
||||||
|
// Use appropriate config path based on container type
|
||||||
|
QString configPath = protocols::awg::serverConfigPath;
|
||||||
|
if (container == DockerContainer::Awg) {
|
||||||
|
configPath = protocols::awg::serverLegacyConfigPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString serverConfig = sshSession->getTextFileFromContainer(container, credentials, configPath, errorCode);
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, QString> serverConfigMap;
|
||||||
|
auto serverConfigLines = serverConfig.split("\n");
|
||||||
|
for (auto &line : serverConfigLines) {
|
||||||
|
auto trimmedLine = line.trimmed();
|
||||||
|
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
QStringList parts = trimmedLine.split(" = ");
|
||||||
|
if (parts.count() == 2) {
|
||||||
|
serverConfigMap.insert(parts[0].trimmed(), parts[1].trimmed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto* awgConfig = config.getAwgProtocolConfig()) {
|
||||||
|
QString addressValue = serverConfigMap.value("Address");
|
||||||
|
QStringList addressParts = addressValue.split("/");
|
||||||
|
awgConfig->serverConfig.subnetAddress = addressParts.value(0);
|
||||||
|
if (addressParts.size() > 1) {
|
||||||
|
awgConfig->serverConfig.subnetCidr = addressParts.value(1);
|
||||||
|
}
|
||||||
|
awgConfig->serverConfig.junkPacketCount = serverConfigMap.value(configKey::junkPacketCount);
|
||||||
|
awgConfig->serverConfig.junkPacketMinSize = serverConfigMap.value(configKey::junkPacketMinSize);
|
||||||
|
awgConfig->serverConfig.junkPacketMaxSize = serverConfigMap.value(configKey::junkPacketMaxSize);
|
||||||
|
awgConfig->serverConfig.initPacketJunkSize = serverConfigMap.value(configKey::initPacketJunkSize);
|
||||||
|
awgConfig->serverConfig.responsePacketJunkSize = serverConfigMap.value(configKey::responsePacketJunkSize);
|
||||||
|
awgConfig->serverConfig.initPacketMagicHeader = serverConfigMap.value(configKey::initPacketMagicHeader);
|
||||||
|
awgConfig->serverConfig.responsePacketMagicHeader = serverConfigMap.value(configKey::responsePacketMagicHeader);
|
||||||
|
awgConfig->serverConfig.underloadPacketMagicHeader = serverConfigMap.value(configKey::underloadPacketMagicHeader);
|
||||||
|
awgConfig->serverConfig.transportPacketMagicHeader = serverConfigMap.value(configKey::transportPacketMagicHeader);
|
||||||
|
|
||||||
|
// hack to parse i1-i5 from commented lines in server config
|
||||||
|
awgConfig->serverConfig.specialJunk1 = serverConfigMap.value(QString("# ") + configKey::specialJunk1);
|
||||||
|
awgConfig->serverConfig.specialJunk2 = serverConfigMap.value(QString("# ") + configKey::specialJunk2);
|
||||||
|
awgConfig->serverConfig.specialJunk3 = serverConfigMap.value(QString("# ") + configKey::specialJunk3);
|
||||||
|
awgConfig->serverConfig.specialJunk4 = serverConfigMap.value(QString("# ") + configKey::specialJunk4);
|
||||||
|
awgConfig->serverConfig.specialJunk5 = serverConfigMap.value(QString("# ") + configKey::specialJunk5);
|
||||||
|
|
||||||
|
// AWG 2.0 specific fields
|
||||||
|
if (container == DockerContainer::Awg2) {
|
||||||
|
awgConfig->serverConfig.protocolVersion = "2";
|
||||||
|
awgConfig->serverConfig.cookieReplyPacketJunkSize = serverConfigMap.value(configKey::cookieReplyPacketJunkSize);
|
||||||
|
awgConfig->serverConfig.transportPacketJunkSize = serverConfigMap.value(configKey::transportPacketJunkSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
21
client/core/installers/awgInstaller.h
Normal file
21
client/core/installers/awgInstaller.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef AWGINSTALLER_H
|
||||||
|
#define AWGINSTALLER_H
|
||||||
|
|
||||||
|
#include "installerBase.h"
|
||||||
|
|
||||||
|
class AwgInstaller : public InstallerBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit AwgInstaller(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
amnezia::ContainerConfig generateConfig(amnezia::DockerContainer container, int port, amnezia::TransportProto transportProto) override;
|
||||||
|
amnezia::ErrorCode extractConfigFromContainer(amnezia::DockerContainer container, const amnezia::ServerCredentials &credentials,
|
||||||
|
SshSession* serverController, amnezia::ContainerConfig &config) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void generateAwgParameters(amnezia::AwgServerConfig &serverConfig, bool isAwg2 = false);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // AWGINSTALLER_H
|
||||||
|
|
||||||
116
client/core/installers/installerBase.cpp
Normal file
116
client/core/installers/installerBase.cpp
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
#include "installerBase.h"
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/models/protocolConfig.h"
|
||||||
|
#include "core/models/protocols/awgProtocolConfig.h"
|
||||||
|
#include "core/models/protocols/wireGuardProtocolConfig.h"
|
||||||
|
#include "core/models/protocols/openVpnProtocolConfig.h"
|
||||||
|
#include "core/models/protocols/xrayProtocolConfig.h"
|
||||||
|
#include "core/models/protocols/sftpProtocolConfig.h"
|
||||||
|
#include "core/models/protocols/socks5ProxyProtocolConfig.h"
|
||||||
|
#include "core/models/protocols/ikev2ProtocolConfig.h"
|
||||||
|
#include "core/models/protocols/torProtocolConfig.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
using namespace ProtocolUtils;
|
||||||
|
|
||||||
|
InstallerBase::InstallerBase(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainerConfig InstallerBase::generateConfig(DockerContainer container, int port, TransportProto transportProto)
|
||||||
|
{
|
||||||
|
return createBaseConfig(container, port, transportProto);
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode InstallerBase::extractConfigFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, ContainerConfig &config)
|
||||||
|
{
|
||||||
|
Q_UNUSED(container);
|
||||||
|
Q_UNUSED(credentials);
|
||||||
|
Q_UNUSED(sshSession);
|
||||||
|
Q_UNUSED(config);
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainerConfig InstallerBase::createBaseConfig(DockerContainer container, int port, TransportProto transportProto)
|
||||||
|
{
|
||||||
|
ContainerConfig config;
|
||||||
|
config.container = container;
|
||||||
|
|
||||||
|
Proto protocol = ContainerUtils::defaultProtocol(container);
|
||||||
|
QString portStr = QString::number(port);
|
||||||
|
QString transportProtoStr = ProtocolUtils::transportProtoToString(transportProto, protocol);
|
||||||
|
|
||||||
|
switch (protocol) {
|
||||||
|
case Proto::Awg: {
|
||||||
|
AwgProtocolConfig awgConfig;
|
||||||
|
awgConfig.serverConfig.port = portStr;
|
||||||
|
awgConfig.serverConfig.transportProto = transportProtoStr;
|
||||||
|
config.protocolConfig = awgConfig;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Proto::WireGuard: {
|
||||||
|
WireGuardProtocolConfig wgConfig;
|
||||||
|
wgConfig.serverConfig.port = portStr;
|
||||||
|
wgConfig.serverConfig.transportProto = transportProtoStr;
|
||||||
|
config.protocolConfig = wgConfig;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Proto::OpenVpn: {
|
||||||
|
OpenVpnProtocolConfig ovpnConfig;
|
||||||
|
ovpnConfig.serverConfig.port = portStr;
|
||||||
|
ovpnConfig.serverConfig.transportProto = transportProtoStr;
|
||||||
|
config.protocolConfig = ovpnConfig;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Proto::Xray:
|
||||||
|
case Proto::SSXray: {
|
||||||
|
XrayProtocolConfig xrayConfig;
|
||||||
|
xrayConfig.serverConfig.port = portStr;
|
||||||
|
xrayConfig.serverConfig.transportProto = transportProtoStr;
|
||||||
|
config.protocolConfig = xrayConfig;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Proto::Sftp: {
|
||||||
|
SftpProtocolConfig sftpConfig;
|
||||||
|
sftpConfig.port = portStr;
|
||||||
|
config.protocolConfig = sftpConfig;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Proto::Socks5Proxy: {
|
||||||
|
Socks5ProxyProtocolConfig socks5Config;
|
||||||
|
socks5Config.port = portStr;
|
||||||
|
config.protocolConfig = socks5Config;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Proto::Ikev2: {
|
||||||
|
Ikev2ProtocolConfig ikev2Config;
|
||||||
|
config.protocolConfig = ikev2Config;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Proto::TorWebSite: {
|
||||||
|
TorProtocolConfig torConfig;
|
||||||
|
config.protocolConfig = torConfig;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Proto::Dns: {
|
||||||
|
DnsProtocolConfig dnsConfig;
|
||||||
|
config.protocolConfig = dnsConfig;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
32
client/core/installers/installerBase.h
Normal file
32
client/core/installers/installerBase.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#ifndef INSTALLERBASE_H
|
||||||
|
#define INSTALLERBASE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/errorCodes.h"
|
||||||
|
#include "core/utils/routeModes.h"
|
||||||
|
#include "core/utils/commonStructs.h"
|
||||||
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
|
#include "core/models/containerConfig.h"
|
||||||
|
|
||||||
|
class InstallerBase : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit InstallerBase(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
virtual amnezia::ContainerConfig generateConfig(amnezia::DockerContainer container, int port, amnezia::TransportProto transportProto);
|
||||||
|
|
||||||
|
virtual amnezia::ErrorCode extractConfigFromContainer(amnezia::DockerContainer container, const amnezia::ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, amnezia::ContainerConfig &config);
|
||||||
|
|
||||||
|
amnezia::ContainerConfig createBaseConfig(amnezia::DockerContainer container, int port, amnezia::TransportProto transportProto);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INSTALLERBASE_H
|
||||||
|
|
||||||
73
client/core/installers/openvpnInstaller.cpp
Normal file
73
client/core/installers/openvpnInstaller.cpp
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#include "openvpnInstaller.h"
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
using namespace ProtocolUtils;
|
||||||
|
|
||||||
|
OpenVpnInstaller::OpenVpnInstaller(QObject *parent)
|
||||||
|
: InstallerBase(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode OpenVpnInstaller::extractConfigFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, ContainerConfig &config)
|
||||||
|
{
|
||||||
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
|
|
||||||
|
QString serverConfig = sshSession->getTextFileFromContainer(container, credentials,
|
||||||
|
protocols::openvpn::serverConfigPath, errorCode);
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, QString> serverConfigMap;
|
||||||
|
auto serverConfigLines = serverConfig.split("\n");
|
||||||
|
for (auto &line : serverConfigLines) {
|
||||||
|
auto trimmedLine = line.trimmed();
|
||||||
|
if (trimmedLine.startsWith("#") || trimmedLine.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
QStringList parts = trimmedLine.split(" ");
|
||||||
|
if (parts.count() >= 2) {
|
||||||
|
QString key = parts[0];
|
||||||
|
QString value = parts.mid(1).join(" ");
|
||||||
|
serverConfigMap.insert(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto* ovpnConfig = config.getOpenVpnProtocolConfig()) {
|
||||||
|
QString serverValue = serverConfigMap.value("server");
|
||||||
|
|
||||||
|
if (!serverValue.isEmpty()) {
|
||||||
|
QStringList serverParts = serverValue.split(" ");
|
||||||
|
if (serverParts.count() >= 1) {
|
||||||
|
ovpnConfig->serverConfig.subnetAddress = serverParts[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ovpnConfig->serverConfig.ncpDisable = serverConfig.contains("ncp-disable");
|
||||||
|
ovpnConfig->serverConfig.tlsAuth = serverConfig.contains("tls-auth");
|
||||||
|
|
||||||
|
QString cipher = serverConfigMap.value("cipher");
|
||||||
|
if (!cipher.isEmpty()) {
|
||||||
|
ovpnConfig->serverConfig.cipher = cipher;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString hash = serverConfigMap.value("auth");
|
||||||
|
if (!hash.isEmpty()) {
|
||||||
|
ovpnConfig->serverConfig.hash = hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
17
client/core/installers/openvpnInstaller.h
Normal file
17
client/core/installers/openvpnInstaller.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef OPENVPNINSTALLER_H
|
||||||
|
#define OPENVPNINSTALLER_H
|
||||||
|
|
||||||
|
#include "installerBase.h"
|
||||||
|
|
||||||
|
class OpenVpnInstaller : public InstallerBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit OpenVpnInstaller(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
amnezia::ErrorCode extractConfigFromContainer(amnezia::DockerContainer container, const amnezia::ServerCredentials &credentials,
|
||||||
|
SshSession* serverController, amnezia::ContainerConfig &config) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // OPENVPNINSTALLER_H
|
||||||
|
|
||||||
69
client/core/installers/sftpInstaller.cpp
Normal file
69
client/core/installers/sftpInstaller.cpp
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#include "sftpInstaller.h"
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
|
#include "core/utils/utilities.h"
|
||||||
|
#include "core/models/protocols/sftpProtocolConfig.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
using namespace ProtocolUtils;
|
||||||
|
|
||||||
|
SftpInstaller::SftpInstaller(QObject *parent)
|
||||||
|
: InstallerBase(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainerConfig SftpInstaller::generateConfig(DockerContainer container, int port, TransportProto transportProto)
|
||||||
|
{
|
||||||
|
ContainerConfig config = createBaseConfig(container, port, transportProto);
|
||||||
|
|
||||||
|
if (auto* sftpConfig = config.getSftpProtocolConfig()) {
|
||||||
|
sftpConfig->userName = protocols::sftp::defaultUserName;
|
||||||
|
sftpConfig->password = Utils::getRandomString(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode SftpInstaller::extractConfigFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, ContainerConfig &config)
|
||||||
|
{
|
||||||
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
|
|
||||||
|
QString stdOut;
|
||||||
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
};
|
||||||
|
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
||||||
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
};
|
||||||
|
|
||||||
|
QString containerName = ContainerUtils::containerToString(container);
|
||||||
|
QString script = QString("sudo docker inspect --format '{{.Config.Cmd}}' %1").arg(containerName);
|
||||||
|
|
||||||
|
errorCode = sshSession->runScript(credentials, script, cbReadStdOut, cbReadStdErr);
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sftpInfo = stdOut.split(":");
|
||||||
|
if (sftpInfo.size() < 2) {
|
||||||
|
return ErrorCode::ServerContainerMissingError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto* sftpConfig = config.getSftpProtocolConfig()) {
|
||||||
|
sftpConfig->userName = sftpInfo.at(0).trimmed();
|
||||||
|
sftpConfig->password = sftpInfo.at(1).trimmed();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
18
client/core/installers/sftpInstaller.h
Normal file
18
client/core/installers/sftpInstaller.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef SFTPINSTALLER_H
|
||||||
|
#define SFTPINSTALLER_H
|
||||||
|
|
||||||
|
#include "installerBase.h"
|
||||||
|
|
||||||
|
class SftpInstaller : public InstallerBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit SftpInstaller(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
amnezia::ContainerConfig generateConfig(amnezia::DockerContainer container, int port, amnezia::TransportProto transportProto) override;
|
||||||
|
amnezia::ErrorCode extractConfigFromContainer(amnezia::DockerContainer container, const amnezia::ServerCredentials &credentials,
|
||||||
|
SshSession* serverController, amnezia::ContainerConfig &config) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SFTPINSTALLER_H
|
||||||
|
|
||||||
42
client/core/installers/socks5Installer.cpp
Normal file
42
client/core/installers/socks5Installer.cpp
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#include "socks5Installer.h"
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
|
#include "core/utils/utilities.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
using namespace ProtocolUtils;
|
||||||
|
|
||||||
|
Socks5Installer::Socks5Installer(QObject *parent)
|
||||||
|
: InstallerBase(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainerConfig Socks5Installer::generateConfig(DockerContainer container, int port, TransportProto transportProto)
|
||||||
|
{
|
||||||
|
ContainerConfig config = createBaseConfig(container, port, transportProto);
|
||||||
|
|
||||||
|
if (auto* socks5Config = config.getSocks5ProxyProtocolConfig()) {
|
||||||
|
socks5Config->userName = protocols::socks5Proxy::defaultUserName;
|
||||||
|
socks5Config->password = Utils::getRandomString(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode Socks5Installer::extractConfigFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, ContainerConfig &config)
|
||||||
|
{
|
||||||
|
Q_UNUSED(container);
|
||||||
|
Q_UNUSED(credentials);
|
||||||
|
Q_UNUSED(sshSession);
|
||||||
|
Q_UNUSED(config);
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
18
client/core/installers/socks5Installer.h
Normal file
18
client/core/installers/socks5Installer.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef SOCKS5INSTALLER_H
|
||||||
|
#define SOCKS5INSTALLER_H
|
||||||
|
|
||||||
|
#include "installerBase.h"
|
||||||
|
|
||||||
|
class Socks5Installer : public InstallerBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit Socks5Installer(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
amnezia::ContainerConfig generateConfig(amnezia::DockerContainer container, int port, amnezia::TransportProto transportProto) override;
|
||||||
|
amnezia::ErrorCode extractConfigFromContainer(amnezia::DockerContainer container, const amnezia::ServerCredentials &credentials,
|
||||||
|
SshSession* serverController, amnezia::ContainerConfig &config) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SOCKS5INSTALLER_H
|
||||||
|
|
||||||
57
client/core/installers/torInstaller.cpp
Normal file
57
client/core/installers/torInstaller.cpp
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#include "torInstaller.h"
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
|
#include "core/models/protocols/torProtocolConfig.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
using namespace ProtocolUtils;
|
||||||
|
|
||||||
|
TorInstaller::TorInstaller(QObject *parent)
|
||||||
|
: InstallerBase(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode TorInstaller::extractConfigFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, ContainerConfig &config)
|
||||||
|
{
|
||||||
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
|
|
||||||
|
QString stdOut;
|
||||||
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
};
|
||||||
|
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
||||||
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
};
|
||||||
|
|
||||||
|
QString containerName = ContainerUtils::containerToString(container);
|
||||||
|
QString script = QString("sudo docker exec -i %1 sh -c 'cat /var/lib/tor/hidden_service/hostname'").arg(containerName);
|
||||||
|
|
||||||
|
errorCode = sshSession->runScript(credentials, script, cbReadStdOut, cbReadStdErr);
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdOut.isEmpty()) {
|
||||||
|
return ErrorCode::ServerContainerMissingError;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString onion = stdOut;
|
||||||
|
onion.replace("\n", "");
|
||||||
|
|
||||||
|
if (auto* torConfig = config.getTorProtocolConfig()) {
|
||||||
|
torConfig->serverConfig.site = onion;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
17
client/core/installers/torInstaller.h
Normal file
17
client/core/installers/torInstaller.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef TORINSTALLER_H
|
||||||
|
#define TORINSTALLER_H
|
||||||
|
|
||||||
|
#include "installerBase.h"
|
||||||
|
|
||||||
|
class TorInstaller : public InstallerBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit TorInstaller(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
amnezia::ErrorCode extractConfigFromContainer(amnezia::DockerContainer container, const amnezia::ServerCredentials &credentials,
|
||||||
|
SshSession* serverController, amnezia::ContainerConfig &config) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TORINSTALLER_H
|
||||||
|
|
||||||
51
client/core/installers/wireguardInstaller.cpp
Normal file
51
client/core/installers/wireguardInstaller.cpp
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#include "wireguardInstaller.h"
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
using namespace ProtocolUtils;
|
||||||
|
|
||||||
|
WireguardInstaller::WireguardInstaller(QObject *parent)
|
||||||
|
: InstallerBase(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode WireguardInstaller::extractConfigFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, ContainerConfig &config)
|
||||||
|
{
|
||||||
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
|
|
||||||
|
QString serverConfig = sshSession->getTextFileFromContainer(container, credentials,
|
||||||
|
protocols::wireguard::serverConfigPath, errorCode);
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, QString> serverConfigMap;
|
||||||
|
auto serverConfigLines = serverConfig.split("\n");
|
||||||
|
for (auto &line : serverConfigLines) {
|
||||||
|
auto trimmedLine = line.trimmed();
|
||||||
|
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
QStringList parts = trimmedLine.split(" = ");
|
||||||
|
if (parts.count() == 2) {
|
||||||
|
serverConfigMap.insert(parts[0].trimmed(), parts[1].trimmed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto* wgConfig = config.getWireGuardProtocolConfig()) {
|
||||||
|
wgConfig->serverConfig.subnetAddress = serverConfigMap.value("Address").remove("/24");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
17
client/core/installers/wireguardInstaller.h
Normal file
17
client/core/installers/wireguardInstaller.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef WIREGUARDINSTALLER_H
|
||||||
|
#define WIREGUARDINSTALLER_H
|
||||||
|
|
||||||
|
#include "installerBase.h"
|
||||||
|
|
||||||
|
class WireguardInstaller : public InstallerBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit WireguardInstaller(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
amnezia::ErrorCode extractConfigFromContainer(amnezia::DockerContainer container, const amnezia::ServerCredentials &credentials,
|
||||||
|
SshSession* serverController, amnezia::ContainerConfig &config) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WIREGUARDINSTALLER_H
|
||||||
|
|
||||||
80
client/core/installers/xrayInstaller.cpp
Normal file
80
client/core/installers/xrayInstaller.cpp
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
#include "xrayInstaller.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonArray>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/utils/selfhosted/sshSession.h"
|
||||||
|
#include "core/models/protocols/xrayProtocolConfig.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
Logger logger("XrayInstaller");
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
using namespace ProtocolUtils;
|
||||||
|
|
||||||
|
XrayInstaller::XrayInstaller(QObject *parent)
|
||||||
|
: InstallerBase(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode XrayInstaller::extractConfigFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||||
|
SshSession* sshSession, ContainerConfig &config)
|
||||||
|
{
|
||||||
|
ErrorCode errorCode = ErrorCode::NoError;
|
||||||
|
|
||||||
|
QString currentConfig = sshSession->getTextFileFromContainer(
|
||||||
|
container, credentials, amnezia::protocols::xray::serverConfigPath, errorCode);
|
||||||
|
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(currentConfig.toUtf8());
|
||||||
|
if (doc.isNull() || !doc.isObject()) {
|
||||||
|
logger.error() << "Failed to parse server config JSON";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
QJsonObject serverConfig = doc.object();
|
||||||
|
|
||||||
|
if (!serverConfig.contains(protocols::xray::inbounds)) {
|
||||||
|
logger.error() << "Server config missing 'inbounds' field";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray inbounds = serverConfig[protocols::xray::inbounds].toArray();
|
||||||
|
if (inbounds.isEmpty()) {
|
||||||
|
logger.error() << "Server config has empty 'inbounds' array";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject inbound = inbounds[0].toObject();
|
||||||
|
if (!inbound.contains(protocols::xray::streamSettings)) {
|
||||||
|
logger.error() << "Inbound missing 'streamSettings' field";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject streamSettings = inbound[protocols::xray::streamSettings].toObject();
|
||||||
|
QJsonObject realitySettings = streamSettings[protocols::xray::realitySettings].toObject();
|
||||||
|
if (!realitySettings.contains(protocols::xray::serverNames)) {
|
||||||
|
logger.error() << "Settings missing 'serverNames' field";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString siteName = realitySettings[protocols::xray::serverNames][0].toString();
|
||||||
|
|
||||||
|
if (auto* xrayConfig = config.getXrayProtocolConfig()) {
|
||||||
|
xrayConfig->serverConfig.site = siteName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
17
client/core/installers/xrayInstaller.h
Normal file
17
client/core/installers/xrayInstaller.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef XRAYINSTALLER_H
|
||||||
|
#define XRAYINSTALLER_H
|
||||||
|
|
||||||
|
#include "installerBase.h"
|
||||||
|
|
||||||
|
class XrayInstaller : public InstallerBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit XrayInstaller(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
amnezia::ErrorCode extractConfigFromContainer(amnezia::DockerContainer container, const amnezia::ServerCredentials &credentials,
|
||||||
|
SshSession* serverController, amnezia::ContainerConfig &config) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // XRAYINSTALLER_H
|
||||||
|
|
||||||
223
client/core/models/api/apiConfig.cpp
Normal file
223
client/core/models/api/apiConfig.cpp
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
#include "apiConfig.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#include "core/utils/api/apiUtils.h"
|
||||||
|
#include "core/utils/constants/apiKeys.h"
|
||||||
|
|
||||||
|
namespace amnezia
|
||||||
|
{
|
||||||
|
|
||||||
|
QJsonObject ApiConfig::Subscription::toJson() const
|
||||||
|
{
|
||||||
|
QJsonObject obj;
|
||||||
|
if (!endDate.isEmpty()) {
|
||||||
|
obj[apiDefs::key::endDate] = endDate;
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiConfig::Subscription ApiConfig::Subscription::fromJson(const QJsonObject& json)
|
||||||
|
{
|
||||||
|
Subscription sub;
|
||||||
|
sub.endDate = json.value(apiDefs::key::endDate).toString();
|
||||||
|
return sub;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ApiConfig::ServiceInfo::toJson() const
|
||||||
|
{
|
||||||
|
QJsonObject obj;
|
||||||
|
obj[apiDefs::key::isAdVisible] = isAdVisible;
|
||||||
|
obj[apiDefs::key::isRenewalAvailable] = isRenewalAvailable;
|
||||||
|
if (!adHeader.isEmpty()) {
|
||||||
|
obj[apiDefs::key::adHeader] = adHeader;
|
||||||
|
}
|
||||||
|
if (!adDescription.isEmpty()) {
|
||||||
|
obj[apiDefs::key::adDescription] = adDescription;
|
||||||
|
}
|
||||||
|
if (!adEndpoint.isEmpty()) {
|
||||||
|
obj[apiDefs::key::adEndpoint] = adEndpoint;
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiConfig::ServiceInfo ApiConfig::ServiceInfo::fromJson(const QJsonObject& json)
|
||||||
|
{
|
||||||
|
ServiceInfo info;
|
||||||
|
info.isAdVisible = json.value(apiDefs::key::isAdVisible).toBool(false);
|
||||||
|
info.isRenewalAvailable = json.value(apiDefs::key::isRenewalAvailable).toBool(false);
|
||||||
|
info.adHeader = json.value(apiDefs::key::adHeader).toString();
|
||||||
|
info.adDescription = json.value(apiDefs::key::adDescription).toString();
|
||||||
|
info.adEndpoint = json.value(apiDefs::key::adEndpoint).toString();
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ApiConfig::PublicKeyInfo::toJson() const
|
||||||
|
{
|
||||||
|
QJsonObject obj;
|
||||||
|
if (!expiresAt.isEmpty()) {
|
||||||
|
obj[apiDefs::key::expiresAt] = expiresAt;
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiConfig::PublicKeyInfo ApiConfig::PublicKeyInfo::fromJson(const QJsonObject& json)
|
||||||
|
{
|
||||||
|
PublicKeyInfo info;
|
||||||
|
info.expiresAt = json.value(apiDefs::key::expiresAt).toString();
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiConfig::isPremium() const
|
||||||
|
{
|
||||||
|
return serviceType == "amnezia-premium";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiConfig::isFree() const
|
||||||
|
{
|
||||||
|
return serviceType == "amnezia-free";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiConfig::isExternalPremium() const
|
||||||
|
{
|
||||||
|
return serviceType == "external-premium";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiConfig::isSubscriptionExpired() const
|
||||||
|
{
|
||||||
|
if (subscription.endDate.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime endDate = QDateTime::fromString(subscription.endDate, Qt::ISODateWithMs);
|
||||||
|
if (!endDate.isValid()) {
|
||||||
|
endDate = QDateTime::fromString(subscription.endDate, Qt::ISODate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!endDate.isValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return endDate < QDateTime::currentDateTimeUtc();
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ApiConfig::toJson() const
|
||||||
|
{
|
||||||
|
QJsonObject obj;
|
||||||
|
|
||||||
|
if (!serviceType.isEmpty()) {
|
||||||
|
obj[apiDefs::key::serviceType] = serviceType;
|
||||||
|
}
|
||||||
|
if (!serviceProtocol.isEmpty()) {
|
||||||
|
obj[QLatin1String("service_protocol")] = serviceProtocol;
|
||||||
|
}
|
||||||
|
if (!userCountryCode.isEmpty()) {
|
||||||
|
obj[QLatin1String("user_country_code")] = userCountryCode;
|
||||||
|
}
|
||||||
|
if (!serverCountryCode.isEmpty()) {
|
||||||
|
obj[apiDefs::key::serverCountryCode] = serverCountryCode;
|
||||||
|
}
|
||||||
|
if (!serverCountryName.isEmpty()) {
|
||||||
|
obj[apiDefs::key::serverCountryName] = serverCountryName;
|
||||||
|
}
|
||||||
|
if (!vpnKey.isEmpty()) {
|
||||||
|
obj[apiDefs::key::vpnKey] = vpnKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject subscriptionObj = subscription.toJson();
|
||||||
|
if (!subscriptionObj.isEmpty()) {
|
||||||
|
obj[apiDefs::key::subscription] = subscriptionObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeDeviceCount > 0) {
|
||||||
|
obj[apiDefs::key::activeDeviceCount] = activeDeviceCount;
|
||||||
|
}
|
||||||
|
if (maxDeviceCount > 0) {
|
||||||
|
obj[apiDefs::key::maxDeviceCount] = maxDeviceCount;
|
||||||
|
}
|
||||||
|
if (issuedConfigs > 0) {
|
||||||
|
obj[apiDefs::key::issuedConfigs] = issuedConfigs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!availableCountries.isEmpty()) {
|
||||||
|
obj[apiDefs::key::availableCountries] = availableCountries;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!supportedProtocols.isEmpty()) {
|
||||||
|
obj[apiDefs::key::supportedProtocols] = supportedProtocols;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject serviceInfoObj = serviceInfo.toJson();
|
||||||
|
if (!serviceInfoObj.isEmpty()) {
|
||||||
|
obj[apiDefs::key::serviceInfo] = serviceInfoObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject publicKeyObj = publicKey.toJson();
|
||||||
|
if (!publicKeyObj.isEmpty()) {
|
||||||
|
obj[apiDefs::key::publicKey] = publicKeyObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stackType.isEmpty()) {
|
||||||
|
obj[apiDefs::key::stackType] = stackType;
|
||||||
|
}
|
||||||
|
if (!cliVersion.isEmpty()) {
|
||||||
|
obj[apiDefs::key::cliVersion] = cliVersion;
|
||||||
|
}
|
||||||
|
if (isTestPurchase) {
|
||||||
|
obj[apiDefs::key::isTestPurchase] = isTestPurchase;
|
||||||
|
}
|
||||||
|
if (isInAppPurchase) {
|
||||||
|
obj[apiDefs::key::isInAppPurchase] = isInAppPurchase;
|
||||||
|
}
|
||||||
|
if (subscriptionExpiredByServer) {
|
||||||
|
obj[apiDefs::key::subscriptionExpiredByServer] = subscriptionExpiredByServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiConfig ApiConfig::fromJson(const QJsonObject& json)
|
||||||
|
{
|
||||||
|
ApiConfig config;
|
||||||
|
|
||||||
|
config.serviceType = json.value(apiDefs::key::serviceType).toString();
|
||||||
|
config.serviceProtocol = json.value(QLatin1String("service_protocol")).toString();
|
||||||
|
config.userCountryCode = json.value(QLatin1String("user_country_code")).toString();
|
||||||
|
config.serverCountryCode = json.value(apiDefs::key::serverCountryCode).toString();
|
||||||
|
config.serverCountryName = json.value(apiDefs::key::serverCountryName).toString();
|
||||||
|
config.vpnKey = json.value(apiDefs::key::vpnKey).toString();
|
||||||
|
|
||||||
|
QJsonObject subscriptionObj = json.value(apiDefs::key::subscription).toObject();
|
||||||
|
if (!subscriptionObj.isEmpty()) {
|
||||||
|
config.subscription = Subscription::fromJson(subscriptionObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
config.activeDeviceCount = json.value(apiDefs::key::activeDeviceCount).toInt(0);
|
||||||
|
config.maxDeviceCount = json.value(apiDefs::key::maxDeviceCount).toInt(0);
|
||||||
|
config.issuedConfigs = json.value(apiDefs::key::issuedConfigs).toInt(0);
|
||||||
|
|
||||||
|
config.availableCountries = json.value(apiDefs::key::availableCountries).toArray();
|
||||||
|
config.supportedProtocols = json.value(apiDefs::key::supportedProtocols).toArray();
|
||||||
|
|
||||||
|
QJsonObject serviceInfoObj = json.value(apiDefs::key::serviceInfo).toObject();
|
||||||
|
if (!serviceInfoObj.isEmpty()) {
|
||||||
|
config.serviceInfo = ServiceInfo::fromJson(serviceInfoObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject publicKeyObj = json.value(apiDefs::key::publicKey).toObject();
|
||||||
|
if (!publicKeyObj.isEmpty()) {
|
||||||
|
config.publicKey = PublicKeyInfo::fromJson(publicKeyObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
config.stackType = json.value(apiDefs::key::stackType).toString();
|
||||||
|
config.cliVersion = json.value(apiDefs::key::cliVersion).toString();
|
||||||
|
config.isTestPurchase = json.value(apiDefs::key::isTestPurchase).toBool(false);
|
||||||
|
config.isInAppPurchase = json.value(apiDefs::key::isInAppPurchase).toBool(false);
|
||||||
|
config.subscriptionExpiredByServer = json.value(apiDefs::key::subscriptionExpiredByServer).toBool(false);
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace amnezia
|
||||||
|
|
||||||
77
client/core/models/api/apiConfig.h
Normal file
77
client/core/models/api/apiConfig.h
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#ifndef APICONFIG_H
|
||||||
|
#define APICONFIG_H
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QString>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#include "core/utils/api/apiEnums.h"
|
||||||
|
#include "core/utils/constants/apiKeys.h"
|
||||||
|
#include "core/utils/constants/apiConstants.h"
|
||||||
|
|
||||||
|
namespace amnezia
|
||||||
|
{
|
||||||
|
|
||||||
|
struct ApiConfig
|
||||||
|
{
|
||||||
|
QString serviceType;
|
||||||
|
QString serviceProtocol;
|
||||||
|
QString userCountryCode;
|
||||||
|
QString serverCountryCode;
|
||||||
|
QString serverCountryName;
|
||||||
|
QString vpnKey;
|
||||||
|
|
||||||
|
struct Subscription {
|
||||||
|
QString endDate;
|
||||||
|
|
||||||
|
QJsonObject toJson() const;
|
||||||
|
static Subscription fromJson(const QJsonObject& json);
|
||||||
|
};
|
||||||
|
Subscription subscription;
|
||||||
|
|
||||||
|
int activeDeviceCount;
|
||||||
|
int maxDeviceCount;
|
||||||
|
int issuedConfigs;
|
||||||
|
QJsonArray availableCountries;
|
||||||
|
QJsonArray supportedProtocols;
|
||||||
|
|
||||||
|
struct ServiceInfo {
|
||||||
|
bool isAdVisible = false;
|
||||||
|
bool isRenewalAvailable = false;
|
||||||
|
QString adHeader;
|
||||||
|
QString adDescription;
|
||||||
|
QString adEndpoint;
|
||||||
|
|
||||||
|
QJsonObject toJson() const;
|
||||||
|
static ServiceInfo fromJson(const QJsonObject& json);
|
||||||
|
};
|
||||||
|
ServiceInfo serviceInfo;
|
||||||
|
|
||||||
|
struct PublicKeyInfo {
|
||||||
|
QString expiresAt;
|
||||||
|
|
||||||
|
QJsonObject toJson() const;
|
||||||
|
static PublicKeyInfo fromJson(const QJsonObject& json);
|
||||||
|
};
|
||||||
|
PublicKeyInfo publicKey;
|
||||||
|
|
||||||
|
QString stackType;
|
||||||
|
QString cliVersion;
|
||||||
|
bool isTestPurchase;
|
||||||
|
bool isInAppPurchase = false;
|
||||||
|
bool subscriptionExpiredByServer = false;
|
||||||
|
|
||||||
|
bool isPremium() const;
|
||||||
|
bool isFree() const;
|
||||||
|
bool isExternalPremium() const;
|
||||||
|
bool isSubscriptionExpired() const;
|
||||||
|
|
||||||
|
QJsonObject toJson() const;
|
||||||
|
static ApiConfig fromJson(const QJsonObject& json);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace amnezia
|
||||||
|
|
||||||
|
#endif // APICONFIG_H
|
||||||
|
|
||||||
140
client/core/models/api/apiV1ServerConfig.cpp
Normal file
140
client/core/models/api/apiV1ServerConfig.cpp
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
#include "apiV1ServerConfig.h"
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/apiKeys.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/utils/api/apiUtils.h"
|
||||||
|
|
||||||
|
namespace amnezia
|
||||||
|
{
|
||||||
|
|
||||||
|
using namespace ContainerEnumNS;
|
||||||
|
|
||||||
|
bool ApiV1ServerConfig::isPremium() const
|
||||||
|
{
|
||||||
|
constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT);
|
||||||
|
return apiEndpoint.contains(premiumV1Endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiV1ServerConfig::isFree() const
|
||||||
|
{
|
||||||
|
constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT);
|
||||||
|
return apiEndpoint.contains(freeV2Endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ApiV1ServerConfig::vpnKey() const
|
||||||
|
{
|
||||||
|
QJsonObject json = toJson();
|
||||||
|
return apiUtils::getPremiumV1VpnKey(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiV1ServerConfig::hasContainers() const
|
||||||
|
{
|
||||||
|
return !containers.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainerConfig ApiV1ServerConfig::containerConfig(DockerContainer container) const
|
||||||
|
{
|
||||||
|
if (!containers.contains(container)) {
|
||||||
|
return ContainerConfig{};
|
||||||
|
}
|
||||||
|
return containers.value(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ApiV1ServerConfig::toJson() const
|
||||||
|
{
|
||||||
|
QJsonObject obj;
|
||||||
|
|
||||||
|
if (!name.isEmpty()) {
|
||||||
|
obj[configKey::name] = name;
|
||||||
|
}
|
||||||
|
if (!description.isEmpty()) {
|
||||||
|
obj[configKey::description] = description;
|
||||||
|
}
|
||||||
|
if (!protocol.isEmpty()) {
|
||||||
|
obj[apiDefs::key::protocol] = protocol;
|
||||||
|
}
|
||||||
|
if (!apiEndpoint.isEmpty()) {
|
||||||
|
obj[apiDefs::key::apiEndpoint] = apiEndpoint;
|
||||||
|
}
|
||||||
|
if (!apiKey.isEmpty()) {
|
||||||
|
obj[apiDefs::key::apiKey] = apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj[configKey::configVersion] = configVersion;
|
||||||
|
|
||||||
|
if (!hostName.isEmpty()) {
|
||||||
|
obj[configKey::hostName] = hostName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray containersArray;
|
||||||
|
for (auto it = containers.begin(); it != containers.end(); ++it) {
|
||||||
|
QJsonObject containerObj = it.value().toJson();
|
||||||
|
containersArray.append(containerObj);
|
||||||
|
}
|
||||||
|
if (!containersArray.isEmpty()) {
|
||||||
|
obj[configKey::containers] = containersArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultContainer != DockerContainer::None) {
|
||||||
|
obj[configKey::defaultContainer] = ContainerUtils::containerToString(defaultContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dns1.isEmpty()) {
|
||||||
|
obj[configKey::dns1] = dns1;
|
||||||
|
}
|
||||||
|
if (!dns2.isEmpty()) {
|
||||||
|
obj[configKey::dns2] = dns2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crc > 0) {
|
||||||
|
obj[configKey::crc] = crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiV1ServerConfig ApiV1ServerConfig::fromJson(const QJsonObject& json)
|
||||||
|
{
|
||||||
|
ApiV1ServerConfig config;
|
||||||
|
|
||||||
|
config.name = json.value(configKey::name).toString();
|
||||||
|
config.description = json.value(configKey::description).toString();
|
||||||
|
config.protocol = json.value(apiDefs::key::protocol).toString();
|
||||||
|
config.apiEndpoint = json.value(apiDefs::key::apiEndpoint).toString();
|
||||||
|
config.apiKey = json.value(apiDefs::key::apiKey).toString();
|
||||||
|
config.configVersion = json.value(configKey::configVersion).toInt(1);
|
||||||
|
config.hostName = json.value(configKey::hostName).toString();
|
||||||
|
|
||||||
|
QJsonArray containersArray = json.value(configKey::containers).toArray();
|
||||||
|
for (const QJsonValue& val : containersArray) {
|
||||||
|
QJsonObject containerObj = val.toObject();
|
||||||
|
ContainerConfig containerConfig = ContainerConfig::fromJson(containerObj);
|
||||||
|
|
||||||
|
QString containerStr = containerObj.value(configKey::container).toString();
|
||||||
|
DockerContainer container = ContainerUtils::containerFromString(containerStr);
|
||||||
|
|
||||||
|
config.containers.insert(container, containerConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString defaultContainerStr = json.value(configKey::defaultContainer).toString();
|
||||||
|
config.defaultContainer = ContainerUtils::containerFromString(defaultContainerStr);
|
||||||
|
|
||||||
|
config.dns1 = json.value(configKey::dns1).toString();
|
||||||
|
config.dns2 = json.value(configKey::dns2).toString();
|
||||||
|
|
||||||
|
config.crc = json.value(configKey::crc).toInt(0);
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace amnezia
|
||||||
|
|
||||||
47
client/core/models/api/apiV1ServerConfig.h
Normal file
47
client/core/models/api/apiV1ServerConfig.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#ifndef APIV1SERVERCONFIG_H
|
||||||
|
#define APIV1SERVERCONFIG_H
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/models/containerConfig.h"
|
||||||
|
#include "core/utils/api/apiEnums.h"
|
||||||
|
#include "core/utils/constants/apiKeys.h"
|
||||||
|
#include "core/utils/constants/apiConstants.h"
|
||||||
|
|
||||||
|
namespace amnezia
|
||||||
|
{
|
||||||
|
|
||||||
|
using namespace ContainerEnumNS;
|
||||||
|
|
||||||
|
struct ApiV1ServerConfig {
|
||||||
|
QString description;
|
||||||
|
QString hostName;
|
||||||
|
QMap<DockerContainer, ContainerConfig> containers;
|
||||||
|
DockerContainer defaultContainer;
|
||||||
|
QString dns1;
|
||||||
|
QString dns2;
|
||||||
|
|
||||||
|
QString name;
|
||||||
|
QString protocol;
|
||||||
|
QString apiEndpoint;
|
||||||
|
QString apiKey;
|
||||||
|
int crc;
|
||||||
|
int configVersion;
|
||||||
|
|
||||||
|
bool isPremium() const;
|
||||||
|
bool isFree() const;
|
||||||
|
QString vpnKey() const;
|
||||||
|
bool hasContainers() const;
|
||||||
|
ContainerConfig containerConfig(DockerContainer container) const;
|
||||||
|
QJsonObject toJson() const;
|
||||||
|
static ApiV1ServerConfig fromJson(const QJsonObject& json);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace amnezia
|
||||||
|
|
||||||
|
#endif // APIV1SERVERCONFIG_H
|
||||||
|
|
||||||
170
client/core/models/api/apiV2ServerConfig.cpp
Normal file
170
client/core/models/api/apiV2ServerConfig.cpp
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
#include "apiV2ServerConfig.h"
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/protocols/protocolUtils.h"
|
||||||
|
#include "core/utils/constants/configKeys.h"
|
||||||
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
|
#include "core/utils/api/apiUtils.h"
|
||||||
|
#include "core/models/api/apiConfig.h"
|
||||||
|
#include "core/models/api/authData.h"
|
||||||
|
|
||||||
|
namespace amnezia
|
||||||
|
{
|
||||||
|
|
||||||
|
using namespace ContainerEnumNS;
|
||||||
|
|
||||||
|
QString ApiV2ServerConfig::vpnKey() const
|
||||||
|
{
|
||||||
|
if (!apiConfig.vpnKey.isEmpty()) {
|
||||||
|
return apiConfig.vpnKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject json = toJson();
|
||||||
|
return apiUtils::getPremiumV2VpnKey(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ApiV2ServerConfig::serviceType() const
|
||||||
|
{
|
||||||
|
return apiConfig.serviceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ApiV2ServerConfig::serviceProtocol() const
|
||||||
|
{
|
||||||
|
return apiConfig.serviceProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiV2ServerConfig::isPremium() const
|
||||||
|
{
|
||||||
|
return apiConfig.isPremium();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiV2ServerConfig::isFree() const
|
||||||
|
{
|
||||||
|
return apiConfig.isFree();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiV2ServerConfig::isExternalPremium() const
|
||||||
|
{
|
||||||
|
return apiConfig.isExternalPremium();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiV2ServerConfig::hasContainers() const
|
||||||
|
{
|
||||||
|
return !containers.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainerConfig ApiV2ServerConfig::containerConfig(DockerContainer container) const
|
||||||
|
{
|
||||||
|
if (!containers.contains(container)) {
|
||||||
|
return ContainerConfig{};
|
||||||
|
}
|
||||||
|
return containers.value(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ApiV2ServerConfig::toJson() const
|
||||||
|
{
|
||||||
|
QJsonObject obj;
|
||||||
|
|
||||||
|
if (!name.isEmpty()) {
|
||||||
|
obj[configKey::name] = name;
|
||||||
|
}
|
||||||
|
if (nameOverriddenByUser) {
|
||||||
|
obj[configKey::nameOverriddenByUser] = true;
|
||||||
|
}
|
||||||
|
if (!description.isEmpty()) {
|
||||||
|
obj[configKey::description] = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj[configKey::configVersion] = configVersion;
|
||||||
|
|
||||||
|
if (!hostName.isEmpty()) {
|
||||||
|
obj[configKey::hostName] = hostName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray containersArray;
|
||||||
|
for (auto it = containers.begin(); it != containers.end(); ++it) {
|
||||||
|
QJsonObject containerObj = it.value().toJson();
|
||||||
|
containersArray.append(containerObj);
|
||||||
|
}
|
||||||
|
if (!containersArray.isEmpty()) {
|
||||||
|
obj[configKey::containers] = containersArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultContainer != DockerContainer::None) {
|
||||||
|
obj[configKey::defaultContainer] = ContainerUtils::containerToString(defaultContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dns1.isEmpty()) {
|
||||||
|
obj[configKey::dns1] = dns1;
|
||||||
|
}
|
||||||
|
if (!dns2.isEmpty()) {
|
||||||
|
obj[configKey::dns2] = dns2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crc > 0) {
|
||||||
|
obj[configKey::crc] = crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject apiConfigObj = apiConfig.toJson();
|
||||||
|
if (!apiConfigObj.isEmpty()) {
|
||||||
|
obj[apiDefs::key::apiConfig] = apiConfigObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject authDataObj = authData.toJson();
|
||||||
|
if (!authDataObj.isEmpty()) {
|
||||||
|
obj[QLatin1String("auth_data")] = authDataObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiV2ServerConfig ApiV2ServerConfig::fromJson(const QJsonObject& json)
|
||||||
|
{
|
||||||
|
ApiV2ServerConfig config;
|
||||||
|
|
||||||
|
config.name = json.value(configKey::name).toString();
|
||||||
|
config.nameOverriddenByUser = json.value(configKey::nameOverriddenByUser).toBool(false);
|
||||||
|
config.description = json.value(configKey::description).toString();
|
||||||
|
config.configVersion = json.value(configKey::configVersion).toInt(2);
|
||||||
|
config.hostName = json.value(configKey::hostName).toString();
|
||||||
|
|
||||||
|
QJsonArray containersArray = json.value(configKey::containers).toArray();
|
||||||
|
for (const QJsonValue& val : containersArray) {
|
||||||
|
QJsonObject containerObj = val.toObject();
|
||||||
|
ContainerConfig containerConfig = ContainerConfig::fromJson(containerObj);
|
||||||
|
|
||||||
|
QString containerStr = containerObj.value(configKey::container).toString();
|
||||||
|
DockerContainer container = ContainerUtils::containerFromString(containerStr);
|
||||||
|
|
||||||
|
config.containers.insert(container, containerConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString defaultContainerStr = json.value(configKey::defaultContainer).toString();
|
||||||
|
config.defaultContainer = ContainerUtils::containerFromString(defaultContainerStr);
|
||||||
|
|
||||||
|
config.dns1 = json.value(configKey::dns1).toString();
|
||||||
|
config.dns2 = json.value(configKey::dns2).toString();
|
||||||
|
|
||||||
|
config.crc = json.value(configKey::crc).toInt(0);
|
||||||
|
|
||||||
|
QJsonObject apiConfigObj = json.value(apiDefs::key::apiConfig).toObject();
|
||||||
|
if (!apiConfigObj.isEmpty()) {
|
||||||
|
config.apiConfig = ApiConfig::fromJson(apiConfigObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject authDataObj = json.value(QLatin1String("auth_data")).toObject();
|
||||||
|
if (!authDataObj.isEmpty()) {
|
||||||
|
config.authData = AuthData::fromJson(authDataObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace amnezia
|
||||||
|
|
||||||
52
client/core/models/api/apiV2ServerConfig.h
Normal file
52
client/core/models/api/apiV2ServerConfig.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#ifndef APIV2SERVERCONFIG_H
|
||||||
|
#define APIV2SERVERCONFIG_H
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
#include "core/utils/containerEnum.h"
|
||||||
|
#include "core/utils/containers/containerUtils.h"
|
||||||
|
#include "core/utils/protocolEnum.h"
|
||||||
|
#include "core/models/containerConfig.h"
|
||||||
|
#include "core/models/api/apiConfig.h"
|
||||||
|
#include "core/models/api/authData.h"
|
||||||
|
#include "core/utils/api/apiEnums.h"
|
||||||
|
#include "core/utils/constants/apiKeys.h"
|
||||||
|
#include "core/utils/constants/apiConstants.h"
|
||||||
|
|
||||||
|
namespace amnezia
|
||||||
|
{
|
||||||
|
|
||||||
|
using namespace ContainerEnumNS;
|
||||||
|
|
||||||
|
struct ApiV2ServerConfig {
|
||||||
|
QString description;
|
||||||
|
QString hostName;
|
||||||
|
QMap<DockerContainer, ContainerConfig> containers;
|
||||||
|
DockerContainer defaultContainer;
|
||||||
|
QString dns1;
|
||||||
|
QString dns2;
|
||||||
|
|
||||||
|
QString name;
|
||||||
|
bool nameOverriddenByUser = false;
|
||||||
|
int crc;
|
||||||
|
int configVersion;
|
||||||
|
ApiConfig apiConfig;
|
||||||
|
AuthData authData;
|
||||||
|
|
||||||
|
QString vpnKey() const;
|
||||||
|
QString serviceType() const;
|
||||||
|
QString serviceProtocol() const;
|
||||||
|
bool isPremium() const;
|
||||||
|
bool isFree() const;
|
||||||
|
bool isExternalPremium() const;
|
||||||
|
bool hasContainers() const;
|
||||||
|
ContainerConfig containerConfig(DockerContainer container) const;
|
||||||
|
QJsonObject toJson() const;
|
||||||
|
static ApiV2ServerConfig fromJson(const QJsonObject& json);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace amnezia
|
||||||
|
|
||||||
|
#endif // APIV2SERVERCONFIG_H
|
||||||
|
|
||||||
23
client/core/models/api/authData.cpp
Normal file
23
client/core/models/api/authData.cpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#include "authData.h"
|
||||||
|
|
||||||
|
namespace amnezia
|
||||||
|
{
|
||||||
|
|
||||||
|
QJsonObject AuthData::toJson() const
|
||||||
|
{
|
||||||
|
QJsonObject obj;
|
||||||
|
if (!apiKey.isEmpty()) {
|
||||||
|
obj[apiDefs::key::apiKey] = apiKey;
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthData AuthData::fromJson(const QJsonObject& json)
|
||||||
|
{
|
||||||
|
AuthData data;
|
||||||
|
data.apiKey = json.value(apiDefs::key::apiKey).toString();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace amnezia
|
||||||
|
|
||||||
24
client/core/models/api/authData.h
Normal file
24
client/core/models/api/authData.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#ifndef AUTHDATA_H
|
||||||
|
#define AUTHDATA_H
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "core/utils/api/apiEnums.h"
|
||||||
|
#include "core/utils/constants/apiKeys.h"
|
||||||
|
#include "core/utils/constants/apiConstants.h"
|
||||||
|
|
||||||
|
namespace amnezia
|
||||||
|
{
|
||||||
|
|
||||||
|
struct AuthData {
|
||||||
|
QString apiKey;
|
||||||
|
|
||||||
|
QJsonObject toJson() const;
|
||||||
|
static AuthData fromJson(const QJsonObject& json);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace amnezia
|
||||||
|
|
||||||
|
#endif // AUTHDATA_H
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user