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:
vkamn
2026-04-30 14:53:03 +08:00
committed by GitHub
parent 2edd7de413
commit 847bb6923b
469 changed files with 25992 additions and 17154 deletions
@@ -1,35 +0,0 @@
#ifndef ALLOWEDDNSCONTROLLER_H
#define ALLOWEDDNSCONTROLLER_H
#include <QObject>
#include "settings.h"
#include "ui/models/allowed_dns_model.h"
class AllowedDnsController : public QObject
{
Q_OBJECT
public:
explicit AllowedDnsController(const std::shared_ptr<Settings> &settings,
const QSharedPointer<AllowedDnsModel> &allowedDnsModel,
QObject *parent = nullptr);
public slots:
void addDns(QString ip);
void removeDns(int index);
void importDns(const QString &fileName, bool replaceExisting);
void exportDns(const QString &fileName);
signals:
void errorOccurred(const QString &errorMessage);
void finished(const QString &message);
void saveFile(const QString &fileName, const QString &data);
private:
std::shared_ptr<Settings> m_settings;
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
};
#endif // ALLOWEDDNSCONTROLLER_H
@@ -1,4 +1,4 @@
#include "allowedDnsController.h"
#include "allowedDnsUiController.h"
#include <QFile>
#include <QStandardPaths>
@@ -7,17 +7,22 @@
#include <QJsonObject>
#include "systemController.h"
#include "core/networkUtilities.h"
#include "core/defs.h"
#include "core/utils/networkUtilities.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
AllowedDnsController::AllowedDnsController(const std::shared_ptr<Settings> &settings,
const QSharedPointer<AllowedDnsModel> &allowedDnsModel,
QObject *parent)
: QObject(parent), m_settings(settings), m_allowedDnsModel(allowedDnsModel)
AllowedDnsUiController::AllowedDnsUiController(AllowedDnsController* allowedDnsController,
AllowedDnsModel* allowedDnsModel,
QObject *parent)
: QObject(parent),
m_allowedDnsController(allowedDnsController),
m_allowedDnsModel(allowedDnsModel)
{
m_allowedDnsModel->updateModel(m_allowedDnsController->getCurrentDnsServers());
}
void AllowedDnsController::addDns(QString ip)
void AllowedDnsUiController::addDns(QString ip)
{
if (ip.isEmpty()) {
return;
@@ -28,23 +33,22 @@ void AllowedDnsController::addDns(QString ip)
return;
}
if (m_allowedDnsModel->addDns(ip)) {
if (m_allowedDnsController->addDns(ip)) {
emit finished(tr("New DNS server added: %1").arg(ip));
} else {
emit errorOccurred(tr("DNS server already exists: %1").arg(ip));
}
}
void AllowedDnsController::removeDns(int index)
void AllowedDnsUiController::removeDns(int index)
{
auto modelIndex = m_allowedDnsModel->index(index);
auto ip = m_allowedDnsModel->data(modelIndex, AllowedDnsModel::Roles::IpRole).toString();
m_allowedDnsModel->removeDns(modelIndex);
m_allowedDnsController->removeDns(index);
emit finished(tr("DNS server removed: %1").arg(ip));
}
void AllowedDnsController::importDns(const QString &fileName, bool replaceExisting)
void AllowedDnsUiController::importDns(const QString &fileName, bool replaceExisting)
{
QByteArray jsonData;
if (!SystemController::readFile(fileName, jsonData)) {
@@ -77,14 +81,13 @@ void AllowedDnsController::importDns(const QString &fileName, bool replaceExisti
dnsServers.append(ip);
}
m_allowedDnsModel->addDnsList(dnsServers, replaceExisting);
m_allowedDnsController->addDnsList(dnsServers, replaceExisting);
emit finished(tr("Import completed"));
}
void AllowedDnsController::exportDns(const QString &fileName)
void AllowedDnsUiController::exportDns(const QString &fileName)
{
auto dnsServers = m_allowedDnsModel->getCurrentDnsServers();
auto dnsServers = m_allowedDnsController->getCurrentDnsServers();
QJsonArray jsonArray;
@@ -98,4 +101,9 @@ void AllowedDnsController::exportDns(const QString &fileName)
SystemController::saveFile(fileName, jsonData);
emit finished(tr("Export completed"));
}
void AllowedDnsUiController::updateModel()
{
m_allowedDnsModel->updateModel(m_allowedDnsController->getCurrentDnsServers());
}
@@ -0,0 +1,37 @@
#ifndef ALLOWEDDNSUICONTROLLER_H
#define ALLOWEDDNSUICONTROLLER_H
#include <QObject>
#include "core/controllers/allowedDnsController.h"
#include "ui/models/allowedDnsModel.h"
class AllowedDnsUiController : public QObject
{
Q_OBJECT
public:
explicit AllowedDnsUiController(AllowedDnsController* allowedDnsController,
AllowedDnsModel* allowedDnsModel,
QObject *parent = nullptr);
public slots:
void addDns(QString ip);
void removeDns(int index);
void importDns(const QString &fileName, bool replaceExisting);
void exportDns(const QString &fileName);
void updateModel();
signals:
void errorOccurred(const QString &errorMessage);
void finished(const QString &message);
void saveFile(const QString &fileName, const QString &data);
private:
AllowedDnsController* m_allowedDnsController;
AllowedDnsModel* m_allowedDnsModel;
};
#endif // ALLOWEDDNSUICONTROLLER_H
File diff suppressed because it is too large Load Diff
@@ -1,82 +0,0 @@
#ifndef APICONFIGSCONTROLLER_H
#define APICONFIGSCONTROLLER_H
#include <QList>
#include <QObject>
#include "configurators/openvpn_configurator.h"
#include "ui/models/api/apiBenefitsModel.h"
#include "ui/models/api/apiServicesModel.h"
#include "ui/models/api/apiSubscriptionPlansModel.h"
#include "ui/models/servers_model.h"
class ApiConfigsController : public QObject
{
Q_OBJECT
public:
ApiConfigsController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ApiServicesModel> &apiServicesModel,
const QSharedPointer<ApiSubscriptionPlansModel> &subscriptionPlansModel,
const QSharedPointer<ApiBenefitsModel> &benefitsModel, const std::shared_ptr<Settings> &settings,
QObject *parent = nullptr);
Q_PROPERTY(QList<QString> qrCodes READ getQrCodes NOTIFY vpnKeyExportReady)
Q_PROPERTY(int qrCodesCount READ getQrCodesCount NOTIFY vpnKeyExportReady)
Q_PROPERTY(QString vpnKey READ getVpnKey NOTIFY vpnKeyExportReady)
public slots:
bool exportNativeConfig(const QString &serverCountryCode, const QString &fileName);
bool revokeNativeConfig(const QString &serverCountryCode);
bool exportVpnKey(const QString &fileName);
void prepareVpnKeyExport();
void copyVpnKeyToClipboard();
bool fillAvailableServices();
bool importService();
bool importPremiumFromAppStore(const QString &storeProductId);
bool restoreServiceFromAppStore();
bool importFreeFromGateway();
bool importTrialFromGateway(const QString &email);
bool updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
bool reloadServiceConfig = false);
bool updateServiceFromTelegram(const int serverIndex);
bool deactivateDevice(const bool isRemoveEvent);
bool deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode);
bool isConfigValid();
void setCurrentProtocol(const QString &protocolName);
bool isVlessProtocol();
signals:
void errorOccurred(ErrorCode errorCode);
void trialEmailError(const QString &message);
void subscriptionExpiredOnServer();
void subscriptionRefreshNeeded();
void installServerFromApiFinished(const QString &message, int preferredDefaultServerIndex = -1);
void changeApiCountryFinished(const QString &message);
void reloadServerFromApiFinished(const QString &message);
void updateServerFromApiFinished();
void vpnKeyExportReady();
private:
QList<QString> getQrCodes();
int getQrCodesCount();
QString getVpnKey();
ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody, bool isTestPurchase = false);
ErrorCode importServiceFromBilling(const QByteArray &responseBody, const bool isTestPurchase, int &duplicateServerIndex);
QList<QString> m_qrCodes;
QString m_vpnKey;
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ApiServicesModel> m_apiServicesModel;
std::shared_ptr<Settings> m_settings;
QSharedPointer<ApiSubscriptionPlansModel> m_subscriptionPlansModel;
QSharedPointer<ApiBenefitsModel> m_benefitsModel;
};
#endif
@@ -1,69 +0,0 @@
#include "apiNewsController.h"
#include "core/api/apiUtils.h"
#include <QJsonDocument>
#include <QJsonObject>
namespace
{
namespace configKey
{
constexpr char userCountryCode[] = "user_country_code";
constexpr char serviceType[] = "service_type";
}
}
ApiNewsController::ApiNewsController(const QSharedPointer<NewsModel> &newsModel, const std::shared_ptr<Settings> &settings,
const QSharedPointer<ServersModel> &serversModel, QObject *parent)
: QObject(parent), m_newsModel(newsModel), m_settings(settings), m_serversModel(serversModel)
{
}
void ApiNewsController::fetchNews(bool showError)
{
if (m_serversModel.isNull()) {
qWarning() << "ServersModel is null, skip fetchNews";
return;
}
const auto stacks = m_serversModel->gatewayStacks();
if (stacks.isEmpty()) {
qDebug() << "No Gateway stacks, skip fetchNews";
return;
}
auto gatewayController = QSharedPointer<GatewayController>::create(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(),
apiDefs::requestTimeoutMsecs, m_settings->isStrictKillSwitchEnabled());
QJsonObject payload;
payload.insert("locale", m_settings->getAppLanguage().name().split("_").first());
const QJsonObject stacksJson = stacks.toJson();
if (stacksJson.contains(configKey::userCountryCode)) {
payload.insert(configKey::userCountryCode, stacksJson.value(configKey::userCountryCode));
}
if (stacksJson.contains(configKey::serviceType)) {
payload.insert(configKey::serviceType, stacksJson.value(configKey::serviceType));
}
auto future = gatewayController->postAsync(QString("%1v1/news"), payload);
future.then(this, [this, showError, gatewayController](QPair<ErrorCode, QByteArray> result) {
auto [errorCode, responseBody] = result;
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode, showError);
return;
}
QJsonDocument doc = QJsonDocument::fromJson(responseBody);
QJsonArray newsArray;
if (doc.isArray()) {
newsArray = doc.array();
} else if (doc.isObject()) {
QJsonObject obj = doc.object();
if (obj.value("news").isArray()) {
newsArray = obj.value("news").toArray();
}
}
m_newsModel->updateModel(newsArray);
emit fetchNewsFinished();
});
}
@@ -1,34 +0,0 @@
#ifndef APINEWSCONTROLLER_H
#define APINEWSCONTROLLER_H
#include <QJsonArray>
#include <QObject>
#include <QSharedPointer>
#include <memory>
#include "core/api/apiDefs.h"
#include "core/controllers/gatewayController.h"
#include "settings.h"
#include "ui/models/newsModel.h"
#include "ui/models/servers_model.h"
class ApiNewsController : public QObject
{
Q_OBJECT
public:
explicit ApiNewsController(const QSharedPointer<NewsModel> &newsModel, const std::shared_ptr<Settings> &settings,
const QSharedPointer<ServersModel> &serversModel, QObject *parent = nullptr);
Q_INVOKABLE void fetchNews(bool showError);
signals:
void errorOccurred(ErrorCode errorCode, bool showError);
void fetchNewsFinished();
private:
QSharedPointer<NewsModel> m_newsModel;
std::shared_ptr<Settings> m_settings;
QSharedPointer<ServersModel> m_serversModel;
};
#endif // APINEWSCONTROLLER_H
@@ -0,0 +1,28 @@
#include "apiNewsUiController.h"
ApiNewsUiController::ApiNewsUiController(NewsModel* newsModel,
NewsController* newsController,
QObject *parent)
: QObject(parent), m_newsModel(newsModel), m_newsController(newsController)
{
}
void ApiNewsUiController::fetchNews(bool showError)
{
if (!m_newsController) {
qWarning() << "NewsController is null, skip fetchNews";
return;
}
auto future = m_newsController->fetchNews();
future.then(this, [this, showError](QPair<ErrorCode, QJsonArray> result) {
auto [errorCode, newsArray] = result;
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode, showError);
return;
}
m_newsModel->updateModel(newsArray);
emit fetchNewsFinished();
});
}
@@ -0,0 +1,32 @@
#ifndef APINEWSUICONTROLLER_H
#define APINEWSUICONTROLLER_H
#include <QJsonArray>
#include <QObject>
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/controllers/api/newsController.h"
#include "ui/models/newsModel.h"
class ApiNewsUiController : public QObject
{
Q_OBJECT
public:
explicit ApiNewsUiController(NewsModel* newsModel,
NewsController* newsController,
QObject *parent = nullptr);
Q_INVOKABLE void fetchNews(bool showError);
signals:
void errorOccurred(ErrorCode errorCode, bool showError);
void fetchNewsFinished();
private:
NewsModel* m_newsModel;
NewsController* m_newsController;
};
#endif // APINEWSUICONTROLLER_H
@@ -1,148 +0,0 @@
#include "apiSettingsController.h"
#include <QEventLoop>
#include <QJsonDocument>
#include <QTimer>
#include "core/api/apiUtils.h"
#include "core/controllers/gatewayController.h"
#include "platforms/ios/ios_controller.h"
#include "version.h"
namespace
{
namespace configKey
{
constexpr char userCountryCode[] = "user_country_code";
constexpr char serverCountryCode[] = "server_country_code";
constexpr char serviceType[] = "service_type";
constexpr char serviceInfo[] = "service_info";
constexpr char apiConfig[] = "api_config";
constexpr char authData[] = "auth_data";
}
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
QString getSubscriptionStatusForRenewal(const QSharedPointer<ApiAccountInfoModel> &accountInfoModel)
{
if (!accountInfoModel.isNull() && accountInfoModel->data(QStringLiteral("isSubscriptionExpired")).toBool()) {
return QStringLiteral("expired");
}
if (!accountInfoModel.isNull() && accountInfoModel->data(QStringLiteral("isSubscriptionExpiringSoon")).toBool()) {
return QStringLiteral("expire_soon");
}
return QStringLiteral("active");
}
}
ApiSettingsController::ApiSettingsController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ApiAccountInfoModel> &apiAccountInfoModel,
const QSharedPointer<ApiCountryModel> &apiCountryModel,
const QSharedPointer<ApiDevicesModel> &apiDevicesModel,
const std::shared_ptr<Settings> &settings, QObject *parent)
: QObject(parent),
m_serversModel(serversModel),
m_apiAccountInfoModel(apiAccountInfoModel),
m_apiCountryModel(apiCountryModel),
m_apiDevicesModel(apiDevicesModel),
m_settings(settings)
{
}
ApiSettingsController::~ApiSettingsController()
{
}
bool ApiSettingsController::getAccountInfo(bool reload)
{
if (reload) {
QEventLoop wait;
QTimer::singleShot(1000, &wait, &QEventLoop::quit);
wait.exec(QEventLoop::ExcludeUserInputEvents);
}
auto processedIndex = m_serversModel->getProcessedServerIndex();
auto serverConfig = m_serversModel->getServerConfig(processedIndex);
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
auto authData = serverConfig.value(configKey::authData).toObject();
bool isTestPurchase = apiConfig.value(apiDefs::key::isTestPurchase).toBool(false);
GatewayController gatewayController(m_settings->getGatewayEndpoint(isTestPurchase), m_settings->isDevGatewayEnv(isTestPurchase),
requestTimeoutMsecs, m_settings->isStrictKillSwitchEnabled());
QJsonObject apiPayload;
apiPayload[configKey::userCountryCode] = apiConfig.value(configKey::userCountryCode).toString();
apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString();
apiPayload[configKey::authData] = authData;
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
apiPayload[apiDefs::key::appLanguage] = m_settings->getAppLanguage().name().split("_").first();
QByteArray responseBody;
ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
}
QJsonObject accountInfo = QJsonDocument::fromJson(responseBody).object();
m_apiAccountInfoModel->updateModel(accountInfo, serverConfig);
if (reload) {
updateApiCountryModel();
updateApiDevicesModel();
}
return true;
}
void ApiSettingsController::getRenewalLink()
{
auto processedIndex = m_serversModel->getProcessedServerIndex();
auto serverConfig = m_serversModel->getServerConfig(processedIndex);
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
auto authData = serverConfig.value(configKey::authData).toObject();
bool isTestPurchase = apiConfig.value(apiDefs::key::isTestPurchase).toBool(false);
auto gatewayController = QSharedPointer<GatewayController>::create(m_settings->getGatewayEndpoint(isTestPurchase),
m_settings->isDevGatewayEnv(isTestPurchase),
requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
QJsonObject apiPayload;
apiPayload[configKey::userCountryCode] = apiConfig.value(configKey::userCountryCode).toString();
apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString();
apiPayload[configKey::authData] = authData;
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
apiPayload[apiDefs::key::appLanguage] = m_settings->getAppLanguage().name().split("_").first();
apiPayload[apiDefs::key::subscriptionStatus] = getSubscriptionStatusForRenewal(m_apiAccountInfoModel);
auto future = gatewayController->postAsync(QString("%1v1/renewal_link"), apiPayload);
future.then(this, [this, gatewayController](QPair<ErrorCode, QByteArray> result) {
auto [errorCode, responseBody] = result;
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return;
}
QJsonObject responseJson = QJsonDocument::fromJson(responseBody).object();
QString url = responseJson.value("renewal_url").toString();
if (!url.isEmpty()) {
emit renewalLinkReceived(url);
}
});
}
void ApiSettingsController::updateApiCountryModel()
{
m_apiCountryModel->updateModel(m_apiAccountInfoModel->getAvailableCountries(), "");
m_apiCountryModel->updateIssuedConfigsInfo(m_apiAccountInfoModel->getIssuedConfigsInfo());
}
void ApiSettingsController::updateApiDevicesModel()
{
m_apiDevicesModel->updateModel(m_apiAccountInfoModel->getIssuedConfigsInfo());
}
@@ -1,39 +0,0 @@
#ifndef APISETTINGSCONTROLLER_H
#define APISETTINGSCONTROLLER_H
#include <QObject>
#include "ui/models/api/apiAccountInfoModel.h"
#include "ui/models/api/apiCountryModel.h"
#include "ui/models/api/apiDevicesModel.h"
#include "ui/models/servers_model.h"
class ApiSettingsController : public QObject
{
Q_OBJECT
public:
ApiSettingsController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ApiAccountInfoModel> &apiAccountInfoModel,
const QSharedPointer<ApiCountryModel> &apiCountryModel, const QSharedPointer<ApiDevicesModel> &apiDevicesModel,
const std::shared_ptr<Settings> &settings, QObject *parent = nullptr);
~ApiSettingsController();
public slots:
bool getAccountInfo(bool reload);
void updateApiCountryModel();
void updateApiDevicesModel();
void getRenewalLink();
signals:
void errorOccurred(ErrorCode errorCode);
void renewalLinkReceived(const QString &url);
private:
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel;
QSharedPointer<ApiCountryModel> m_apiCountryModel;
QSharedPointer<ApiDevicesModel> m_apiDevicesModel;
std::shared_ptr<Settings> m_settings;
};
#endif // APISETTINGSCONTROLLER_H
@@ -0,0 +1,68 @@
#include "servicesCatalogUiController.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
ServicesCatalogUiController::ServicesCatalogUiController(ServicesCatalogController* servicesCatalogController,
ApiServicesModel* apiServicesModel,
QObject *parent)
: QObject(parent),
m_servicesCatalogController(servicesCatalogController),
m_apiServicesModel(apiServicesModel)
{
}
bool ServicesCatalogUiController::fillAvailableServices()
{
QJsonObject servicesData;
ErrorCode errorCode = m_servicesCatalogController->fillAvailableServices(servicesData);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
}
m_apiServicesModel->updateModel(servicesData);
return true;
}
QJsonObject ServicesCatalogUiController::getSelectedServiceInfo()
{
return m_apiServicesModel->getSelectedServiceInfo();
}
QString ServicesCatalogUiController::getSelectedServiceType()
{
return m_apiServicesModel->getSelectedServiceType();
}
QString ServicesCatalogUiController::getSelectedServiceProtocol()
{
return m_apiServicesModel->getSelectedServiceProtocol();
}
QString ServicesCatalogUiController::getSelectedServiceName()
{
return m_apiServicesModel->getSelectedServiceName();
}
QJsonArray ServicesCatalogUiController::getSelectedServiceCountries()
{
return m_apiServicesModel->getSelectedServiceCountries();
}
QString ServicesCatalogUiController::getCountryCode()
{
return m_apiServicesModel->getCountryCode();
}
QString ServicesCatalogUiController::getStoreEndpoint()
{
return m_apiServicesModel->getStoreEndpoint();
}
QVariant ServicesCatalogUiController::getSelectedServiceData(const QString &roleString)
{
return m_apiServicesModel->getSelectedServiceData(roleString);
}
@@ -0,0 +1,44 @@
#ifndef SERVICESCATALOGUICONTROLLER_H
#define SERVICESCATALOGUICONTROLLER_H
#include <QObject>
#include <QJsonArray>
#include <QJsonObject>
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/controllers/api/servicesCatalogController.h"
#include "ui/models/api/apiServicesModel.h"
class ServicesCatalogUiController : public QObject
{
Q_OBJECT
public:
explicit ServicesCatalogUiController(ServicesCatalogController* servicesCatalogController,
ApiServicesModel* apiServicesModel,
QObject *parent = nullptr);
public slots:
bool fillAvailableServices();
QJsonObject getSelectedServiceInfo();
QString getSelectedServiceType();
QString getSelectedServiceProtocol();
QString getSelectedServiceName();
QJsonArray getSelectedServiceCountries();
QString getCountryCode();
QString getStoreEndpoint();
QVariant getSelectedServiceData(const QString &roleString);
signals:
void errorOccurred(ErrorCode errorCode);
private:
ServicesCatalogController* m_servicesCatalogController;
ApiServicesModel* m_apiServicesModel;
};
#endif // SERVICESCATALOGUICONTROLLER_H
@@ -0,0 +1,483 @@
#include "subscriptionUiController.h"
#include "amneziaApplication.h"
#include "core/configurators/wireguardConfigurator.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/qrCodeUtils.h"
#include "ui/controllers/systemController.h"
#include "version.h"
#include "core/models/serverConfig.h"
#include <QClipboard>
#include <QDebug>
#include <QSet>
#include <QEventLoop>
#include <QFutureWatcher>
#include <QTimer>
namespace
{
namespace configKey
{
constexpr char awg[] = "awg";
constexpr char vless[] = "vless";
constexpr char apiEndpoint[] = "api_endpoint";
constexpr char accessToken[] = "api_key";
constexpr char certificate[] = "certificate";
constexpr char publicKey[] = "public_key";
constexpr char protocol[] = "protocol";
constexpr char uuid[] = "installation_uuid";
constexpr char osVersion[] = "os_version";
constexpr char appVersion[] = "app_version";
constexpr char userCountryCode[] = "user_country_code";
constexpr char serverCountryCode[] = "server_country_code";
constexpr char serviceType[] = "service_type";
constexpr char serviceInfo[] = "service_info";
constexpr char serviceProtocol[] = "service_protocol";
constexpr char apiPayload[] = "api_payload";
constexpr char keyPayload[] = "key_payload";
constexpr char apiConfig[] = "api_config";
constexpr char authData[] = "auth_data";
constexpr char config[] = "config";
constexpr char subscription[] = "subscription";
constexpr char endDate[] = "end_date";
constexpr char isConnectEvent[] = "is_connect_event";
}
}
SubscriptionUiController::SubscriptionUiController(ServersController* serversController,
ApiServicesModel* apiServicesModel,
ServicesCatalogController* servicesCatalogController,
SubscriptionController* subscriptionController,
ApiSubscriptionPlansModel* apiSubscriptionPlansModel,
ApiBenefitsModel* apiBenefitsModel,
ApiAccountInfoModel* apiAccountInfoModel,
ApiCountryModel* apiCountryModel,
ApiDevicesModel* apiDevicesModel,
SettingsController* settingsController,
QObject *parent)
: QObject(parent), m_serversController(serversController), m_apiServicesModel(apiServicesModel), m_servicesCatalogController(servicesCatalogController), m_subscriptionController(subscriptionController), m_apiSubscriptionPlansModel(apiSubscriptionPlansModel), m_apiBenefitsModel(apiBenefitsModel), m_apiAccountInfoModel(apiAccountInfoModel), m_apiCountryModel(apiCountryModel), m_apiDevicesModel(apiDevicesModel), m_settingsController(settingsController)
{
connect(m_apiServicesModel, &ApiServicesModel::serviceSelectionChanged, this, [this]() {
ApiServicesModel::ApiServicesData selectedServiceData = m_apiServicesModel->selectedServiceData();
m_apiSubscriptionPlansModel->updateModel(selectedServiceData.subscriptionPlansJson);
m_apiBenefitsModel->updateModel(selectedServiceData.benefits);
});
}
bool SubscriptionUiController::exportVpnKey(int serverIndex, const QString &fileName)
{
if (fileName.isEmpty()) {
emit errorOccurred(ErrorCode::PermissionsError);
return false;
}
prepareVpnKeyExport(serverIndex);
if (m_vpnKey.isEmpty()) {
emit errorOccurred(ErrorCode::ApiConfigEmptyError);
return false;
}
SystemController::saveFile(fileName, m_vpnKey);
return true;
}
bool SubscriptionUiController::exportNativeConfig(int serverIndex, const QString &serverCountryCode, const QString &fileName)
{
if (fileName.isEmpty()) {
emit errorOccurred(ErrorCode::PermissionsError);
return false;
}
QString nativeConfig;
ErrorCode errorCode = m_subscriptionController->exportNativeConfig(serverIndex, serverCountryCode, nativeConfig);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
}
SystemController::saveFile(fileName, nativeConfig);
return true;
}
bool SubscriptionUiController::revokeNativeConfig(int serverIndex, const QString &serverCountryCode)
{
ErrorCode errorCode = m_subscriptionController->revokeNativeConfig(serverIndex, serverCountryCode);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
}
return true;
}
void SubscriptionUiController::prepareVpnKeyExport(int serverIndex)
{
QString vpnKey;
ErrorCode errorCode = m_subscriptionController->prepareVpnKeyExport(serverIndex, vpnKey);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return;
}
m_vpnKey = vpnKey;
QString vpnKeyForQr = vpnKey;
vpnKeyForQr.replace("vpn://", "");
m_qrCodes = qrCodeUtils::generateQrCodeImageSeries(vpnKeyForQr.toUtf8());
emit vpnKeyExportReady();
}
void SubscriptionUiController::copyVpnKeyToClipboard()
{
auto clipboard = amnApp->getClipboard();
clipboard->setText(m_vpnKey);
}
bool SubscriptionUiController::fillAvailableServices()
{
QJsonObject servicesData;
ErrorCode errorCode = m_servicesCatalogController->fillAvailableServices(servicesData);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
}
m_apiServicesModel->updateModel(servicesData);
if (m_apiServicesModel->rowCount() > 0) {
m_apiServicesModel->setServiceIndex(0);
}
return true;
}
bool SubscriptionUiController::importPremiumFromAppStore(const QString &storeProductId)
{
#if defined(Q_OS_IOS) || defined(MACOS_NE)
QString productId = storeProductId.trimmed();
if (productId.isEmpty()) {
productId = QStringLiteral("amnezia_premium_6_month");
}
ServerConfig serverConfig;
int duplicateServerIndex = -1;
ErrorCode errorCode = m_subscriptionController->processAppStorePurchase(
m_apiServicesModel->getCountryCode(),
m_apiServicesModel->getSelectedServiceType(),
m_apiServicesModel->getSelectedServiceProtocol(),
productId,
serverConfig,
&duplicateServerIndex);
if (errorCode != ErrorCode::NoError) {
if (errorCode == ErrorCode::ApiConfigAlreadyAdded) {
emit installServerFromApiFinished(tr("This subscription has already been added"), duplicateServerIndex);
return true;
}
emit errorOccurred(errorCode);
return false;
}
emit installServerFromApiFinished(tr("%1 has been added to the app").arg(m_apiServicesModel->getSelectedServiceName()));
#endif
return true;
}
bool SubscriptionUiController::restoreServiceFromAppStore()
{
#if defined(Q_OS_IOS) || defined(MACOS_NE)
const QString premiumServiceType = QStringLiteral("amnezia-premium");
if (!fillAvailableServices()) {
qWarning().noquote() << "[IAP] Unable to fetch services list before restore";
emit errorOccurred(ErrorCode::ApiServicesMissingError);
return false;
}
if (m_apiServicesModel->rowCount() <= 0) {
emit errorOccurred(ErrorCode::ApiServicesMissingError);
return false;
}
// Ensure we have a valid premium selection for gateway requests
bool premiumSelected = false;
for (int i = 0; i < m_apiServicesModel->rowCount(); ++i) {
m_apiServicesModel->setServiceIndex(i);
if (m_apiServicesModel->getSelectedServiceType() == premiumServiceType) {
premiumSelected = true;
break;
}
}
if (!premiumSelected) {
emit errorOccurred(ErrorCode::ApiServicesMissingError);
return false;
}
SubscriptionController::AppStoreRestoreResult result = m_subscriptionController->processAppStoreRestore(
m_apiServicesModel->getCountryCode(),
m_apiServicesModel->getSelectedServiceType(),
m_apiServicesModel->getSelectedServiceProtocol());
if (!result.hasInstalledConfig) {
if (result.duplicateConfigAlreadyPresent) {
emit installServerFromApiFinished(tr("This subscription has already been added"), result.duplicateServerIndex);
return true;
}
emit errorOccurred(result.errorCode);
return false;
}
emit installServerFromApiFinished(tr("Subscription restored successfully."));
if (result.duplicateCount > 0) {
qInfo().noquote() << "[IAP] Skipped" << result.duplicateCount
<< "duplicate restored transactions for original transaction IDs already processed";
}
#endif
return true;
}
bool SubscriptionUiController::importFreeFromGateway()
{
QString userCountryCode = m_apiServicesModel->getCountryCode();
QString serviceType = m_apiServicesModel->getSelectedServiceType();
QString serviceProtocol = m_apiServicesModel->getSelectedServiceProtocol();
if (m_serversController->isServerFromApiAlreadyExists(userCountryCode, serviceType, serviceProtocol)) {
emit errorOccurred(ErrorCode::ApiConfigAlreadyAdded);
return false;
}
SubscriptionController::ProtocolData protocolData = m_subscriptionController->generateProtocolData(serviceProtocol);
ServerConfig serverConfig;
ErrorCode errorCode = m_subscriptionController->importServiceFromGateway(userCountryCode, serviceType,
serviceProtocol, protocolData,
serverConfig);
if (errorCode == ErrorCode::NoError) {
emit installServerFromApiFinished(tr("%1 installed successfully.").arg(m_apiServicesModel->getSelectedServiceName()));
return true;
} else {
emit errorOccurred(errorCode);
return false;
}
}
bool SubscriptionUiController::importTrialFromGateway(const QString &email)
{
emit trialEmailError(QString());
ServerConfig serverConfig;
ErrorCode errorCode = m_subscriptionController->importTrialFromGateway(m_apiServicesModel->getCountryCode(),
m_apiServicesModel->getSelectedServiceType(),
m_apiServicesModel->getSelectedServiceProtocol(),
email,
serverConfig);
if (errorCode != ErrorCode::NoError) {
if (errorCode == ErrorCode::ApiTrialAlreadyUsedError) {
emit trialEmailError(
tr("This email address has already been used to activate a trial. If you like the service, you can upgrade to Premium"));
} else {
emit errorOccurred(errorCode);
}
return false;
}
emit installServerFromApiFinished(tr("%1 installed successfully.").arg(m_apiServicesModel->getSelectedServiceName()));
return true;
}
bool SubscriptionUiController::updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
bool reloadServiceConfig)
{
bool isConnectEvent = newCountryCode.isEmpty() && newCountryName.isEmpty() && !reloadServiceConfig;
bool wasSubscriptionExpired = false;
ServerConfig oldServerConfig = m_serversController->getServerConfig(serverIndex);
if (oldServerConfig.isApiV2()) {
const ApiV2ServerConfig *oldApiV2 = oldServerConfig.as<ApiV2ServerConfig>();
if (oldApiV2) {
wasSubscriptionExpired = oldApiV2->apiConfig.isSubscriptionExpired();
}
}
ErrorCode errorCode = m_subscriptionController->updateServiceFromGateway(serverIndex, newCountryCode, isConnectEvent);
if (errorCode == ErrorCode::NoError) {
if (wasSubscriptionExpired) {
emit subscriptionRefreshNeeded();
}
if (reloadServiceConfig) {
emit reloadServerFromApiFinished(tr("API config reloaded"));
} else if (newCountryName.isEmpty()) {
emit updateServerFromApiFinished();
} else {
emit changeApiCountryFinished(tr("Successfully changed the country of connection to %1").arg(newCountryName));
}
return true;
} else {
if (errorCode == ErrorCode::ApiSubscriptionExpiredError) {
emit subscriptionExpiredOnServer();
}
emit errorOccurred(errorCode);
return false;
}
}
bool SubscriptionUiController::updateServiceFromTelegram(const int serverIndex)
{
#ifdef Q_OS_IOS
IosController::Instance()->requestInetAccess();
QThread::msleep(10);
#endif
ErrorCode errorCode = m_subscriptionController->updateServiceFromTelegram(serverIndex);
if (errorCode == ErrorCode::NoError) {
emit updateServerFromApiFinished();
return true;
} else {
emit errorOccurred(errorCode);
return false;
}
}
bool SubscriptionUiController::deactivateDevice(int serverIndex, const bool isRemoveEvent)
{
ErrorCode errorCode = m_subscriptionController->deactivateDevice(serverIndex, isRemoveEvent);
if (errorCode != ErrorCode::NoError) {
if (errorCode == ErrorCode::ApiSubscriptionExpiredError && isRemoveEvent) {
return true;
}
emit errorOccurred(errorCode);
return false;
}
return true;
}
bool SubscriptionUiController::deactivateExternalDevice(int serverIndex, const QString &uuid, const QString &serverCountryCode)
{
ErrorCode errorCode = m_subscriptionController->deactivateExternalDevice(serverIndex, uuid, serverCountryCode);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
}
return true;
}
void SubscriptionUiController::validateConfig()
{
int serverIndex = m_serversController->getDefaultServerIndex();
bool hasInstalledContainers = m_serversController->hasInstalledContainers(serverIndex);
ErrorCode errorCode = m_subscriptionController->validateAndUpdateConfig(serverIndex, hasInstalledContainers);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
emit configValidated(false);
return;
}
emit configValidated(true);
}
void SubscriptionUiController::setCurrentProtocol(int serverIndex, const QString &protocolName)
{
m_subscriptionController->setCurrentProtocol(serverIndex, protocolName);
}
bool SubscriptionUiController::isVlessProtocol(int serverIndex)
{
return m_subscriptionController->isVlessProtocol(serverIndex);
}
void SubscriptionUiController::removeApiConfig(int serverIndex)
{
m_subscriptionController->removeApiConfig(serverIndex);
emit apiConfigRemoved(tr("Api config removed"));
}
QList<QString> SubscriptionUiController::getQrCodes()
{
return m_qrCodes;
}
int SubscriptionUiController::getQrCodesCount()
{
return static_cast<int>(m_qrCodes.size());
}
QString SubscriptionUiController::getVpnKey()
{
return m_vpnKey;
}
bool SubscriptionUiController::getAccountInfo(int serverIndex, bool reload)
{
if (reload) {
QEventLoop wait;
QTimer::singleShot(1000, &wait, &QEventLoop::quit);
wait.exec(QEventLoop::ExcludeUserInputEvents);
}
QJsonObject accountInfo;
ErrorCode errorCode = m_subscriptionController->getAccountInfo(serverIndex, accountInfo);
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
}
ServerConfig serverConfig = m_serversController->getServerConfig(serverIndex);
QJsonObject serverConfigJson = serverConfig.toJson();
m_apiAccountInfoModel->updateModel(accountInfo, serverConfigJson);
if (reload) {
updateApiCountryModel();
updateApiDevicesModel();
}
return true;
}
void SubscriptionUiController::updateApiCountryModel()
{
m_apiCountryModel->updateModel(m_apiAccountInfoModel->getAvailableCountries(), "");
m_apiCountryModel->updateIssuedConfigsInfo(m_apiAccountInfoModel->getIssuedConfigsInfo());
}
void SubscriptionUiController::updateApiDevicesModel()
{
m_apiDevicesModel->updateModel(m_apiAccountInfoModel->getIssuedConfigsInfo(), m_settingsController->getInstallationUuid(false));
}
void SubscriptionUiController::getRenewalLink(int serverIndex)
{
if (serverIndex < 0) {
emit errorOccurred(ErrorCode::InternalError);
return;
}
auto *watcher = new QFutureWatcher<QPair<ErrorCode, QString>>(this);
connect(watcher, &QFutureWatcher<QPair<ErrorCode, QString>>::finished, this, [this, watcher]() {
const auto [errorCode, url] = watcher->result();
watcher->deleteLater();
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return;
}
emit renewalLinkReceived(url);
});
watcher->setFuture(m_subscriptionController->getRenewalLink(serverIndex));
}
@@ -0,0 +1,103 @@
#ifndef SUBSCRIPTIONUICONTROLLER_H
#define SUBSCRIPTIONUICONTROLLER_H
#include <QObject>
#include "core/controllers/serversController.h"
#include "core/controllers/settingsController.h"
#include "core/controllers/api/servicesCatalogController.h"
#include "core/controllers/api/subscriptionController.h"
#include "ui/models/api/apiSubscriptionPlansModel.h"
#include "ui/models/api/apiBenefitsModel.h"
#include "ui/models/api/apiServicesModel.h"
#include "ui/models/api/apiAccountInfoModel.h"
#include "ui/models/api/apiCountryModel.h"
#include "ui/models/api/apiDevicesModel.h"
class SubscriptionUiController : public QObject
{
Q_OBJECT
public:
SubscriptionUiController(ServersController* serversController,
ApiServicesModel* apiServicesModel,
ServicesCatalogController* servicesCatalogController,
SubscriptionController* subscriptionController,
ApiSubscriptionPlansModel* apiSubscriptionPlansModel,
ApiBenefitsModel* apiBenefitsModel,
ApiAccountInfoModel* apiAccountInfoModel,
ApiCountryModel* apiCountryModel,
ApiDevicesModel* apiDevicesModel,
SettingsController* settingsController,
QObject *parent = nullptr);
Q_PROPERTY(QList<QString> qrCodes READ getQrCodes NOTIFY vpnKeyExportReady)
Q_PROPERTY(int qrCodesCount READ getQrCodesCount NOTIFY vpnKeyExportReady)
Q_PROPERTY(QString vpnKey READ getVpnKey NOTIFY vpnKeyExportReady)
public slots:
bool exportNativeConfig(int serverIndex, const QString &serverCountryCode, const QString &fileName);
bool revokeNativeConfig(int serverIndex, const QString &serverCountryCode);
bool exportVpnKey(int serverIndex, const QString &fileName);
void prepareVpnKeyExport(int serverIndex);
void copyVpnKeyToClipboard();
bool fillAvailableServices();
bool importPremiumFromAppStore(const QString &storeProductId);
bool importFreeFromGateway();
bool restoreServiceFromAppStore();
bool importTrialFromGateway(const QString &email);
bool updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
bool reloadServiceConfig = false);
bool updateServiceFromTelegram(const int serverIndex);
bool deactivateDevice(int serverIndex, const bool isRemoveEvent);
bool deactivateExternalDevice(int serverIndex, const QString &uuid, const QString &serverCountryCode);
void validateConfig();
void setCurrentProtocol(int serverIndex, const QString &protocolName);
bool isVlessProtocol(int serverIndex);
void removeApiConfig(int serverIndex);
bool getAccountInfo(int serverIndex, bool reload);
void getRenewalLink(int serverIndex);
void updateApiCountryModel();
void updateApiDevicesModel();
signals:
void configValidated(bool isValid);
void errorOccurred(ErrorCode errorCode);
void trialEmailError(const QString &message);
void subscriptionExpiredOnServer();
void renewalLinkReceived(const QString &url);
void installServerFromApiFinished(const QString &message, int preferredDefaultServerIndex = -1);
void changeApiCountryFinished(const QString &message);
void reloadServerFromApiFinished(const QString &message);
void updateServerFromApiFinished();
void subscriptionRefreshNeeded();
void apiConfigRemoved(const QString &message);
void vpnKeyExportReady();
private:
QList<QString> getQrCodes();
int getQrCodesCount();
QString getVpnKey();
QList<QString> m_qrCodes;
QString m_vpnKey;
ServersController* m_serversController;
ApiServicesModel* m_apiServicesModel;
ServicesCatalogController* m_servicesCatalogController;
SubscriptionController* m_subscriptionController;
ApiSubscriptionPlansModel* m_apiSubscriptionPlansModel;
ApiBenefitsModel* m_apiBenefitsModel;
ApiAccountInfoModel* m_apiAccountInfoModel;
ApiCountryModel* m_apiCountryModel;
ApiDevicesModel* m_apiDevicesModel;
SettingsController* m_settingsController;
};
#endif // SUBSCRIPTIONUICONTROLLER_H
@@ -1,49 +0,0 @@
#include "appSplitTunnelingController.h"
#include <QFileInfo>
#include "core/defs.h"
AppSplitTunnelingController::AppSplitTunnelingController(const std::shared_ptr<Settings> &settings,
const QSharedPointer<AppSplitTunnelingModel> &appSplitTunnelingModel, QObject *parent)
: QObject(parent), m_settings(settings), m_appSplitTunnelingModel(appSplitTunnelingModel)
{
}
void AppSplitTunnelingController::addApp(const QString &appPath)
{
InstalledAppInfo appInfo { "", "", appPath };
if (!appPath.isEmpty()) {
QFileInfo fileInfo(appPath);
appInfo.appName = fileInfo.fileName();
}
if (m_appSplitTunnelingModel->addApp(appInfo)) {
emit finished(tr("Application added: %1").arg(appInfo.appName));
} else {
emit errorOccurred(tr("The application has already been added"));
}
}
void AppSplitTunnelingController::addApps(QVector<QPair<QString, QString>> apps)
{
for (const auto &app : apps) {
InstalledAppInfo appInfo { app.first, app.second, "" };
m_appSplitTunnelingModel->addApp(appInfo);
}
emit finished(tr("The selected applications have been added"));
}
void AppSplitTunnelingController::removeApp(const int index)
{
auto modelIndex = m_appSplitTunnelingModel->index(index);
auto appPath = m_appSplitTunnelingModel->data(modelIndex, AppSplitTunnelingModel::Roles::AppPathRole).toString();
m_appSplitTunnelingModel->removeApp(modelIndex);
QFileInfo fileInfo(appPath);
emit finished(tr("Application removed: %1").arg(fileInfo.fileName()));
}
@@ -1,31 +0,0 @@
#ifndef APPSPLITTUNNELINGCONTROLLER_H
#define APPSPLITTUNNELINGCONTROLLER_H
#include <QObject>
#include "settings.h"
#include "ui/models/appSplitTunnelingModel.h"
class AppSplitTunnelingController : public QObject
{
Q_OBJECT
public:
explicit AppSplitTunnelingController(const std::shared_ptr<Settings> &settings,
const QSharedPointer<AppSplitTunnelingModel> &sitesModel, QObject *parent = nullptr);
public slots:
void addApp(const QString &appPath);
void addApps(QVector<QPair<QString, QString>> apps);
void removeApp(const int index);
signals:
void errorOccurred(const QString &errorMessage);
void finished(const QString &message);
private:
std::shared_ptr<Settings> m_settings;
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
};
#endif // APPSPLITTUNNELINGCONTROLLER_H
@@ -0,0 +1,79 @@
#include "appSplitTunnelingUiController.h"
#include <QFileInfo>
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
AppSplitTunnelingUiController::AppSplitTunnelingUiController(AppSplitTunnelingController* appSplitTunnelingController,
AppSplitTunnelingModel* appSplitTunnelingModel,
QObject *parent)
: QObject(parent),
m_appSplitTunnelingController(appSplitTunnelingController),
m_appSplitTunnelingModel(appSplitTunnelingModel)
{
m_appSplitTunnelingModel->updateModel(m_appSplitTunnelingController->getApps());
}
void AppSplitTunnelingUiController::addApp(const QString &appPath)
{
amnezia::InstalledAppInfo appInfo { "", "", appPath };
if (!appPath.isEmpty()) {
QFileInfo fileInfo(appPath);
appInfo.appName = fileInfo.fileName();
}
if (m_appSplitTunnelingController->addApp(appInfo)) {
emit finished(tr("Application added: %1").arg(appInfo.appName));
} else {
emit errorOccurred(tr("The application has already been added"));
}
}
void AppSplitTunnelingUiController::addApps(QVector<QPair<QString, QString>> apps)
{
for (const auto &app : apps) {
amnezia::InstalledAppInfo appInfo { app.first, app.second, "" };
m_appSplitTunnelingController->addApp(appInfo);
}
emit finished(tr("The selected applications have been added"));
}
void AppSplitTunnelingUiController::removeApp(const int index)
{
auto modelIndex = m_appSplitTunnelingModel->index(index);
auto appPath = m_appSplitTunnelingModel->data(modelIndex, AppSplitTunnelingModel::Roles::AppPathRole).toString();
m_appSplitTunnelingController->removeApp(index);
QFileInfo fileInfo(appPath);
emit finished(tr("Application removed: %1").arg(fileInfo.fileName()));
}
void AppSplitTunnelingUiController::toggleSplitTunneling(bool enabled)
{
m_appSplitTunnelingController->toggleSplitTunneling(enabled);
emit isSplitTunnelingEnabledChanged();
}
void AppSplitTunnelingUiController::setRouteMode(int routeMode)
{
m_appSplitTunnelingController->setRouteMode(static_cast<amnezia::AppsRouteMode>(routeMode));
emit routeModeChanged();
}
int AppSplitTunnelingUiController::getRouteMode() const
{
return static_cast<int>(m_appSplitTunnelingController->getRouteMode());
}
bool AppSplitTunnelingUiController::isSplitTunnelingEnabled() const
{
return m_appSplitTunnelingController->isSplitTunnelingEnabled();
}
void AppSplitTunnelingUiController::updateModel()
{
m_appSplitTunnelingModel->updateModel(m_appSplitTunnelingController->getApps());
}
@@ -0,0 +1,48 @@
#ifndef APPSPLITTUNNELINGUICONTROLLER_H
#define APPSPLITTUNNELINGUICONTROLLER_H
#include <QObject>
#include <QVector>
#include "core/controllers/appSplitTunnelingController.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "ui/models/appSplitTunnelingModel.h"
class AppSplitTunnelingUiController : public QObject
{
Q_OBJECT
Q_PROPERTY(int routeMode READ getRouteMode WRITE setRouteMode NOTIFY routeModeChanged)
Q_PROPERTY(bool isSplitTunnelingEnabled READ isSplitTunnelingEnabled NOTIFY isSplitTunnelingEnabledChanged)
public:
explicit AppSplitTunnelingUiController(AppSplitTunnelingController* appSplitTunnelingController,
AppSplitTunnelingModel* appSplitTunnelingModel,
QObject *parent = nullptr);
public slots:
void addApp(const QString &appPath);
void addApps(QVector<QPair<QString, QString>> apps);
void removeApp(const int index);
void toggleSplitTunneling(bool enabled);
void setRouteMode(int routeMode);
int getRouteMode() const;
bool isSplitTunnelingEnabled() const;
void updateModel();
signals:
void routeModeChanged();
void isSplitTunnelingEnabledChanged();
void errorOccurred(const QString &errorMessage);
void finished(const QString &message);
private:
AppSplitTunnelingController* m_appSplitTunnelingController;
AppSplitTunnelingModel* m_appSplitTunnelingModel;
};
#endif // APPSPLITTUNNELINGUICONTROLLER_H
@@ -1,182 +0,0 @@
#include "connectionController.h"
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
#include <QGuiApplication>
#else
#include <QApplication>
#endif
#include "amnezia_application.h"
#include "utilities.h"
#include "core/controllers/vpnConfigurationController.h"
#include "version.h"
ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<ClientManagementModel> &clientManagementModel,
const QSharedPointer<VpnConnection> &vpnConnection, const std::shared_ptr<Settings> &settings,
QObject *parent)
: QObject(parent),
m_serversModel(serversModel),
m_containersModel(containersModel),
m_clientManagementModel(clientManagementModel),
m_vpnConnection(vpnConnection),
m_settings(settings)
{
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, this, &ConnectionController::onConnectionStateChanged);
connect(this, &ConnectionController::connectToVpn, m_vpnConnection.get(), &VpnConnection::connectToVpn, Qt::QueuedConnection);
connect(this, &ConnectionController::disconnectFromVpn, m_vpnConnection.get(), &VpnConnection::disconnectFromVpn, Qt::QueuedConnection);
connect(this, &ConnectionController::connectButtonClicked, this, &ConnectionController::toggleConnection, Qt::QueuedConnection);
m_state = Vpn::ConnectionState::Disconnected;
}
void ConnectionController::openConnection()
{
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true))
{
emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning);
return;
}
#endif
int serverIndex = m_serversModel->getDefaultServerIndex();
QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
DockerContainer container = qvariant_cast<DockerContainer>(m_serversModel->data(serverIndex, ServersModel::Roles::DefaultContainerRole));
if (!m_containersModel->isSupportedByCurrentPlatform(container)) {
emit connectionErrorOccurred(ErrorCode::NotSupportedOnThisPlatform);
return;
}
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
auto dns = m_serversModel->getDnsPair(serverIndex);
auto vpnConfiguration = vpnConfigurationController.createVpnConfiguration(dns, serverConfig, containerConfig, container);
emit connectToVpn(serverIndex, credentials, container, vpnConfiguration);
}
void ConnectionController::closeConnection()
{
emit disconnectFromVpn();
}
ErrorCode ConnectionController::getLastConnectionError()
{
return m_vpnConnection->lastError();
}
void ConnectionController::onConnectionStateChanged(Vpn::ConnectionState state)
{
m_state = state;
m_isConnected = false;
m_connectionStateText = tr("Connecting...");
switch (state) {
case Vpn::ConnectionState::Connected: {
amnApp->networkManager()->clearConnectionCache();
m_isConnectionInProgress = false;
m_isConnected = true;
m_connectionStateText = tr("Connected");
break;
}
case Vpn::ConnectionState::Connecting: {
m_isConnectionInProgress = true;
break;
}
case Vpn::ConnectionState::Reconnecting: {
m_isConnectionInProgress = true;
m_connectionStateText = tr("Reconnecting...");
break;
}
case Vpn::ConnectionState::Disconnected: {
m_isConnectionInProgress = false;
m_connectionStateText = tr("Connect");
break;
}
case Vpn::ConnectionState::Disconnecting: {
m_isConnectionInProgress = true;
m_connectionStateText = tr("Disconnecting...");
break;
}
case Vpn::ConnectionState::Preparing: {
m_isConnectionInProgress = true;
m_connectionStateText = tr("Preparing...");
break;
}
case Vpn::ConnectionState::Error: {
m_isConnectionInProgress = false;
m_connectionStateText = tr("Connect");
emit connectionErrorOccurred(getLastConnectionError());
break;
}
case Vpn::ConnectionState::Unknown: {
m_isConnectionInProgress = false;
m_connectionStateText = tr("Connect");
emit connectionErrorOccurred(getLastConnectionError());
break;
}
}
emit connectionStateChanged();
}
void ConnectionController::onCurrentContainerUpdated()
{
if (m_isConnected || m_isConnectionInProgress) {
emit reconnectWithUpdatedContainer(tr("Settings updated successfully, reconnnection..."));
openConnection();
} else {
emit reconnectWithUpdatedContainer(tr("Settings updated successfully"));
}
}
void ConnectionController::onTranslationsUpdated()
{
// get translated text of current state
onConnectionStateChanged(getCurrentConnectionState());
}
Vpn::ConnectionState ConnectionController::getCurrentConnectionState()
{
return m_state;
}
QString ConnectionController::connectionStateText() const
{
return m_connectionStateText;
}
void ConnectionController::toggleConnection()
{
if (m_state == Vpn::ConnectionState::Preparing) {
emit preparingConfig();
return;
}
if (isConnectionInProgress()) {
closeConnection();
} else if (isConnected()) {
closeConnection();
} else {
emit prepareConfig();
}
}
bool ConnectionController::isConnectionInProgress() const
{
return m_isConnectionInProgress;
}
bool ConnectionController::isConnected() const
{
return m_isConnected;
}
@@ -1,75 +0,0 @@
#ifndef CONNECTIONCONTROLLER_H
#define CONNECTIONCONTROLLER_H
#include "protocols/vpnprotocol.h"
#include "ui/models/clientManagementModel.h"
#include "ui/models/containers_model.h"
#include "ui/models/servers_model.h"
#include "vpnconnection.h"
class ConnectionController : public QObject
{
Q_OBJECT
public:
Q_PROPERTY(bool isConnected READ isConnected NOTIFY connectionStateChanged)
Q_PROPERTY(bool isConnectionInProgress READ isConnectionInProgress NOTIFY connectionStateChanged)
Q_PROPERTY(QString connectionStateText READ connectionStateText NOTIFY connectionStateChanged)
explicit ConnectionController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<ClientManagementModel> &clientManagementModel,
const QSharedPointer<VpnConnection> &vpnConnection, const std::shared_ptr<Settings> &settings,
QObject *parent = nullptr);
~ConnectionController() = default;
bool isConnected() const;
bool isConnectionInProgress() const;
QString connectionStateText() const;
public slots:
void toggleConnection();
void openConnection();
void closeConnection();
ErrorCode getLastConnectionError();
void onConnectionStateChanged(Vpn::ConnectionState state);
void onCurrentContainerUpdated();
void onTranslationsUpdated();
signals:
void connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration);
void disconnectFromVpn();
void connectionStateChanged();
void connectionErrorOccurred(ErrorCode errorCode);
void reconnectWithUpdatedContainer(const QString &message);
void connectButtonClicked();
void preparingConfig();
void prepareConfig();
private:
Vpn::ConnectionState getCurrentConnectionState();
void continueConnection();
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<ClientManagementModel> m_clientManagementModel;
QSharedPointer<VpnConnection> m_vpnConnection;
std::shared_ptr<Settings> m_settings;
bool m_isConnected = false;
bool m_isConnectionInProgress = false;
QString m_connectionStateText = tr("Connect");
Vpn::ConnectionState m_state;
};
#endif // CONNECTIONCONTROLLER_H
@@ -0,0 +1,152 @@
#include "connectionUiController.h"
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
#include <QGuiApplication>
#else
#include <QApplication>
#endif
#include "amneziaApplication.h"
#include "core/controllers/serversController.h"
ConnectionUiController::ConnectionUiController(ConnectionController* connectionController,
ServersController* serversController,
QObject *parent)
: QObject(parent),
m_connectionController(connectionController),
m_serversController(serversController)
{
connect(m_connectionController, &ConnectionController::connectionStateChanged, this, &ConnectionUiController::onConnectionStateChanged);
connect(this, &ConnectionUiController::connectButtonClicked, this, &ConnectionUiController::toggleConnection, Qt::QueuedConnection);
m_state = Vpn::ConnectionState::Disconnected;
}
void ConnectionUiController::openConnection()
{
int serverIndex = m_serversController->getDefaultServerIndex();
ErrorCode errorCode = m_connectionController->openConnection(serverIndex);
if (errorCode != ErrorCode::NoError) {
emit connectionErrorOccurred(errorCode);
return;
}
}
void ConnectionUiController::closeConnection()
{
m_connectionController->closeConnection();
}
ErrorCode ConnectionUiController::getLastConnectionError()
{
return m_connectionController->lastConnectionError();
}
void ConnectionUiController::onConnectionStateChanged(Vpn::ConnectionState state)
{
m_state = state;
m_isConnected = false;
m_connectionStateText = tr("Connecting...");
switch (state) {
case Vpn::ConnectionState::Connected: {
amnApp->networkManager()->clearConnectionCache();
m_isConnectionInProgress = false;
m_isConnected = true;
m_connectionStateText = tr("Connected");
break;
}
case Vpn::ConnectionState::Connecting: {
m_isConnectionInProgress = true;
break;
}
case Vpn::ConnectionState::Reconnecting: {
m_isConnectionInProgress = true;
m_connectionStateText = tr("Reconnecting...");
break;
}
case Vpn::ConnectionState::Disconnected: {
m_isConnectionInProgress = false;
m_connectionStateText = tr("Connect");
break;
}
case Vpn::ConnectionState::Disconnecting: {
m_isConnectionInProgress = true;
m_connectionStateText = tr("Disconnecting...");
break;
}
case Vpn::ConnectionState::Preparing: {
m_isConnectionInProgress = true;
m_connectionStateText = tr("Preparing...");
break;
}
case Vpn::ConnectionState::Error: {
m_isConnectionInProgress = false;
m_connectionStateText = tr("Connect");
emit connectionErrorOccurred(getLastConnectionError());
break;
}
case Vpn::ConnectionState::Unknown: {
m_isConnectionInProgress = false;
m_connectionStateText = tr("Connect");
emit connectionErrorOccurred(getLastConnectionError());
break;
}
}
emit connectionStateChanged();
}
void ConnectionUiController::onCurrentContainerUpdated()
{
if (m_isConnected || m_isConnectionInProgress) {
emit reconnectWithUpdatedContainer(tr("Settings updated successfully, reconnection..."));
openConnection();
} else {
emit reconnectWithUpdatedContainer(tr("Settings updated successfully"));
}
}
void ConnectionUiController::onTranslationsUpdated()
{
onConnectionStateChanged(getCurrentConnectionState());
}
Vpn::ConnectionState ConnectionUiController::getCurrentConnectionState()
{
return m_state;
}
QString ConnectionUiController::connectionStateText() const
{
return m_connectionStateText;
}
void ConnectionUiController::toggleConnection()
{
if (m_state == Vpn::ConnectionState::Preparing) {
emit preparingConfig();
return;
}
if (isConnectionInProgress()) {
closeConnection();
} else if (isConnected()) {
closeConnection();
} else {
emit prepareConfig();
}
}
bool ConnectionUiController::isConnectionInProgress() const
{
return m_isConnectionInProgress;
}
bool ConnectionUiController::isConnected() const
{
return m_isConnected;
}
@@ -0,0 +1,68 @@
#ifndef CONNECTIONUICONTROLLER_H
#define CONNECTIONUICONTROLLER_H
#include <QObject>
#include "core/controllers/connectionController.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/protocols/vpnProtocol.h"
#include "core/controllers/serversController.h"
class ConnectionUiController : public QObject
{
Q_OBJECT
public:
Q_PROPERTY(bool isConnected READ isConnected NOTIFY connectionStateChanged)
Q_PROPERTY(bool isConnectionInProgress READ isConnectionInProgress NOTIFY connectionStateChanged)
Q_PROPERTY(QString connectionStateText READ connectionStateText NOTIFY connectionStateChanged)
explicit ConnectionUiController(ConnectionController* connectionController,
ServersController* serversController,
QObject *parent = nullptr);
~ConnectionUiController() = default;
bool isConnected() const;
bool isConnectionInProgress() const;
QString connectionStateText() const;
public slots:
void toggleConnection();
void openConnection();
void closeConnection();
ErrorCode getLastConnectionError();
void onConnectionStateChanged(Vpn::ConnectionState state);
void onCurrentContainerUpdated();
void onTranslationsUpdated();
signals:
void connectionStateChanged();
void connectionErrorOccurred(ErrorCode errorCode);
void reconnectWithUpdatedContainer(const QString &message);
void connectButtonClicked();
void preparingConfig();
void prepareConfig();
private:
Vpn::ConnectionState getCurrentConnectionState();
ConnectionController* m_connectionController;
ServersController* m_serversController;
bool m_isConnected = false;
bool m_isConnectionInProgress = false;
QString m_connectionStateText = tr("Connect");
Vpn::ConnectionState m_state;
};
#endif
-392
View File
@@ -1,392 +0,0 @@
#include "exportController.h"
#include <QBuffer>
#include <QDataStream>
#include <QDesktopServices>
#include <QFile>
#include <QFileInfo>
#include <QImage>
#include <QStandardPaths>
#include "core/controllers/vpnConfigurationController.h"
#include "core/qrCodeUtils.h"
#include "core/serialization/serialization.h"
#include "core/serialization/transfer.h"
#include "systemController.h"
#include <QDebug>
ExportController::ExportController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<ClientManagementModel> &clientManagementModel,
const std::shared_ptr<Settings> &settings, QObject *parent)
: QObject(parent),
m_serversModel(serversModel),
m_containersModel(containersModel),
m_clientManagementModel(clientManagementModel),
m_settings(settings)
{
}
void ExportController::generateFullAccessConfig()
{
clearPreviousConfig();
int serverIndex = m_serversModel->getProcessedServerIndex();
QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
QJsonArray containers = serverConfig.value(config_key::containers).toArray();
for (auto i = 0; i < containers.size(); i++) {
auto containerConfig = containers.at(i).toObject();
auto containerType = ContainerProps::containerFromString(containerConfig.value(config_key::container).toString());
for (auto protocol : ContainerProps::protocolsForContainer(containerType)) {
auto protocolConfig = containerConfig.value(ProtocolProps::protoToString(protocol)).toObject();
protocolConfig.remove(config_key::last_config);
containerConfig[ProtocolProps::protoToString(protocol)] = protocolConfig;
}
containers.replace(i, containerConfig);
}
serverConfig[config_key::containers] = containers;
QByteArray compressedConfig = QJsonDocument(serverConfig).toJson();
compressedConfig = qCompress(compressedConfig, 8);
m_config = QString("vpn://%1").arg(QString(compressedConfig.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)));
m_qrCodes = qrCodeUtils::generateQrCodeImageSeries(compressedConfig);
emit exportConfigChanged();
}
void ExportController::generateConnectionConfig(const QString &clientName)
{
clearPreviousConfig();
int serverIndex = m_serversModel->getProcessedServerIndex();
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getProcessedContainerIndex());
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
ErrorCode errorCode = vpnConfigurationController.createProtocolConfigForContainer(credentials, container, containerConfig);
errorCode = m_clientManagementModel->appendClient(container, credentials, containerConfig, clientName, serverController);
if (errorCode != ErrorCode::NoError) {
emit exportErrorOccurred(errorCode);
return;
}
QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
if (!errorCode) {
serverConfig.remove(config_key::userName);
serverConfig.remove(config_key::password);
serverConfig.remove(config_key::port);
serverConfig.insert(config_key::containers, QJsonArray { containerConfig });
serverConfig.insert(config_key::defaultContainer, ContainerProps::containerToString(container));
auto dns = m_serversModel->getDnsPair(serverIndex);
serverConfig.insert(config_key::dns1, dns.first);
serverConfig.insert(config_key::dns2, dns.second);
}
QByteArray compressedConfig = QJsonDocument(serverConfig).toJson();
compressedConfig = qCompress(compressedConfig, 8);
m_config = QString("vpn://%1").arg(QString(compressedConfig.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)));
m_qrCodes = qrCodeUtils::generateQrCodeImageSeries(compressedConfig);
emit exportConfigChanged();
}
ErrorCode ExportController::generateNativeConfig(const DockerContainer container, const QString &clientName, const Proto &protocol,
QJsonObject &jsonNativeConfig)
{
clearPreviousConfig();
int serverIndex = m_serversModel->getProcessedServerIndex();
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
auto dns = m_serversModel->getDnsPair(serverIndex);
bool isApiConfig = qvariant_cast<bool>(m_serversModel->data(serverIndex, ServersModel::IsServerFromTelegramApiRole));
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
QString protocolConfigString;
ErrorCode errorCode = vpnConfigurationController.createProtocolConfigString(isApiConfig, dns, credentials, container, containerConfig,
protocol, protocolConfigString);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
jsonNativeConfig = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
if (protocol == Proto::OpenVpn || protocol == Proto::WireGuard || protocol == Proto::Awg || protocol == Proto::Xray) {
errorCode = m_clientManagementModel->appendClient(jsonNativeConfig, clientName, container, credentials, serverController);
}
return errorCode;
}
void ExportController::generateOpenVpnConfig(const QString &clientName)
{
QJsonObject nativeConfig;
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getProcessedContainerIndex());
ErrorCode errorCode = ErrorCode::NoError;
if (container == DockerContainer::Cloak || container == DockerContainer::ShadowSocks) {
errorCode = generateNativeConfig(container, clientName, Proto::OpenVpn, nativeConfig);
} else {
errorCode = generateNativeConfig(container, clientName, ContainerProps::defaultProtocol(container), nativeConfig);
}
if (errorCode) {
emit exportErrorOccurred(errorCode);
return;
}
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
for (const QString &line : std::as_const(lines)) {
m_config.append(line + "\n");
}
m_qrCodes = qrCodeUtils::generateQrCodeImageSeries(m_config.toUtf8());
emit exportConfigChanged();
}
void ExportController::generateWireGuardConfig(const QString &clientName)
{
QJsonObject nativeConfig;
ErrorCode errorCode = generateNativeConfig(DockerContainer::WireGuard, clientName, Proto::WireGuard, nativeConfig);
if (errorCode) {
emit exportErrorOccurred(errorCode);
return;
}
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
for (const QString &line : std::as_const(lines)) {
m_config.append(line + "\n");
}
m_qrCodes = qrCodeUtils::generateQrCodeImageSeries(m_config.toUtf8());
emit exportConfigChanged();
}
void ExportController::generateAwgConfig(const QString &clientName)
{
QJsonObject nativeConfig;
ErrorCode errorCode = generateNativeConfig(static_cast<DockerContainer>(m_containersModel->getProcessedContainerIndex()), clientName,
Proto::Awg, nativeConfig);
if (errorCode) {
emit exportErrorOccurred(errorCode);
return;
}
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
for (const QString &line : std::as_const(lines)) {
m_config.append(line + "\n");
}
m_qrCodes = qrCodeUtils::generateQrCodeImageSeries(m_config.toUtf8());
emit exportConfigChanged();
}
void ExportController::generateShadowSocksConfig()
{
QJsonObject nativeConfig;
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getProcessedContainerIndex());
ErrorCode errorCode = ErrorCode::NoError;
if (container == DockerContainer::Cloak) {
errorCode = generateNativeConfig(container, "", Proto::ShadowSocks, nativeConfig);
} else {
errorCode = generateNativeConfig(container, "", ContainerProps::defaultProtocol(container), nativeConfig);
}
if (errorCode) {
emit exportErrorOccurred(errorCode);
return;
}
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
for (const QString &line : std::as_const(lines)) {
m_config.append(line + "\n");
}
m_nativeConfigString = QString("%1:%2@%3:%4")
.arg(nativeConfig.value("method").toString(), nativeConfig.value("password").toString(),
nativeConfig.value("server").toString(), nativeConfig.value("server_port").toString());
m_nativeConfigString = "ss://" + m_nativeConfigString.toUtf8().toBase64();
auto qr = qrCodeUtils::generateQrCode(m_nativeConfigString.toUtf8());
m_qrCodes << qrCodeUtils::svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
emit exportConfigChanged();
}
void ExportController::generateCloakConfig()
{
QJsonObject nativeConfig;
ErrorCode errorCode = generateNativeConfig(DockerContainer::Cloak, "", Proto::Cloak, nativeConfig);
if (errorCode) {
emit exportErrorOccurred(errorCode);
return;
}
nativeConfig.remove(config_key::transport_proto);
nativeConfig.insert("ProxyMethod", "shadowsocks");
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
for (const QString &line : std::as_const(lines)) {
m_config.append(line + "\n");
}
emit exportConfigChanged();
}
void ExportController::generateXrayConfig(const QString &clientName)
{
// Xray data
QJsonObject nativeConfig;
ErrorCode errorCode = generateNativeConfig(DockerContainer::Xray, clientName, Proto::Xray, nativeConfig);
if (errorCode) {
emit exportErrorOccurred(errorCode);
return;
}
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
for (const QString &line : std::as_const(lines)) {
m_config.append(line + "\n");
}
// Xray data
// Parse the Xray data to extract VLESS parameters and generate string
QString configString = QString(QJsonDocument(nativeConfig).toJson(QJsonDocument::Compact));
QJsonDocument doc = QJsonDocument::fromJson(configString.toUtf8());
if (doc.isNull() || !doc.isObject()) {
qDebug() << "ERROR: Failed to parse config JSON";
emit exportErrorOccurred(ErrorCode::InternalError);
return;
}
QJsonObject xrayConfig = doc.object();
QJsonArray outbounds = xrayConfig.value("outbounds").toArray();
if (outbounds.isEmpty()) {
qDebug() << "ERROR: Outbounds array is empty";
emit exportErrorOccurred(ErrorCode::InternalError);
return;
}
QJsonObject outbound = outbounds[0].toObject();
QJsonObject settings = outbound.value("settings").toObject();
QJsonObject streamSettings = outbound.value("streamSettings").toObject();
QJsonArray vnext = settings.value("vnext").toArray();
if (vnext.isEmpty()) {
qDebug() << "ERROR: vnext array is empty";
emit exportErrorOccurred(ErrorCode::InternalError);
return;
}
QJsonObject server = vnext[0].toObject();
QJsonArray users = server.value("users").toArray();
if (users.isEmpty()) {
qDebug() << "ERROR: users array is empty";
emit exportErrorOccurred(ErrorCode::InternalError);
return;
}
QJsonObject user = users[0].toObject();
amnezia::serialization::VlessServerObject vlessServer;
vlessServer.address = server.value("address").toString();
vlessServer.port = server.value("port").toInt();
vlessServer.id = user.value("id").toString();
vlessServer.flow = user.value("flow").toString("xtls-rprx-vision");
vlessServer.encryption = user.value("encryption").toString("none");
vlessServer.network = streamSettings.value("network").toString("tcp");
vlessServer.security = streamSettings.value("security").toString("reality");
if (vlessServer.security == "reality") {
QJsonObject realitySettings = streamSettings.value("realitySettings").toObject();
vlessServer.serverName = realitySettings.value("serverName").toString();
vlessServer.publicKey = realitySettings.value("publicKey").toString();
vlessServer.shortId = realitySettings.value("shortId").toString();
vlessServer.fingerprint = realitySettings.value("fingerprint").toString("chrome");
vlessServer.spiderX = realitySettings.value("spiderX").toString("");
}
m_nativeConfigString = amnezia::serialization::vless::Serialize(vlessServer, "AmneziaVPN");
emit exportConfigChanged();
}
QString ExportController::getConfig()
{
return m_config;
}
QString ExportController::getNativeConfigString()
{
return m_nativeConfigString;
}
QList<QString> ExportController::getQrCodes()
{
return m_qrCodes;
}
void ExportController::exportConfig(const QString &fileName)
{
SystemController::saveFile(fileName, m_config);
}
void ExportController::updateClientManagementModel(const DockerContainer container, ServerCredentials credentials)
{
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
ErrorCode errorCode = m_clientManagementModel->updateModel(container, credentials, serverController);
if (errorCode != ErrorCode::NoError) {
emit exportErrorOccurred(errorCode);
}
}
void ExportController::revokeConfig(const int row, const DockerContainer container, ServerCredentials credentials)
{
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
ErrorCode errorCode =
m_clientManagementModel->revokeClient(row, container, credentials, m_serversModel->getProcessedServerIndex(), serverController);
if (errorCode != ErrorCode::NoError) {
emit exportErrorOccurred(errorCode);
}
emit revokeConfigCompleted();
}
void ExportController::renameClient(const int row, const QString &clientName, const DockerContainer container, ServerCredentials credentials)
{
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
ErrorCode errorCode = m_clientManagementModel->renameClient(row, clientName, container, credentials, serverController);
if (errorCode != ErrorCode::NoError) {
emit exportErrorOccurred(errorCode);
}
}
int ExportController::getQrCodesCount()
{
return m_qrCodes.size();
}
void ExportController::clearPreviousConfig()
{
m_config.clear();
m_nativeConfigString.clear();
m_qrCodes.clear();
emit exportConfigChanged();
}
-71
View File
@@ -1,71 +0,0 @@
#ifndef EXPORTCONTROLLER_H
#define EXPORTCONTROLLER_H
#include <QObject>
#include "ui/models/clientManagementModel.h"
#include "ui/models/containers_model.h"
#include "ui/models/servers_model.h"
class ExportController : public QObject
{
Q_OBJECT
public:
explicit ExportController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<ClientManagementModel> &clientManagementModel, const std::shared_ptr<Settings> &settings,
QObject *parent = nullptr);
Q_PROPERTY(QList<QString> qrCodes READ getQrCodes NOTIFY exportConfigChanged)
Q_PROPERTY(int qrCodesCount READ getQrCodesCount NOTIFY exportConfigChanged)
Q_PROPERTY(QString config READ getConfig NOTIFY exportConfigChanged)
Q_PROPERTY(QString nativeConfigString READ getNativeConfigString NOTIFY exportConfigChanged)
public slots:
void generateFullAccessConfig();
void generateConnectionConfig(const QString &clientName);
void generateOpenVpnConfig(const QString &clientName);
void generateWireGuardConfig(const QString &clientName);
void generateAwgConfig(const QString &clientName);
void generateShadowSocksConfig();
void generateCloakConfig();
void generateXrayConfig(const QString &clientName);
QString getConfig();
QString getNativeConfigString();
QList<QString> getQrCodes();
void exportConfig(const QString &fileName);
void updateClientManagementModel(const DockerContainer container, ServerCredentials credentials);
void revokeConfig(const int row, const DockerContainer container, ServerCredentials credentials);
void renameClient(const int row, const QString &clientName, const DockerContainer container, ServerCredentials credentials);
signals:
void generateConfig(int type);
void revokeConfigCompleted();
void exportErrorOccurred(const QString &errorMessage);
void exportErrorOccurred(ErrorCode errorCode);
void exportConfigChanged();
void saveFile(const QString &fileName, const QString &data);
private:
int getQrCodesCount();
void clearPreviousConfig();
ErrorCode generateNativeConfig(const DockerContainer container, const QString &clientName, const Proto &protocol,
QJsonObject &jsonNativeConfig);
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<ClientManagementModel> m_clientManagementModel;
std::shared_ptr<Settings> m_settings;
QString m_config;
QString m_nativeConfigString;
QList<QString> m_qrCodes;
};
#endif // EXPORTCONTROLLER_H
-773
View File
@@ -1,773 +0,0 @@
#include "importController.h"
#include <QFile>
#include <QFileInfo>
#include <QQuickItem>
#include <QRandomGenerator>
#include <QStandardPaths>
#include <QUrlQuery>
#include "core/api/apiDefs.h"
#include "core/api/apiUtils.h"
#include "core/errorstrings.h"
#include "core/qrCodeUtils.h"
#include "core/serialization/serialization.h"
#include "protocols/protocols_defs.h"
#include "systemController.h"
#include "utilities.h"
#ifdef Q_OS_ANDROID
#include "platforms/android/android_controller.h"
#endif
#if defined(Q_OS_IOS) || defined(MACOS_NE)
#include <CoreFoundation/CoreFoundation.h>
#endif
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;
}
#if defined Q_OS_ANDROID
ImportController *mInstance = nullptr;
#endif
} // namespace
ImportController::ImportController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel,
const std::shared_ptr<Settings> &settings, QObject *parent)
: QObject(parent), m_serversModel(serversModel), m_containersModel(containersModel), m_settings(settings)
{
#ifdef Q_OS_ANDROID
mInstance = this;
#endif
}
bool ImportController::extractConfigFromFile(const QString &fileName)
{
QString data;
if (!SystemController::readFile(fileName, data)) {
emit importErrorOccurred(ErrorCode::ImportOpenConfigError, false);
return false;
}
m_configFileName = QFileInfo(QFile(fileName).fileName()).fileName();
#ifdef Q_OS_ANDROID
if (m_configFileName.isEmpty()) {
m_configFileName = AndroidController::instance()->getFileName(fileName);
}
#endif
return extractConfigFromData(data);
}
bool ImportController::extractConfigFromData(QString data)
{
m_maliciousWarningText.clear();
QString config = data;
QString prefix;
QString errormsg;
if (config.startsWith("vless://")) {
m_configType = ConfigTypes::Xray;
m_config = extractXrayConfig(
Utils::JsonToString(serialization::vless::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
prefix);
return m_config.empty() ? false : true;
}
if (config.startsWith("vmess://") && config.contains("@")) {
m_configType = ConfigTypes::Xray;
m_config = extractXrayConfig(
Utils::JsonToString(serialization::vmess_new::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
prefix);
return m_config.empty() ? false : true;
}
if (config.startsWith("vmess://")) {
m_configType = ConfigTypes::Xray;
m_config = extractXrayConfig(
Utils::JsonToString(serialization::vmess::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
prefix);
return m_config.empty() ? false : true;
}
if (config.startsWith("trojan://")) {
m_configType = ConfigTypes::Xray;
m_config = extractXrayConfig(
Utils::JsonToString(serialization::trojan::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact),
prefix);
return m_config.empty() ? false : true;
}
if (config.startsWith("ss://") && !config.contains("plugin=")) {
m_configType = ConfigTypes::ShadowSocks;
m_config = extractXrayConfig(
Utils::JsonToString(serialization::ss::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact), prefix);
return m_config.empty() ? false : true;
}
if (config.startsWith("ssd://")) {
QStringList tmp;
QList<std::pair<QString, QJsonObject>> servers = serialization::ssd::Deserialize(config, &prefix, &tmp);
m_configType = ConfigTypes::ShadowSocks;
// Took only first config from list
if (!servers.isEmpty()) {
m_config = extractXrayConfig(servers.first().first);
}
return m_config.empty() ? false : true;
}
m_configType = checkConfigFormat(config);
if (m_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;
m_configType = checkConfigFormat(config);
}
switch (m_configType) {
case ConfigTypes::OpenVpn: {
m_config = extractOpenVpnConfig(config);
if (!m_config.empty()) {
checkForMaliciousStrings(m_config);
return true;
}
return false;
}
case ConfigTypes::Awg:
case ConfigTypes::WireGuard: {
m_config = extractWireGuardConfig(config);
return m_config.empty() ? false : true;
}
case ConfigTypes::Xray: {
m_config = extractXrayConfig(config);
return m_config.empty() ? false : true;
}
case ConfigTypes::Amnezia: {
m_config = QJsonDocument::fromJson(config.toUtf8()).object();
if (apiUtils::isServerFromApi(m_config)) {
auto apiConfig = m_config.value(apiDefs::key::apiConfig).toObject();
apiConfig[apiDefs::key::vpnKey] = data;
m_config[apiDefs::key::apiConfig] = apiConfig;
}
processAmneziaConfig(m_config);
if (!m_config.empty()) {
checkForMaliciousStrings(m_config);
return true;
}
return false;
}
case ConfigTypes::Backup: {
if (!m_serversModel->getServersCount()) {
emit restoreAppConfig(config.toUtf8());
} else {
emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false);
}
break;
}
case ConfigTypes::Invalid: {
emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false);
m_configFileName.clear();
break;
}
}
return false;
}
bool ImportController::extractConfigFromQr(const QByteArray &data)
{
m_configType = checkConfigFormat(QString::fromUtf8(data));
QJsonObject dataObj = QJsonDocument::fromJson(data).object();
if (!dataObj.isEmpty()) {
m_config = dataObj;
return true;
}
QByteArray ba_uncompressed = qUncompress(data);
if (!ba_uncompressed.isEmpty()) {
m_config = QJsonDocument::fromJson(ba_uncompressed).object();
if (m_config.isEmpty()) {
return false;
}
m_configType = checkConfigFormat(QString::fromUtf8(ba_uncompressed));
return true;
}
if (m_configType == ConfigTypes::Invalid) {
QByteArray ba = QByteArray::fromBase64(data, QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
QByteArray baUncompressed = qUncompress(ba);
if (!baUncompressed.isEmpty()) {
ba = baUncompressed;
}
if (!ba.isEmpty()) {
m_config = QJsonDocument::fromJson(ba).object();
if (m_config.isEmpty()) {
return false;
}
m_configType = checkConfigFormat(QString::fromUtf8(ba));
return true;
}
}
return false;
}
QString ImportController::getConfig()
{
return QJsonDocument(m_config).toJson(QJsonDocument::Indented);
}
QString ImportController::getConfigFileName()
{
return m_configFileName;
}
QString ImportController::getMaliciousWarningText()
{
return m_maliciousWarningText;
}
bool ImportController::isNativeWireGuardConfig()
{
return m_configType == ConfigTypes::WireGuard;
}
void ImportController::processNativeWireGuardConfig()
{
auto containers = m_config.value(config_key::containers).toArray();
if (!containers.isEmpty()) {
auto container = containers.at(0).toObject();
auto serverProtocolConfig = container.value(ContainerProps::containerTypeToProtocolString(DockerContainer::WireGuard)).toObject();
auto clientProtocolConfig = QJsonDocument::fromJson(serverProtocolConfig.value(config_key::last_config).toString().toUtf8()).object();
QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(4, 7));
QString junkPacketMinSize = QString::number(10);
QString junkPacketMaxSize = QString::number(50);
clientProtocolConfig[config_key::junkPacketCount] = junkPacketCount;
clientProtocolConfig[config_key::junkPacketMinSize] = junkPacketMinSize;
clientProtocolConfig[config_key::junkPacketMaxSize] = junkPacketMaxSize;
clientProtocolConfig[config_key::initPacketJunkSize] = "0";
clientProtocolConfig[config_key::responsePacketJunkSize] = "0";
clientProtocolConfig[config_key::initPacketMagicHeader] = "1";
clientProtocolConfig[config_key::responsePacketMagicHeader] = "2";
clientProtocolConfig[config_key::underloadPacketMagicHeader] = "3";
clientProtocolConfig[config_key::transportPacketMagicHeader] = "4";
clientProtocolConfig[config_key::cookieReplyPacketJunkSize] = "0";
clientProtocolConfig[config_key::transportPacketJunkSize] = "0";
clientProtocolConfig[config_key::specialJunk1] = protocols::awg::defaultSpecialJunk1;
clientProtocolConfig[config_key::isObfuscationEnabled] = true;
serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(clientProtocolConfig).toJson());
container["wireguard"] = serverProtocolConfig;
containers.replace(0, container);
m_config[config_key::containers] = containers;
}
}
void ImportController::importConfig()
{
ServerCredentials credentials;
credentials.hostName = m_config.value(config_key::hostName).toString();
credentials.port = m_config.value(config_key::port).toInt();
credentials.userName = m_config.value(config_key::userName).toString();
credentials.secretData = m_config.value(config_key::password).toString();
if (credentials.isValid() || m_config.contains(config_key::containers)) {
m_serversModel->addServer(m_config);
emit importFinished();
} else if (m_config.contains(config_key::configVersion)) {
quint16 crc = qChecksum(QJsonDocument(m_config).toJson());
if (m_serversModel->isServerFromApiAlreadyExists(crc)) {
emit importErrorOccurred(ErrorCode::ApiConfigAlreadyAdded, true);
} else {
m_config.insert(config_key::crc, crc);
m_serversModel->addServer(m_config);
emit importFinished();
}
} else {
qDebug() << "Failed to import profile";
qDebug().noquote() << QJsonDocument(m_config).toJson();
emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false);
}
m_config = {};
m_configFileName.clear();
m_maliciousWarningText.clear();
}
void ImportController::clearConfigFileName()
{
m_configFileName.clear();
}
QJsonObject ImportController::extractOpenVpnConfig(const QString &data)
{
QJsonObject openVpnConfig;
openVpnConfig[config_key::config] = data;
QJsonObject lastConfig;
lastConfig[config_key::last_config] = QString(QJsonDocument(openVpnConfig).toJson());
lastConfig[config_key::isThirdPartyConfig] = true;
QJsonObject containers;
containers.insert(config_key::container, QJsonValue("amnezia-openvpn"));
containers.insert(config_key::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[config_key::containers] = arr;
config[config_key::defaultContainer] = "amnezia-openvpn";
config[config_key::description] = m_settings->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[config_key::dns1] = dnsMatch.next().captured(1);
}
if (dnsMatch.hasNext()) {
config[config_key::dns2] = dnsMatch.next().captured(1);
}
config[config_key::hostName] = hostName;
return config;
}
QJsonObject ImportController::extractWireGuardConfig(const QString &data)
{
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[config_key::config] = data;
auto url { QUrl::fromUserInput(configMap.value("Endpoint")) };
QString hostName;
QString port;
if (!url.host().isEmpty()) {
hostName = url.host();
} else {
qDebug() << "Key parameter 'Endpoint' is missing or has an invalid format";
emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false);
return QJsonObject();
}
if (url.port() != -1) {
port = QString::number(url.port());
} else {
port = protocols::wireguard::defaultPort;
}
lastConfig[config_key::hostName] = hostName;
lastConfig[config_key::port] = port.toInt();
if (!configMap.value("PrivateKey").isEmpty() && !configMap.value("Address").isEmpty() && !configMap.value("PublicKey").isEmpty()) {
lastConfig[config_key::client_priv_key] = configMap.value("PrivateKey");
lastConfig[config_key::client_ip] = configMap.value("Address");
if (!configMap.value("PresharedKey").isEmpty()) {
lastConfig[config_key::psk_key] = configMap.value("PresharedKey");
} else if (!configMap.value("PreSharedKey").isEmpty()) {
lastConfig[config_key::psk_key] = configMap.value("PreSharedKey");
}
lastConfig[config_key::server_pub_key] = configMap.value("PublicKey");
} else {
qDebug() << "One of the key parameters is missing (PrivateKey, Address, PublicKey)";
emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false);
return QJsonObject();
}
if (!configMap.value("MTU").isEmpty()) {
lastConfig[config_key::mtu] = configMap.value("MTU");
}
if (!configMap.value("PersistentKeepalive").isEmpty()) {
lastConfig[config_key::persistent_keep_alive] = configMap.value("PersistentKeepalive");
}
QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configMap.value("AllowedIPs").split(", "));
lastConfig[config_key::allowed_ips] = allowedIpsJsonArray;
QString protocolName = "wireguard";
QString protocolVersion;
const QStringList requiredJunkFields = { config_key::junkPacketCount, config_key::junkPacketMinSize,
config_key::junkPacketMaxSize, config_key::initPacketJunkSize,
config_key::responsePacketJunkSize, config_key::initPacketMagicHeader,
config_key::responsePacketMagicHeader, config_key::underloadPacketMagicHeader,
config_key::transportPacketMagicHeader };
const QStringList optionalJunkFields = { config_key::cookieReplyPacketJunkSize,
config_key::transportPacketJunkSize,
config_key::specialJunk1, config_key::specialJunk2, config_key::specialJunk3,
config_key::specialJunk4, config_key::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(config_key::cookieReplyPacketJunkSize).isEmpty();
bool hasTransportPacketJunkSize = !configMap.value(config_key::transportPacketJunkSize).isEmpty();
bool hasSpecialJunk = !configMap.value(config_key::specialJunk1).isEmpty() ||
!configMap.value(config_key::specialJunk2).isEmpty() ||
!configMap.value(config_key::specialJunk3).isEmpty() ||
!configMap.value(config_key::specialJunk4).isEmpty() ||
!configMap.value(config_key::specialJunk5).isEmpty();
if (hasCookieReplyPacketJunkSize && hasTransportPacketJunkSize) {
protocolVersion = "2";
} else if (hasSpecialJunk && !hasCookieReplyPacketJunkSize && !hasTransportPacketJunkSize) {
protocolVersion = "1.5";
}
protocolName = "awg";
m_configType = ConfigTypes::Awg;
}
if (!configMap.value("MTU").isEmpty()) {
lastConfig[config_key::mtu] = configMap.value("MTU");
} else {
lastConfig[config_key::mtu] = (protocolName == "awg")
? protocols::awg::defaultMtu
: protocols::wireguard::defaultMtu;
}
QJsonObject wireguardConfig;
wireguardConfig[config_key::last_config] = QString(QJsonDocument(lastConfig).toJson());
wireguardConfig[config_key::isThirdPartyConfig] = true;
wireguardConfig[config_key::port] = port;
wireguardConfig[config_key::transport_proto] = "udp";
if (protocolName == "awg" && !protocolVersion.isEmpty()) {
wireguardConfig[config_key::protocolVersion] = protocolVersion;
}
QJsonObject containers;
containers.insert(config_key::container, QJsonValue("amnezia-" + protocolName));
containers.insert(protocolName, QJsonValue(wireguardConfig));
QJsonArray arr;
arr.push_back(containers);
QJsonObject config;
config[config_key::containers] = arr;
config[config_key::defaultContainer] = "amnezia-" + protocolName;
config[config_key::description] = m_settings->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[config_key::dns1] = dnsMatch.captured(1);
config[config_key::dns2] = dnsMatch.captured(2);
}
config[config_key::hostName] = hostName;
return config;
}
QJsonObject ImportController::extractXrayConfig(const QString &data, const QString &description)
{
QJsonParseError parserErr;
QJsonDocument jsonConf = QJsonDocument::fromJson(data.toLocal8Bit(), &parserErr);
QJsonObject xrayVpnConfig;
xrayVpnConfig[config_key::config] = jsonConf.toJson().constData();
QJsonObject lastConfig;
lastConfig[config_key::last_config] = jsonConf.toJson().constData();
lastConfig[config_key::isThirdPartyConfig] = true;
QJsonObject containers;
if (m_configType == ConfigTypes::ShadowSocks) {
containers.insert(config_key::ssxray, QJsonValue(lastConfig));
containers.insert(config_key::container, QJsonValue("amnezia-ssxray"));
} else {
containers.insert(config_key::container, QJsonValue("amnezia-xray"));
containers.insert(config_key::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[config_key::containers] = arr;
if (m_configType == ConfigTypes::ShadowSocks) {
config[config_key::defaultContainer] = "amnezia-ssxray";
} else {
config[config_key::defaultContainer] = "amnezia-xray";
}
if (description.isEmpty()) {
config[config_key::description] = m_settings->nextAvailableServerName();
} else {
config[config_key::description] = description;
}
config[config_key::hostName] = hostName;
return config;
}
#ifdef Q_OS_ANDROID
static QMutex qrDecodeMutex;
// static
bool ImportController::decodeQrCode(const QString &code)
{
QMutexLocker lock(&qrDecodeMutex);
if (!mInstance->m_isQrCodeProcessed) {
mInstance->m_qrCodeChunks.clear();
mInstance->m_isQrCodeProcessed = true;
mInstance->m_totalQrCodeChunksCount = 0;
mInstance->m_receivedQrCodeChunksCount = 0;
}
return mInstance->parseQrCodeChunk(code);
}
#endif
#if defined Q_OS_ANDROID || defined Q_OS_IOS
void ImportController::startDecodingQr()
{
m_qrCodeChunks.clear();
m_totalQrCodeChunksCount = 0;
m_receivedQrCodeChunksCount = 0;
#if defined(Q_OS_IOS) || defined(MACOS_NE)
m_isQrCodeProcessed = true;
#endif
#if defined Q_OS_ANDROID
AndroidController::instance()->startQrReaderActivity();
#endif
}
void ImportController::stopDecodingQr()
{
emit qrDecodingFinished();
}
bool ImportController::parseQrCodeChunk(const QString &code)
{
// qDebug() << code;
if (!m_isQrCodeProcessed)
return false;
// check if chunk received
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();
if (m_qrCodeChunks.size() == m_totalQrCodeChunksCount) {
QByteArray data;
for (int i = 0; i < m_totalQrCodeChunksCount; ++i) {
data.append(m_qrCodeChunks.value(i));
}
bool ok = extractConfigFromQr(data);
if (ok) {
m_isQrCodeProcessed = false;
qDebug() << "stopDecodingQr";
stopDecodingQr();
return true;
} else {
qDebug() << "error while extracting data from qr";
m_qrCodeChunks.clear();
m_totalQrCodeChunksCount = 0;
m_receivedQrCodeChunksCount = 0;
}
}
} else {
bool ok = extractConfigFromQr(ba);
if (ok) {
m_isQrCodeProcessed = false;
qDebug() << "stopDecodingQr";
stopDecodingQr();
return true;
}
}
return false;
}
double ImportController::getQrCodeScanProgressBarValue()
{
return (1.0 / m_totalQrCodeChunksCount) * m_receivedQrCodeChunksCount;
}
QString ImportController::getQrCodeScanProgressString()
{
return tr("Scanned %1 of %2.").arg(m_receivedQrCodeChunksCount).arg(m_totalQrCodeChunksCount);
}
#endif
void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig)
{
const QJsonArray &containers = serverConfig[config_key::containers].toArray();
for (const QJsonValue &container : containers) {
auto containerConfig = container.toObject();
auto containerName = containerConfig[config_key::container].toString();
if ((containerName == ContainerProps::containerToString(DockerContainer::OpenVpn))
|| (containerName == ContainerProps::containerToString(DockerContainer::Cloak))
|| (containerName == ContainerProps::containerToString(DockerContainer::ShadowSocks))) {
QString protocolConfig =
containerConfig[ProtocolProps::protoToString(Proto::OpenVpn)].toObject()[config_key::last_config].toString();
QString protocolConfigJson = QJsonDocument::fromJson(protocolConfig.toUtf8()).object()[config_key::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;
}
}
m_maliciousWarningText = tr("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()) {
m_maliciousWarningText.push_back(tr("<br>In the imported configuration, potentially dangerous lines were found:"));
for (const auto &string : maliciousStrings) {
m_maliciousWarningText.push_back(QString("<br><i>%1</i>").arg(string));
}
}
}
}
}
void ImportController::processAmneziaConfig(QJsonObject &config)
{
auto containers = config.value(config_key::containers).toArray();
for (auto i = 0; i < containers.size(); i++) {
auto container = containers.at(i).toObject();
auto dockerContainer = ContainerProps::containerFromString(container.value(config_key::container).toString());
if (ContainerProps::isAwgContainer(dockerContainer) || dockerContainer == DockerContainer::WireGuard) {
auto containerConfig = container.value(ContainerProps::containerTypeToProtocolString(dockerContainer)).toObject();
auto protocolConfig = containerConfig.value(config_key::last_config).toString();
if (protocolConfig.isEmpty()) {
return;
}
QJsonObject jsonConfig = QJsonDocument::fromJson(protocolConfig.toUtf8()).object();
jsonConfig[config_key::mtu] =
ContainerProps::isAwgContainer(dockerContainer) ? protocols::awg::defaultMtu : protocols::wireguard::defaultMtu;
containerConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson());
container[ContainerProps::containerTypeToProtocolString(dockerContainer)] = containerConfig;
containers.replace(i, container);
config.insert(config_key::containers, containers);
}
}
}
-94
View File
@@ -1,94 +0,0 @@
#ifndef IMPORTCONTROLLER_H
#define IMPORTCONTROLLER_H
#include <QObject>
#include "ui/models/containers_model.h"
#include "ui/models/servers_model.h"
namespace
{
enum class ConfigTypes {
Amnezia,
OpenVpn,
WireGuard,
Awg,
Xray,
ShadowSocks,
Backup,
Invalid
};
}
class ImportController : public QObject
{
Q_OBJECT
public:
explicit ImportController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ContainersModel> &containersModel,
const std::shared_ptr<Settings> &settings, QObject *parent = nullptr);
public slots:
void importConfig();
void clearConfigFileName();
bool extractConfigFromFile(const QString &fileName);
bool extractConfigFromData(QString data);
bool extractConfigFromQr(const QByteArray &data);
QString getConfig();
QString getConfigFileName();
QString getMaliciousWarningText();
#if defined Q_OS_ANDROID || defined Q_OS_IOS
void startDecodingQr();
bool parseQrCodeChunk(const QString &code);
double getQrCodeScanProgressBarValue();
QString getQrCodeScanProgressString();
#endif
#if defined Q_OS_ANDROID
static bool decodeQrCode(const QString &code);
#endif
bool isNativeWireGuardConfig();
void processNativeWireGuardConfig();
signals:
void importFinished();
void importErrorOccurred(ErrorCode errorCode, bool goToPageHome);
void qrDecodingFinished();
void restoreAppConfig(const QByteArray &data);
private:
QJsonObject extractOpenVpnConfig(const QString &data);
QJsonObject extractWireGuardConfig(const QString &data);
QJsonObject extractXrayConfig(const QString &data, const QString &description = "");
void checkForMaliciousStrings(const QJsonObject &protocolConfig);
void processAmneziaConfig(QJsonObject &config);
#if defined Q_OS_ANDROID || defined Q_OS_IOS
void stopDecodingQr();
#endif
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ContainersModel> m_containersModel;
std::shared_ptr<Settings> m_settings;
QJsonObject m_config;
QString m_configFileName;
ConfigTypes m_configType;
QString m_maliciousWarningText;
#if defined Q_OS_ANDROID || defined Q_OS_IOS
QMap<int, QByteArray> m_qrCodeChunks;
bool m_isQrCodeProcessed;
int m_totalQrCodeChunksCount;
int m_receivedQrCodeChunksCount;
#endif
};
#endif // IMPORTCONTROLLER_H
@@ -0,0 +1,203 @@
#include "importUiController.h"
#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QMutex>
#include <QJsonDocument>
#include "systemController.h"
#ifdef Q_OS_ANDROID
#include "platforms/android/android_controller.h"
#endif
#if defined Q_OS_ANDROID
ImportUiController* ImportUiController::mInstance = nullptr;
static QMutex qrDecodeMutex;
#endif
ImportUiController::ImportUiController(ImportController* importController, QObject *parent)
: QObject(parent),
m_importController(importController),
m_isNativeWireGuardConfig(false)
{
#if defined Q_OS_ANDROID
mInstance = this;
#endif
connect(m_importController, &ImportController::importFinished, this, &ImportUiController::importFinished);
connect(m_importController, &ImportController::importErrorOccurred, this, &ImportUiController::importErrorOccurred);
connect(m_importController, &ImportController::restoreAppConfig, this, &ImportUiController::restoreAppConfig);
}
bool ImportUiController::extractConfigFromFile(const QString &fileName)
{
QString data;
if (!SystemController::readFile(fileName, data)) {
emit importErrorOccurred(ErrorCode::ImportOpenConfigError, false);
return false;
}
QString configFileName = QFileInfo(QFile(fileName).fileName()).fileName();
#ifdef Q_OS_ANDROID
if (configFileName.isEmpty()) {
configFileName = AndroidController::instance()->getFileName(fileName);
}
#endif
auto result = m_importController->extractConfigFromData(data, configFileName);
if (result.errorCode != ErrorCode::NoError) {
emit importErrorOccurred(result.errorCode, false);
return false;
}
m_config = result.config;
m_configFileName = result.configFileName;
m_maliciousWarningText = result.maliciousWarningText;
m_isNativeWireGuardConfig = result.isNativeWireGuardConfig;
emit importConfigChanged();
return true;
}
bool ImportUiController::extractConfigFromData(QString data)
{
auto result = m_importController->extractConfigFromData(data);
if (result.errorCode != ErrorCode::NoError) {
emit importErrorOccurred(result.errorCode, false);
return false;
}
m_config = result.config;
m_configFileName = result.configFileName;
m_maliciousWarningText = result.maliciousWarningText;
m_isNativeWireGuardConfig = result.isNativeWireGuardConfig;
emit importConfigChanged();
return true;
}
bool ImportUiController::extractConfigFromQr(const QByteArray &data)
{
auto result = m_importController->extractConfigFromQr(data);
if (result.errorCode != ErrorCode::NoError) {
emit importErrorOccurred(result.errorCode, false);
return false;
}
m_config = result.config;
m_configFileName = result.configFileName;
m_maliciousWarningText = result.maliciousWarningText;
m_isNativeWireGuardConfig = result.isNativeWireGuardConfig;
emit importConfigChanged();
return true;
}
QString ImportUiController::getConfig()
{
return QJsonDocument(m_config).toJson(QJsonDocument::Indented);
}
QString ImportUiController::getConfigFileName()
{
return m_configFileName;
}
QString ImportUiController::getMaliciousWarningText()
{
return m_maliciousWarningText;
}
bool ImportUiController::isNativeWireGuardConfig()
{
return m_isNativeWireGuardConfig;
}
void ImportUiController::processNativeWireGuardConfig()
{
m_config = m_importController->processNativeWireGuardConfig(m_config);
emit importConfigChanged();
}
void ImportUiController::importConfig()
{
m_importController->importConfig(m_config);
m_config = {};
m_configFileName.clear();
m_maliciousWarningText.clear();
m_isNativeWireGuardConfig = false;
emit importConfigChanged();
}
void ImportUiController::clearConfigFileName()
{
m_configFileName.clear();
emit importConfigChanged();
}
#if defined Q_OS_ANDROID || defined Q_OS_IOS
void ImportUiController::startDecodingQr()
{
m_importController->startDecodingQr();
#if defined Q_OS_ANDROID
AndroidController::instance()->startQrReaderActivity();
#endif
}
void ImportUiController::stopDecodingQr()
{
emit qrDecodingFinished();
}
bool ImportUiController::parseQrCodeChunk(const QString &code)
{
auto parseResult = m_importController->parseQrCodeChunk(code);
if (parseResult.success) {
m_config = parseResult.importResult.config;
m_configFileName = parseResult.importResult.configFileName;
m_maliciousWarningText = parseResult.importResult.maliciousWarningText;
m_isNativeWireGuardConfig = parseResult.importResult.isNativeWireGuardConfig;
emit importConfigChanged();
stopDecodingQr();
return true;
}
return false;
}
double ImportUiController::getQrCodeScanProgressBarValue()
{
const int total = m_importController->qrChunksTotal();
if (total == 0) {
return 0.0;
}
return (1.0 / total) * m_importController->qrChunksReceived();
}
QString ImportUiController::getQrCodeScanProgressString()
{
return tr("Scanned %1 of %2.").arg(m_importController->qrChunksReceived()).arg(m_importController->qrChunksTotal());
}
#endif
#if defined Q_OS_ANDROID
bool ImportUiController::decodeQrCode(const QString &code)
{
QMutexLocker lock(&qrDecodeMutex);
if (!mInstance) {
return false;
}
if (!mInstance->m_importController->isQrDecodingActive()) {
mInstance->m_importController->startDecodingQr();
}
return mInstance->parseQrCodeChunk(code);
}
#endif
@@ -0,0 +1,68 @@
#ifndef IMPORTUICONTROLLER_H
#define IMPORTUICONTROLLER_H
#include <QObject>
#include "core/controllers/selfhosted/importController.h"
class ImportUiController : public QObject
{
Q_OBJECT
Q_PROPERTY(QString config READ getConfig NOTIFY importConfigChanged)
Q_PROPERTY(QString configFileName READ getConfigFileName NOTIFY importConfigChanged)
Q_PROPERTY(QString maliciousWarningText READ getMaliciousWarningText NOTIFY importConfigChanged)
Q_PROPERTY(bool isNativeWireGuardConfig READ isNativeWireGuardConfig NOTIFY importConfigChanged)
public:
explicit ImportUiController(ImportController* importController, QObject *parent = nullptr);
public slots:
void importConfig();
void clearConfigFileName();
bool extractConfigFromFile(const QString &fileName);
bool extractConfigFromData(QString data);
bool extractConfigFromQr(const QByteArray &data);
QString getConfig();
QString getConfigFileName();
QString getMaliciousWarningText();
bool isNativeWireGuardConfig();
void processNativeWireGuardConfig();
#if defined Q_OS_ANDROID || defined Q_OS_IOS
void startDecodingQr();
bool parseQrCodeChunk(const QString &code);
double getQrCodeScanProgressBarValue();
QString getQrCodeScanProgressString();
#endif
#if defined Q_OS_ANDROID
static bool decodeQrCode(const QString &code);
#endif
signals:
void importFinished();
void importErrorOccurred(ErrorCode errorCode, bool goToPageHome);
void qrDecodingFinished();
void restoreAppConfig(const QByteArray &data);
void importConfigChanged();
private:
#if defined Q_OS_ANDROID || defined Q_OS_IOS
void stopDecodingQr();
#endif
ImportController* m_importController;
QJsonObject m_config;
QString m_configFileName;
QString m_maliciousWarningText;
bool m_isNativeWireGuardConfig;
#if defined Q_OS_ANDROID
static ImportUiController* mInstance;
#endif
};
#endif // IMPORTUICONTROLLER_H
File diff suppressed because it is too large Load Diff
-120
View File
@@ -1,120 +0,0 @@
#ifndef INSTALLCONTROLLER_H
#define INSTALLCONTROLLER_H
#include <QObject>
#include <QProcess>
#include "containers/containers_defs.h"
#include "core/defs.h"
#include "ui/models/clientManagementModel.h"
#include "ui/models/containers_model.h"
#include "ui/models/protocols_model.h"
#include "ui/models/servers_model.h"
class InstallController : public QObject
{
Q_OBJECT
public:
explicit InstallController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<ProtocolsModel> &protocolsModel,
const QSharedPointer<ClientManagementModel> &clientManagementModel,
const std::shared_ptr<Settings> &settings, QObject *parent = nullptr);
~InstallController();
public slots:
void install(DockerContainer container, int port, TransportProto transportProto);
void setProcessedServerCredentials(const QString &hostName, const QString &userName, const QString &secretData);
void setShouldCreateServer(bool shouldCreateServer);
void scanServerForInstalledContainers();
void updateContainer(QJsonObject config);
void removeProcessedServer();
void rebootProcessedServer();
void removeAllContainers();
void removeProcessedContainer();
void removeApiConfig(const int serverIndex);
void clearCachedProfile(QSharedPointer<ServerController> serverController = nullptr);
QRegularExpression ipAddressPortRegExp();
QRegularExpression ipAddressRegExp();
void mountSftpDrive(const QString &port, const QString &password, const QString &username);
bool checkSshConnection(QSharedPointer<ServerController> serverController = nullptr);
void setEncryptedPassphrase(QString passphrase);
void addEmptyServer();
void validateConfig();
signals:
void configValidated(bool isValid);
void installContainerFinished(const QString &finishMessage, bool isServiceInstall);
void installServerFinished(const QString &finishMessage);
void updateContainerFinished(const QString &message);
void scanServerFinished(bool isInstalledContainerFound);
void rebootProcessedServerFinished(const QString &finishedMessage);
void removeProcessedServerFinished(const QString &finishedMessage);
void removeAllContainersFinished(const QString &finishedMessage);
void removeProcessedContainerFinished(const QString &finishedMessage);
void installationErrorOccurred(ErrorCode errorCode);
void wrongInstallationUser(const QString &message);
void serverAlreadyExists(int serverIndex);
void passphraseRequestStarted();
void passphraseRequestFinished();
void serverIsBusy(const bool isBusy);
void cancelInstallation();
void currentContainerUpdated();
void cachedProfileCleared(const QString &message);
void apiConfigRemoved(const QString &message);
void noInstalledContainers();
void profileCleared(const QJsonObject &config);
private:
void installServer(const DockerContainer container, const QMap<DockerContainer, QJsonObject> &installedContainers,
const ServerCredentials &serverCredentials, const QSharedPointer<ServerController> &serverController,
QString &finishMessage);
void installContainer(const DockerContainer container, const QMap<DockerContainer, QJsonObject> &installedContainers,
const ServerCredentials &serverCredentials, const QSharedPointer<ServerController> &serverController,
QString &finishMessage);
bool isServerAlreadyExists();
ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController,
QMap<DockerContainer, QJsonObject> &installedContainers);
bool isUpdateDockerContainerRequired(const DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig);
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<ProtocolsModel> m_protocolModel;
QSharedPointer<ClientManagementModel> m_clientManagementModel;
std::shared_ptr<Settings> m_settings;
ServerCredentials m_processedServerCredentials;
bool m_shouldCreateServer;
QString m_privateKeyPassphrase;
#ifndef Q_OS_IOS
QList<QSharedPointer<QProcess>> m_sftpMountProcesses;
#endif
};
#endif // INSTALLCONTROLLER_H
@@ -0,0 +1,87 @@
#include "ipSplitTunnelingUiController.h"
#include "systemController.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
IpSplitTunnelingUiController::IpSplitTunnelingUiController(IpSplitTunnelingController* ipSplitTunnelingController,
IpSplitTunnelingModel* ipSplitTunnelingModel, QObject *parent)
: QObject(parent),
m_ipSplitTunnelingController(ipSplitTunnelingController),
m_ipSplitTunnelingModel(ipSplitTunnelingModel)
{
m_ipSplitTunnelingModel->updateModel(m_ipSplitTunnelingController->getCurrentSites());
}
void IpSplitTunnelingUiController::addSite(QString hostname)
{
if (m_ipSplitTunnelingController->addSite(hostname)) {
emit finished(tr("New site added: %1").arg(hostname));
}
}
void IpSplitTunnelingUiController::removeSite(int index)
{
auto modelIndex = m_ipSplitTunnelingModel->index(index);
auto hostname = m_ipSplitTunnelingModel->data(modelIndex, IpSplitTunnelingModel::Roles::UrlRole).toString();
if (m_ipSplitTunnelingController->removeSite(hostname)) {
emit finished(tr("Site removed: %1").arg(hostname));
}
}
void IpSplitTunnelingUiController::removeSites()
{
m_ipSplitTunnelingController->removeSites();
emit finished(tr("Site list cleared!"));
}
void IpSplitTunnelingUiController::importSites(const QString &fileName, bool replaceExisting)
{
QByteArray jsonData;
if (!SystemController::readFile(fileName, jsonData)) {
emit errorOccurred(tr("Can't open file: %1").arg(fileName));
return;
}
QString errorMessage;
if (m_ipSplitTunnelingController->importSitesFromJson(jsonData, replaceExisting, errorMessage)) {
emit finished(tr("Import completed"));
} else {
emit errorOccurred(errorMessage);
}
}
void IpSplitTunnelingUiController::exportSites(const QString &fileName)
{
QByteArray jsonData = m_ipSplitTunnelingController->exportSitesToJson();
SystemController::saveFile(fileName, QString::fromUtf8(jsonData));
emit finished(tr("Export completed"));
}
void IpSplitTunnelingUiController::toggleSplitTunneling(bool enabled)
{
m_ipSplitTunnelingController->toggleSplitTunneling(enabled);
emit isSplitTunnelingEnabledChanged();
}
void IpSplitTunnelingUiController::setRouteMode(int routeMode)
{
m_ipSplitTunnelingController->setRouteMode(static_cast<amnezia::RouteMode>(routeMode));
emit routeModeChanged();
}
int IpSplitTunnelingUiController::getRouteMode() const
{
return static_cast<int>(m_ipSplitTunnelingController->getRouteMode());
}
bool IpSplitTunnelingUiController::isSplitTunnelingEnabled() const
{
return m_ipSplitTunnelingController->isSplitTunnelingEnabled();
}
void IpSplitTunnelingUiController::updateModel()
{
m_ipSplitTunnelingModel->updateModel(m_ipSplitTunnelingController->getCurrentSites());
}
@@ -0,0 +1,46 @@
#ifndef IPSPLITTUNNELINGUICONTROLLER_H
#define IPSPLITTUNNELINGUICONTROLLER_H
#include <QObject>
#include "core/controllers/ipSplitTunnelingController.h"
#include "ui/models/ipSplitTunnelingModel.h"
class IpSplitTunnelingUiController : public QObject
{
Q_OBJECT
Q_PROPERTY(int routeMode READ getRouteMode WRITE setRouteMode NOTIFY routeModeChanged)
Q_PROPERTY(bool isSplitTunnelingEnabled READ isSplitTunnelingEnabled NOTIFY isSplitTunnelingEnabledChanged)
public:
explicit IpSplitTunnelingUiController(IpSplitTunnelingController* ipSplitTunnelingController,
IpSplitTunnelingModel* ipSplitTunnelingModel, QObject *parent = nullptr);
public slots:
void addSite(QString hostname);
void removeSite(int index);
void removeSites();
void importSites(const QString &fileName, bool replaceExisting);
void exportSites(const QString &fileName);
void toggleSplitTunneling(bool enabled);
void setRouteMode(int routeMode);
int getRouteMode() const;
bool isSplitTunnelingEnabled() const;
void updateModel();
signals:
void routeModeChanged();
void isSplitTunnelingEnabledChanged();
void errorOccurred(const QString &errorMessage);
void finished(const QString &message);
void saveFile(const QString &fileName, const QString &data);
private:
IpSplitTunnelingController* m_ipSplitTunnelingController;
IpSplitTunnelingModel* m_ipSplitTunnelingModel;
};
#endif // IPSPLITTUNNELINGUICONTROLLER_H
@@ -0,0 +1,125 @@
#include "languageUiController.h"
LanguageUiController::LanguageUiController(SettingsController* settingsController,
LanguageModel* languageModel,
QObject *parent)
: QObject(parent),
m_settingsController(settingsController),
m_languageModel(languageModel)
{
}
void LanguageUiController::onAppLanguageChanged(const QLocale &locale)
{
emit updateTranslations(locale);
emit translationsUpdated();
}
void LanguageUiController::changeLanguage(const LanguageSettings::AvailableLanguageEnum language)
{
QLocale locale = languageEnumToLocale(language);
m_settingsController->setAppLanguage(locale);
}
int LanguageUiController::getCurrentLanguageIndex() const
{
auto locale = m_settingsController->getAppLanguage();
switch (locale.language()) {
case QLocale::English: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::English); break;
case QLocale::Russian: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Russian); break;
case QLocale::Chinese: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::China_cn); break;
case QLocale::Ukrainian: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Ukrainian); break;
case QLocale::Persian: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Persian); break;
case QLocale::Arabic: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Arabic); break;
case QLocale::Burmese: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Burmese); break;
case QLocale::Urdu: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Urdu); break;
case QLocale::Hindi: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Hindi); break;
default: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::English); break;
}
}
int LanguageUiController::getLineHeightAppend() const
{
auto locale = m_settingsController->getAppLanguage();
switch (locale.language()) {
case QLocale::Burmese: return 10; break;
default: return 0; break;
}
}
QString LanguageUiController::getCurrentLanguageName() const
{
int index = getCurrentLanguageIndex();
return getLocalLanguageName(static_cast<LanguageSettings::AvailableLanguageEnum>(index));
}
LanguageSettings::AvailableLanguageEnum LanguageUiController::getSystemLanguageEnum() const
{
QLocale locale = QLocale::system();
switch (locale.language()) {
case QLocale::Russian: return LanguageSettings::AvailableLanguageEnum::Russian;
case QLocale::Chinese: return LanguageSettings::AvailableLanguageEnum::China_cn;
case QLocale::Ukrainian: return LanguageSettings::AvailableLanguageEnum::Ukrainian;
case QLocale::Persian: return LanguageSettings::AvailableLanguageEnum::Persian;
case QLocale::Arabic: return LanguageSettings::AvailableLanguageEnum::Arabic;
case QLocale::Burmese: return LanguageSettings::AvailableLanguageEnum::Burmese;
case QLocale::Urdu: return LanguageSettings::AvailableLanguageEnum::Urdu;
case QLocale::Hindi: return LanguageSettings::AvailableLanguageEnum::Hindi;
case QLocale::English: return LanguageSettings::AvailableLanguageEnum::English;
default: return LanguageSettings::AvailableLanguageEnum::English;
}
}
QString LanguageUiController::getCurrentSiteUrl(const QString &path) const
{
auto locale = m_settingsController->getAppLanguage();
if (locale.language() == QLocale::Russian) {
return "https://storage.googleapis.com/amnezia/amnezia.org" + (path.isEmpty() ? "" : (QString("?m-path=/%1").arg(path)));
}
return QString("https://amnezia.org") + (path.isEmpty() ? "" : (QString("/%1").arg(path)));
}
QString LanguageUiController::getCurrentDocsUrl(const QString &path) const
{
auto locale = m_settingsController->getAppLanguage();
if (locale.language() == QLocale::Russian) {
return "https://storage.googleapis.com/amnezia/docs" + (path.isEmpty() ? "" : (QString("?m-path=/%1").arg(path)));
}
return QString("https://docs.amnezia.org") + (path.isEmpty() ? "" : (QString("/%1").arg(path)));
}
QString LanguageUiController::getLocalLanguageName(const LanguageSettings::AvailableLanguageEnum language) const
{
QString strLanguage("");
switch (language) {
case LanguageSettings::AvailableLanguageEnum::English: strLanguage = "English"; break;
case LanguageSettings::AvailableLanguageEnum::Russian: strLanguage = "Русский"; break;
case LanguageSettings::AvailableLanguageEnum::Ukrainian: strLanguage = "Українська"; break;
case LanguageSettings::AvailableLanguageEnum::China_cn: strLanguage = "\347\256\200\344\275\223\344\270\255\346\226\207"; break;
case LanguageSettings::AvailableLanguageEnum::Persian: strLanguage = "فارسی"; break;
case LanguageSettings::AvailableLanguageEnum::Arabic: strLanguage = "العربية"; break;
case LanguageSettings::AvailableLanguageEnum::Burmese: strLanguage = "မြန်မာဘာသာ"; break;
case LanguageSettings::AvailableLanguageEnum::Urdu: strLanguage = "اُرْدُوْ"; break;
case LanguageSettings::AvailableLanguageEnum::Hindi: strLanguage = "हिन्दी"; break;
default: break;
}
return strLanguage;
}
QLocale LanguageUiController::languageEnumToLocale(const LanguageSettings::AvailableLanguageEnum language) const
{
switch (language) {
case LanguageSettings::AvailableLanguageEnum::English: return QLocale::English;
case LanguageSettings::AvailableLanguageEnum::Russian: return QLocale::Russian;
case LanguageSettings::AvailableLanguageEnum::China_cn: return QLocale::Chinese;
case LanguageSettings::AvailableLanguageEnum::Ukrainian: return QLocale::Ukrainian;
case LanguageSettings::AvailableLanguageEnum::Persian: return QLocale::Persian;
case LanguageSettings::AvailableLanguageEnum::Arabic: return QLocale::Arabic;
case LanguageSettings::AvailableLanguageEnum::Burmese: return QLocale::Burmese;
case LanguageSettings::AvailableLanguageEnum::Urdu: return QLocale::Urdu;
case LanguageSettings::AvailableLanguageEnum::Hindi: return QLocale::Hindi;
default: return QLocale::English;
}
}
@@ -0,0 +1,46 @@
#ifndef LANGUAGEUICONTROLLER_H
#define LANGUAGEUICONTROLLER_H
#include <QObject>
#include <QLocale>
#include "core/controllers/settingsController.h"
#include "ui/models/languageModel.h"
class LanguageUiController : public QObject
{
Q_OBJECT
Q_PROPERTY(QString currentLanguageName READ getCurrentLanguageName NOTIFY translationsUpdated)
Q_PROPERTY(int currentLanguageIndex READ getCurrentLanguageIndex NOTIFY translationsUpdated)
Q_PROPERTY(int lineHeightAppend READ getLineHeightAppend NOTIFY translationsUpdated)
public:
explicit LanguageUiController(SettingsController* settingsController,
LanguageModel* languageModel,
QObject *parent = nullptr);
public slots:
void changeLanguage(const LanguageSettings::AvailableLanguageEnum language);
void onAppLanguageChanged(const QLocale &locale);
int getCurrentLanguageIndex() const;
int getLineHeightAppend() const;
QString getCurrentLanguageName() const;
QString getCurrentSiteUrl(const QString &path = "") const;
QString getCurrentDocsUrl(const QString &path = "") const;
LanguageSettings::AvailableLanguageEnum getSystemLanguageEnum() const;
signals:
void updateTranslations(const QLocale &locale);
void translationsUpdated();
private:
QString getLocalLanguageName(const LanguageSettings::AvailableLanguageEnum language) const;
QLocale languageEnumToLocale(const LanguageSettings::AvailableLanguageEnum language) const;
SettingsController* m_settingsController;
LanguageModel* m_languageModel;
};
#endif // LANGUAGEUICONTROLLER_H
@@ -1,5 +1,5 @@
#include "focusController.h"
#include "utils/qmlUtils.h"
#include "ui/utils/qmlUtils.h"
#include <QQmlApplicationEngine>
#include <QQuickWindow>
@@ -1,7 +1,7 @@
#ifndef FOCUSCONTROLLER_H
#define FOCUSCONTROLLER_H
#include "ui/controllers/listViewFocusController.h"
#include "ui/controllers/qml/listViewFocusController.h"
#include <QQmlApplicationEngine>
@@ -1,5 +1,5 @@
#include "listViewFocusController.h"
#include "utils/qmlUtils.h"
#include "ui/utils/qmlUtils.h"
#include <QQuickWindow>
@@ -1,6 +1,7 @@
#include "pageController.h"
#include "utils/converter.h"
#include "core/errorstrings.h"
#include "ui/utils/converter.h"
#include "core/utils/errorStrings.h"
#if defined(MACOS_NE)
#include "platforms/ios/ios_controller.h"
#endif
@@ -15,16 +16,29 @@
#include "platforms/android/android_controller.h"
#endif
#if defined Q_OS_MAC
#include "ui/macos_util.h"
#include "ui/utils/macosUtil.h"
#endif
PageController::PageController(const QSharedPointer<ServersModel> &serversModel,
const std::shared_ptr<Settings> &settings, QObject *parent)
: QObject(parent), m_serversModel(serversModel), m_settings(settings)
PageController::PageController(ServersController* serversController,
SettingsController* settingsController,
QObject *parent)
: QObject(parent), m_serversController(serversController), m_settingsController(settingsController)
{
#ifdef Q_OS_ANDROID
auto initialPageNavigationBarColor = getInitialPageNavigationBarColor();
AndroidController::instance()->setNavigationBarColor(initialPageNavigationBarColor);
connect(AndroidController::instance(), &AndroidController::imeInsetsChanged, this, [this](int heightDp) {
m_imeHeight = heightDp;
emit imeHeightChanged(heightDp);
emit safeAreaBottomMarginChanged();
});
connect(AndroidController::instance(), &AndroidController::systemBarsInsetsChanged, this, [this](int navBarHeightDp, int statusBarHeightDp) {
m_cachedNavigationBarHeight = navBarHeightDp;
m_cachedStatusBarHeight = statusBarHeightDp;
emit safeAreaBottomMarginChanged();
emit safeAreaTopMarginChanged();
});
#endif
#if defined Q_OS_MACX
@@ -43,10 +57,9 @@ PageController::PageController(const QSharedPointer<ServersModel> &serversModel,
bool PageController::isStartPageVisible()
{
if (m_serversModel->getServersCount()) {
if (m_serversModel->getDefaultServerIndex() < 0) {
auto defaultServerIndex = m_serversModel->index(0);
m_serversModel->setData(defaultServerIndex, true, ServersModel::Roles::IsDefaultRole);
if (m_serversController->getServersCount()) {
if (m_serversController->getDefaultServerIndex() < 0) {
m_serversController->setDefaultServerIndex(0);
}
return false;
} else {
@@ -63,7 +76,6 @@ QString PageController::getPagePath(PageLoader::PageEnum page)
void PageController::closeWindow()
{
// On mobile platforms, quit app on close; on desktop, just hide window
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
qApp->quit();
#else
@@ -97,7 +109,7 @@ void PageController::keyPressEvent(Qt::Key key)
unsigned int PageController::getInitialPageNavigationBarColor()
{
if (m_serversModel->getServersCount()) {
if (m_serversController->getServersCount()) {
return 0xFF1C1D21;
} else {
return 0xFF0E0E11;
@@ -113,7 +125,7 @@ void PageController::updateNavigationBarColor(const int color)
void PageController::showOnStartup()
{
if (!m_settings->isStartMinimized()) {
if (!m_settingsController->isStartMinimizedEnabled()) {
emit raiseMainWindow();
} else {
#if defined(Q_OS_WIN) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
@@ -165,6 +177,76 @@ int PageController::decrementDrawerDepth()
}
}
bool PageController::isEdgeToEdgeEnabled()
{
#ifdef Q_OS_ANDROID
if (!m_edgeToEdgeCached) {
m_cachedEdgeToEdgeEnabled = AndroidController::instance()->isEdgeToEdgeEnabled();
m_edgeToEdgeCached = true;
}
return m_cachedEdgeToEdgeEnabled;
#else
return false;
#endif
}
int PageController::getStatusBarHeight()
{
#ifdef Q_OS_ANDROID
if (m_cachedStatusBarHeight < 0) {
m_cachedStatusBarHeight = AndroidController::instance()->getStatusBarHeight();
}
return m_cachedStatusBarHeight;
#else
return 0;
#endif
}
int PageController::getNavigationBarHeight()
{
#ifdef Q_OS_ANDROID
if (m_cachedNavigationBarHeight < 0) {
m_cachedNavigationBarHeight = AndroidController::instance()->getNavigationBarHeight();
}
return m_cachedNavigationBarHeight;
#else
return 0;
#endif
}
int PageController::getSafeAreaTopMargin()
{
#ifdef Q_OS_ANDROID
if (isEdgeToEdgeEnabled()) {
int height = getStatusBarHeight();
int result = height > 0 ? height : 40;
return result;
}
#endif
return 0;
}
int PageController::getSafeAreaBottomMargin()
{
#ifdef Q_OS_ANDROID
if (isEdgeToEdgeEnabled()) {
if (m_imeHeight > 0) {
return 0;
}
int height = getNavigationBarHeight();
int result = height > 0 ? height : 56;
return result;
}
#endif
return 0;
}
int PageController::getImeHeight()
{
return m_imeHeight;
}
void PageController::onShowErrorMessage(ErrorCode errorCode)
{
const auto fullErrorMessage = errorString(errorCode);
@@ -4,8 +4,11 @@
#include <QObject>
#include <QQmlEngine>
#include "core/defs.h"
#include "ui/models/servers_model.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/controllers/settingsController.h"
#include "core/controllers/serversController.h"
namespace PageLoader
{
@@ -62,8 +65,6 @@ namespace PageLoader
PageSetupWizardApiFreeInfo,
PageProtocolOpenVpnSettings,
PageProtocolShadowSocksSettings,
PageProtocolCloakSettings,
PageProtocolXraySettings,
PageProtocolWireGuardSettings,
PageProtocolAwgSettings,
@@ -93,9 +94,14 @@ class PageController : public QObject
{
Q_OBJECT
public:
explicit PageController(const QSharedPointer<ServersModel> &serversModel, const std::shared_ptr<Settings> &settings,
explicit PageController(ServersController* serversController,
SettingsController* settingsController,
QObject *parent = nullptr);
Q_PROPERTY(int safeAreaTopMargin READ getSafeAreaTopMargin NOTIFY safeAreaTopMarginChanged)
Q_PROPERTY(int safeAreaBottomMargin READ getSafeAreaBottomMargin NOTIFY safeAreaBottomMarginChanged)
Q_PROPERTY(int imeHeight READ getImeHeight NOTIFY imeHeightChanged)
public slots:
bool isStartPageVisible();
QString getPagePath(PageLoader::PageEnum page);
@@ -119,6 +125,13 @@ public slots:
int incrementDrawerDepth();
int decrementDrawerDepth();
bool isEdgeToEdgeEnabled();
int getStatusBarHeight();
int getNavigationBarHeight();
int getSafeAreaTopMargin();
int getSafeAreaBottomMargin();
int getImeHeight();
private slots:
void onShowErrorMessage(amnezia::ErrorCode errorCode);
@@ -154,14 +167,23 @@ signals:
void escapePressed();
void closeTopDrawer();
private:
QSharedPointer<ServersModel> m_serversModel;
void imeHeightChanged(int height);
void safeAreaTopMarginChanged();
void safeAreaBottomMarginChanged();
std::shared_ptr<Settings> m_settings;
private:
ServersController* m_serversController;
SettingsController* m_settingsController;
bool m_isTriggeredByConnectButton;
int m_drawerDepth = 0;
mutable int m_cachedStatusBarHeight = -1;
mutable int m_cachedNavigationBarHeight = -1;
mutable bool m_cachedEdgeToEdgeEnabled = false;
mutable bool m_edgeToEdgeCached = false;
int m_imeHeight = 0;
};
#endif // PAGECONTROLLER_H
#endif
@@ -0,0 +1,116 @@
#include "exportUiController.h"
#include "../systemController.h"
ExportUiController::ExportUiController(ExportController* exportController, QObject *parent)
: QObject(parent),
m_exportController(exportController)
{
}
void ExportUiController::generateFullAccessConfig(int serverIndex)
{
clearPreviousConfig();
auto result = m_exportController->generateFullAccessConfig(serverIndex);
applyExportResult(result);
}
void ExportUiController::generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName)
{
clearPreviousConfig();
auto result = m_exportController->generateConnectionConfig(serverIndex, containerIndex, clientName);
applyExportResult(result);
}
void ExportUiController::generateOpenVpnConfig(int serverIndex, const QString &clientName)
{
clearPreviousConfig();
auto result = m_exportController->generateOpenVpnConfig(serverIndex, clientName);
applyExportResult(result);
}
void ExportUiController::generateWireGuardConfig(int serverIndex, const QString &clientName)
{
clearPreviousConfig();
auto result = m_exportController->generateWireGuardConfig(serverIndex, clientName);
applyExportResult(result);
}
void ExportUiController::generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName)
{
clearPreviousConfig();
auto result = m_exportController->generateAwgConfig(serverIndex, containerIndex, clientName);
applyExportResult(result);
}
void ExportUiController::generateXrayConfig(int serverIndex, const QString &clientName)
{
clearPreviousConfig();
auto result = m_exportController->generateXrayConfig(serverIndex, clientName);
applyExportResult(result);
}
QString ExportUiController::getConfig()
{
return m_config;
}
QString ExportUiController::getNativeConfigString()
{
return m_nativeConfigString;
}
QList<QString> ExportUiController::getQrCodes()
{
return m_qrCodes;
}
void ExportUiController::exportConfig(const QString &fileName)
{
SystemController::saveFile(fileName, m_config);
}
void ExportUiController::updateClientManagementModel(int serverIndex, int containerIndex)
{
m_exportController->updateClientManagementModel(serverIndex, containerIndex);
}
void ExportUiController::revokeConfig(int row, int serverIndex, int containerIndex)
{
m_exportController->revokeConfig(row, serverIndex, containerIndex);
emit revokeConfigFinished();
}
void ExportUiController::renameClient(int row, const QString &clientName, int serverIndex, int containerIndex)
{
m_exportController->renameClient(row, clientName, serverIndex, containerIndex);
}
int ExportUiController::getQrCodesCount()
{
return m_qrCodes.size();
}
void ExportUiController::clearPreviousConfig()
{
m_config.clear();
m_nativeConfigString.clear();
m_qrCodes.clear();
emit exportConfigChanged();
}
void ExportUiController::applyExportResult(const ExportController::ExportResult &result)
{
if (result.errorCode != ErrorCode::NoError) {
emit exportErrorOccurred(result.errorCode);
return;
}
m_config = result.config;
m_nativeConfigString = result.nativeConfigString;
m_qrCodes = result.qrCodes;
emit exportConfigChanged();
}
@@ -0,0 +1,59 @@
#ifndef EXPORTUICONTROLLER_H
#define EXPORTUICONTROLLER_H
#include <QObject>
#include "core/controllers/selfhosted/exportController.h"
class ExportUiController : public QObject
{
Q_OBJECT
public:
explicit ExportUiController(ExportController* exportController, QObject *parent = nullptr);
Q_PROPERTY(QList<QString> qrCodes READ getQrCodes NOTIFY exportConfigChanged)
Q_PROPERTY(int qrCodesCount READ getQrCodesCount NOTIFY exportConfigChanged)
Q_PROPERTY(QString config READ getConfig NOTIFY exportConfigChanged)
Q_PROPERTY(QString nativeConfigString READ getNativeConfigString NOTIFY exportConfigChanged)
public slots:
void generateFullAccessConfig(int serverIndex);
void generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName);
void generateOpenVpnConfig(int serverIndex, const QString &clientName);
void generateWireGuardConfig(int serverIndex, const QString &clientName);
void generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName);
void generateXrayConfig(int serverIndex, const QString &clientName);
QString getConfig();
QString getNativeConfigString();
QList<QString> getQrCodes();
void exportConfig(const QString &fileName);
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);
signals:
void generateConfig(int type);
void revokeConfigFinished();
void exportErrorOccurred(const QString &errorMessage);
void exportErrorOccurred(ErrorCode errorCode);
void exportConfigChanged();
void saveFile(const QString &fileName, const QString &data);
private:
int getQrCodesCount();
void clearPreviousConfig();
void applyExportResult(const ExportController::ExportResult &result);
ExportController* m_exportController;
QString m_config;
QString m_nativeConfigString;
QList<QString> m_qrCodes;
};
#endif // EXPORTUICONTROLLER_H
+493
View File
@@ -0,0 +1,493 @@
#include "installUiController.h"
#include <QDesktopServices>
#include <QDir>
#include <QEventLoop>
#include <QJsonObject>
#include <QRandomGenerator>
#include <QStandardPaths>
#include "core/utils/api/apiUtils.h"
#include "core/controllers/selfhosted/installController.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/utils/networkUtilities.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 "ui/models/protocols/awgConfigModel.h"
#include "ui/models/protocols/wireguardConfigModel.h"
#include "ui/models/protocols/openvpnConfigModel.h"
#include "ui/models/protocols/xrayConfigModel.h"
#ifdef Q_OS_WINDOWS
#include "ui/models/protocols/ikev2ConfigModel.h"
#endif
#include "ui/models/services/sftpConfigModel.h"
#include "ui/models/services/socks5ProxyConfigModel.h"
#include "ui/models/services/torConfigModel.h"
#include "core/utils/utilities.h"
#include "core/models/serverConfig.h"
#include "core/models/containerConfig.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"
namespace
{
Logger logger("InstallUiController");
namespace configKey
{
constexpr char serviceInfo[] = "service_info";
constexpr char serviceType[] = "service_type";
constexpr char serviceProtocol[] = "service_protocol";
constexpr char userCountryCode[] = "user_country_code";
constexpr char serverCountryCode[] = "server_country_code";
constexpr char serverCountryName[] = "server_country_name";
constexpr char availableCountries[] = "available_countries";
constexpr char apiConfig[] = "api_config";
constexpr char authData[] = "auth_data";
}
}
InstallUiController::InstallUiController(InstallController *installController,
ServersController *serversController,
SettingsController *settingsController,
ProtocolsModel *protocolsModel,
UsersController *usersController,
AwgConfigModel *awgConfigModel,
WireGuardConfigModel *wireGuardConfigModel,
OpenVpnConfigModel *openVpnConfigModel,
XrayConfigModel *xrayConfigModel,
TorConfigModel *torConfigModel,
#ifdef Q_OS_WINDOWS
Ikev2ConfigModel *ikev2ConfigModel,
#endif
SftpConfigModel *sftpConfigModel,
Socks5ProxyConfigModel *socks5ConfigModel,
QObject *parent)
: QObject(parent),
m_installController(installController),
m_serversController(serversController),
m_settingsController(settingsController),
m_protocolModel(protocolsModel),
m_usersController(usersController),
m_awgConfigModel(awgConfigModel),
m_wireGuardConfigModel(wireGuardConfigModel),
m_openVpnConfigModel(openVpnConfigModel),
m_xrayConfigModel(xrayConfigModel),
m_torConfigModel(torConfigModel),
#ifdef Q_OS_WINDOWS
m_ikev2ConfigModel(ikev2ConfigModel),
#endif
m_sftpConfigModel(sftpConfigModel),
m_socks5ConfigModel(socks5ConfigModel)
{
connect(m_installController, &InstallController::configValidated, this, &InstallUiController::configValidated);
connect(m_installController, &InstallController::validationErrorOccurred, this, [this](ErrorCode errorCode) {
if (errorCode == ErrorCode::NoInstalledContainersError) {
emit noInstalledContainers();
} else {
emit installationErrorOccurred(errorCode);
}
});
}
InstallUiController::~InstallUiController()
{
}
void InstallUiController::install(DockerContainer container, int port, TransportProto transportProto, int serverIndex)
{
const bool isNewServer = serverIndex < 0;
ServerCredentials serverCredentials;
if (isNewServer) {
serverCredentials = m_processedServerCredentials;
} else {
serverCredentials = m_serversController->getServerCredentials(serverIndex);
m_processedServerCredentials = ServerCredentials();
}
QMap<DockerContainer, QJsonObject> preparedContainers;
QString finishMessage;
ErrorCode errorCode;
if (isNewServer) {
int existingServerIndex = -1;
if (m_installController->isServerAlreadyExists(serverCredentials, existingServerIndex)) {
emit serverAlreadyExists(existingServerIndex);
return;
}
bool wasContainerInstalled = false;
errorCode = m_installController->installServer(serverCredentials, container, port, transportProto, wasContainerInstalled);
if (errorCode) {
emit installationErrorOccurred(errorCode);
return;
}
int serverIndex = m_serversController->getServersCount() - 1;
ServerConfig serverConfig = m_serversController->getServerConfig(serverIndex);
QMap<DockerContainer, ContainerConfig> containers = serverConfig.containers();
int containersCount = containers.size();
if (wasContainerInstalled) {
finishMessage = tr("%1 installed successfully. ").arg(ContainerUtils::containerHumanNames().value(container));
} else {
finishMessage = tr("%1 is already installed on the server. ").arg(ContainerUtils::containerHumanNames().value(container));
}
if (containersCount > 1) {
finishMessage += tr("\nAdded containers that were already installed on the server");
}
emit installServerFinished(finishMessage);
} else {
ServerConfig serverConfig = m_serversController->getServerConfig(serverIndex);
QMap<DockerContainer, ContainerConfig> containers = serverConfig.containers();
int containersCount = containers.size();
bool wasContainerInstalled = false;
errorCode = m_installController->installContainer(serverIndex, container, port, transportProto,
wasContainerInstalled);
if (errorCode) {
emit installationErrorOccurred(errorCode);
return;
}
ServerConfig newServerConfig = m_serversController->getServerConfig(serverIndex);
QMap<DockerContainer, ContainerConfig> newContainers = newServerConfig.containers();
int newContainersCount = newContainers.size();
bool hasNewContainers = (newContainersCount - containersCount) > (wasContainerInstalled ? 1 : 0);
if (wasContainerInstalled) {
finishMessage = tr("%1 installed successfully. ").arg(ContainerUtils::containerHumanNames().value(container));
} else {
finishMessage = tr("%1 is already installed on the server. ").arg(ContainerUtils::containerHumanNames().value(container));
}
if (hasNewContainers) {
finishMessage += tr("\nAlready installed containers were found on the server. "
"All installed containers have been added to the application");
}
emit installContainerFinished(finishMessage, ContainerUtils::containerService(container) == ServiceType::Other);
}
}
void InstallUiController::scanServerForInstalledContainers(int serverIndex)
{
ServerConfig serverBefore = m_serversController->getServerConfig(serverIndex);
QMap<DockerContainer, ContainerConfig> containersBefore = serverBefore.containers();
int containersCountBefore = containersBefore.size();
ErrorCode errorCode = m_installController->scanServerForInstalledContainers(serverIndex);
if (errorCode == ErrorCode::NoError) {
ServerConfig serverAfter = m_serversController->getServerConfig(serverIndex);
QMap<DockerContainer, ContainerConfig> containersAfter = serverAfter.containers();
int containersCountAfter = containersAfter.size();
bool isInstalledContainerAdded = containersCountAfter > containersCountBefore;
emit scanServerFinished(isInstalledContainerAdded);
return;
}
emit installationErrorOccurred(errorCode);
}
void InstallUiController::updateContainer(int serverIndex, int containerIndex, int protocolIndex)
{
DockerContainer container = static_cast<DockerContainer>(containerIndex);
Proto protocolType = static_cast<Proto>(protocolIndex);
ContainerConfig containerConfig;
containerConfig.container = container;
switch (protocolType) {
case Proto::Awg: {
containerConfig.protocolConfig = m_awgConfigModel->getProtocolConfig();
break;
}
case Proto::WireGuard: {
containerConfig.protocolConfig = m_wireGuardConfigModel->getProtocolConfig();
break;
}
case Proto::OpenVpn: {
containerConfig.protocolConfig = m_openVpnConfigModel->getProtocolConfig();
break;
}
case Proto::Xray:
case Proto::SSXray: {
containerConfig.protocolConfig = m_xrayConfigModel->getProtocolConfig();
break;
}
case Proto::TorWebSite: {
containerConfig.protocolConfig = m_torConfigModel->getProtocolConfig();
break;
}
case Proto::Sftp: {
containerConfig.protocolConfig = m_sftpConfigModel->getProtocolConfig();
break;
}
case Proto::Socks5Proxy: {
containerConfig.protocolConfig = m_socks5ConfigModel->getProtocolConfig();
break;
}
#ifdef Q_OS_WINDOWS
case Proto::Ikev2: {
containerConfig.protocolConfig = m_ikev2ConfigModel->getProtocolConfig();
break;
}
#endif
default:
return;
}
ContainerConfig oldContainerConfig = m_serversController->getContainerConfig(serverIndex, container);
ErrorCode errorCode = m_installController->updateContainer(serverIndex, container, oldContainerConfig, containerConfig);
if (errorCode == ErrorCode::NoError) {
ContainerConfig updatedConfig = m_serversController->getContainerConfig(serverIndex, container);
m_protocolModel->updateModel(updatedConfig);
auto defaultContainer = m_serversController->getServerConfig(serverIndex).defaultContainer();
if ((serverIndex == m_serversController->getDefaultServerIndex()) && (container == defaultContainer)) {
emit currentContainerUpdated();
} else {
emit updateContainerFinished(tr("Settings updated successfully"));
}
return;
}
emit installationErrorOccurred(errorCode);
}
void InstallUiController::rebootServer(int serverIndex)
{
QString serverName = m_serversController->getServerConfig(serverIndex).displayName();
const auto errorCode = m_installController->rebootServer(serverIndex);
if (errorCode == ErrorCode::NoError) {
emit rebootServerFinished(tr("Server '%1' was rebooted").arg(serverName));
} else {
emit installationErrorOccurred(errorCode);
}
}
void InstallUiController::removeServer(int serverIndex)
{
QString serverName = m_serversController->getServerConfig(serverIndex).displayName();
m_serversController->removeServer(serverIndex);
emit removeServerFinished(tr("Server '%1' was removed").arg(serverName));
}
void InstallUiController::removeAllContainers(int serverIndex)
{
QString serverName = m_serversController->getServerConfig(serverIndex).displayName();
ErrorCode errorCode = m_installController->removeAllContainers(serverIndex);
if (errorCode == ErrorCode::NoError) {
emit removeAllContainersFinished(tr("All containers from server '%1' have been removed").arg(serverName));
return;
}
emit installationErrorOccurred(errorCode);
}
void InstallUiController::removeContainer(int serverIndex, int containerIndex)
{
QString serverName = m_serversController->getServerConfig(serverIndex).displayName();
DockerContainer container = static_cast<DockerContainer>(containerIndex);
QString containerName = ContainerUtils::containerHumanNames().value(container);
ErrorCode errorCode = m_installController->removeContainer(serverIndex, container);
if (errorCode == ErrorCode::NoError) {
emit removeContainerFinished(tr("%1 has been removed from the server '%2'").arg(containerName, serverName));
return;
}
emit installationErrorOccurred(errorCode);
}
void InstallUiController::clearCachedProfile(int serverIndex, int containerIndex)
{
DockerContainer container = static_cast<DockerContainer>(containerIndex);
if (ContainerUtils::containerService(container) == ServiceType::Other) {
return;
}
m_installController->clearCachedProfile(serverIndex, container);
emit cachedProfileCleared(tr("%1 cached profile cleared").arg(ContainerUtils::containerHumanNames().value(container)));
ContainerConfig updatedConfig = m_serversController->getContainerConfig(serverIndex, container);
m_protocolModel->updateModel(updatedConfig);
}
QRegularExpression InstallUiController::ipAddressRegExp()
{
return NetworkUtilities::ipAddressRegExp();
}
void InstallUiController::clearProcessedServerCredentials()
{
m_processedServerCredentials = ServerCredentials();
}
void InstallUiController::setProcessedServerCredentials(const QString &hostName, const QString &userName, const QString &secretData)
{
m_processedServerCredentials.hostName = hostName;
if (m_processedServerCredentials.hostName.contains(":")) {
m_processedServerCredentials.port = m_processedServerCredentials.hostName.split(":").at(1).toInt();
m_processedServerCredentials.hostName = m_processedServerCredentials.hostName.split(":").at(0);
}
m_processedServerCredentials.userName = userName;
m_processedServerCredentials.secretData = secretData;
}
void InstallUiController::mountSftpDrive(int serverIndex, const QString &port, const QString &password, const QString &username)
{
ServerCredentials serverCredentials = m_serversController->getServerCredentials(serverIndex);
ErrorCode errorCode = m_installController->mountSftpDrive(serverCredentials, port, password, username);
if (errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(errorCode);
}
}
bool InstallUiController::checkSshConnection()
{
m_privateKeyPassphrase = "";
auto passphraseCallback = [this]() {
emit passphraseRequestStarted();
QEventLoop loop;
QObject::connect(this, &InstallUiController::passphraseRequestFinished, &loop, &QEventLoop::quit);
loop.exec();
return m_privateKeyPassphrase;
};
QString output;
ErrorCode errorCode = m_installController->checkSshConnection(m_processedServerCredentials, output, passphraseCallback);
if (errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(errorCode);
return false;
} else {
if (output.contains(tr("Please login as the user"))) {
output.replace("\n", "");
emit wrongInstallationUser(output);
return false;
}
}
return true;
}
void InstallUiController::setEncryptedPassphrase(QString passphrase)
{
m_privateKeyPassphrase = passphrase;
emit passphraseRequestFinished();
}
void InstallUiController::addEmptyServer()
{
SelfHostedServerConfig serverConfig;
serverConfig.hostName = m_processedServerCredentials.hostName;
serverConfig.userName = m_processedServerCredentials.userName;
serverConfig.password = m_processedServerCredentials.secretData;
serverConfig.port = m_processedServerCredentials.port;
serverConfig.description = m_settingsController->nextAvailableServerName();
serverConfig.defaultContainer = DockerContainer::None;
m_serversController->addServer(ServerConfig(serverConfig));
emit installServerFinished(tr("Server added successfully"));
}
void InstallUiController::validateConfig()
{
int serverIndex = m_serversController->getDefaultServerIndex();
m_installController->validateConfig(serverIndex);
}
void InstallUiController::updateProtocols(int serverIndex, int containerIndex)
{
DockerContainer container = static_cast<DockerContainer>(containerIndex);
ContainerConfig containerConfig = m_serversController->getContainerConfig(serverIndex, container);
containerConfig.container = container;
m_protocolModel->updateModel(containerConfig);
}
void InstallUiController::openServerSettings(int serverIndex, int containerIndex, int protocolIndex)
{
updateProtocolConfigModel(serverIndex, containerIndex, protocolIndex);
}
void InstallUiController::openClientSettings(int serverIndex, int containerIndex, int protocolIndex)
{
updateProtocolConfigModel(serverIndex, containerIndex, protocolIndex);
}
int InstallUiController::defaultPort(int protocolIndex)
{
Proto proto = static_cast<Proto>(protocolIndex);
return ProtocolUtils::defaultPort(proto);
}
int InstallUiController::getPortForInstall(int protocolIndex)
{
Proto proto = static_cast<Proto>(protocolIndex);
return ProtocolUtils::getPortForInstall(proto);
}
int InstallUiController::defaultTransportProto(int protocolIndex)
{
Proto proto = static_cast<Proto>(protocolIndex);
return static_cast<int>(ProtocolUtils::defaultTransportProto(proto));
}
bool InstallUiController::defaultPortChangeable(int protocolIndex)
{
Proto proto = static_cast<Proto>(protocolIndex);
return ProtocolUtils::defaultPortChangeable(proto);
}
bool InstallUiController::defaultTransportProtoChangeable(int protocolIndex)
{
Proto proto = static_cast<Proto>(protocolIndex);
return ProtocolUtils::defaultTransportProtoChangeable(proto);
}
void InstallUiController::updateProtocolConfigModel(int serverIndex, int containerIndex, int protocolIndex)
{
DockerContainer container = static_cast<DockerContainer>(containerIndex);
ContainerConfig containerConfig = m_serversController->getContainerConfig(serverIndex, container);
containerConfig.container = container;
Proto protocolType = static_cast<Proto>(protocolIndex);
auto updateIfPresent = [&](auto* model, auto* config) {
if (model && config) model->updateModel(container, *config);
};
switch (protocolType) {
case Proto::Awg: updateIfPresent(m_awgConfigModel, containerConfig.getAwgProtocolConfig()); break;
case Proto::WireGuard: updateIfPresent(m_wireGuardConfigModel, containerConfig.getWireGuardProtocolConfig()); break;
case Proto::OpenVpn: updateIfPresent(m_openVpnConfigModel, containerConfig.getOpenVpnProtocolConfig()); break;
case Proto::Xray: updateIfPresent(m_xrayConfigModel, containerConfig.getXrayProtocolConfig()); break;
case Proto::TorWebSite: updateIfPresent(m_torConfigModel, containerConfig.getTorProtocolConfig()); break;
case Proto::Sftp: updateIfPresent(m_sftpConfigModel, containerConfig.getSftpProtocolConfig()); break;
case Proto::Socks5Proxy: updateIfPresent(m_socks5ConfigModel, containerConfig.getSocks5ProxyProtocolConfig()); break;
#ifdef Q_OS_WINDOWS
case Proto::Ikev2: updateIfPresent(m_ikev2ConfigModel, containerConfig.getIkev2ProtocolConfig()); break;
#endif
default: break;
}
}
@@ -0,0 +1,151 @@
#ifndef INSTALLUICONTROLLER_H
#define INSTALLUICONTROLLER_H
#include <QObject>
#include <QProcess>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/controllers/serversController.h"
#include "core/controllers/settingsController.h"
#include "core/controllers/selfhosted/usersController.h"
#include "core/controllers/selfhosted/installController.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "core/models/containerConfig.h"
#include "ui/models/protocolsModel.h"
#include "ui/models/protocols/awgConfigModel.h"
#include "ui/models/protocols/wireguardConfigModel.h"
#include "ui/models/protocols/openvpnConfigModel.h"
#include "ui/models/protocols/xrayConfigModel.h"
#ifdef Q_OS_WINDOWS
#include "ui/models/protocols/ikev2ConfigModel.h"
#endif
#include "ui/models/services/sftpConfigModel.h"
#include "ui/models/services/socks5ProxyConfigModel.h"
#include "ui/models/services/torConfigModel.h"
#include "core/models/protocols/sftpProtocolConfig.h"
#include "core/models/protocols/socks5ProxyProtocolConfig.h"
class InstallUiController : public QObject
{
Q_OBJECT
public:
explicit InstallUiController(InstallController* installController,
ServersController* serversController,
SettingsController* settingsController,
ProtocolsModel* protocolsModel,
UsersController* usersController,
AwgConfigModel* awgConfigModel,
WireGuardConfigModel* wireGuardConfigModel,
OpenVpnConfigModel* openVpnConfigModel,
XrayConfigModel* xrayConfigModel,
TorConfigModel* torConfigModel,
#ifdef Q_OS_WINDOWS
Ikev2ConfigModel* ikev2ConfigModel,
#endif
SftpConfigModel* sftpConfigModel,
Socks5ProxyConfigModel* socks5ConfigModel,
QObject *parent = nullptr);
~InstallUiController();
public slots:
void install(DockerContainer container, int port, TransportProto transportProto, int serverIndex);
void setProcessedServerCredentials(const QString &hostName, const QString &userName, const QString &secretData);
void clearProcessedServerCredentials();
void scanServerForInstalledContainers(int serverIndex);
void updateContainer(int serverIndex, int containerIndex, int protocolIndex);
void removeServer(int serverIndex);
void rebootServer(int serverIndex);
void removeAllContainers(int serverIndex);
void removeContainer(int serverIndex, int containerIndex);
void clearCachedProfile(int serverIndex, int containerIndex);
QRegularExpression ipAddressRegExp();
void mountSftpDrive(int serverIndex, const QString &port, const QString &password, const QString &username);
bool checkSshConnection();
void setEncryptedPassphrase(QString passphrase);
void addEmptyServer();
void validateConfig();
Q_INVOKABLE void updateProtocols(int serverIndex, int containerIndex);
void openServerSettings(int serverIndex, int containerIndex, int protocolIndex);
void openClientSettings(int serverIndex, int containerIndex, int protocolIndex);
int defaultPort(int protocolIndex);
int getPortForInstall(int protocolIndex);
int defaultTransportProto(int protocolIndex);
bool defaultPortChangeable(int protocolIndex);
bool defaultTransportProtoChangeable(int protocolIndex);
signals:
void installContainerFinished(const QString &finishMessage, bool isServiceInstall);
void installServerFinished(const QString &finishMessage);
void updateContainerFinished(const QString &message);
void scanServerFinished(bool isInstalledContainerFound);
void rebootServerFinished(const QString &finishedMessage);
void removeServerFinished(const QString &finishedMessage);
void removeAllContainersFinished(const QString &finishedMessage);
void removeContainerFinished(const QString &finishedMessage);
void installationErrorOccurred(ErrorCode errorCode);
void wrongInstallationUser(const QString &message);
void serverAlreadyExists(int serverIndex);
void passphraseRequestStarted();
void passphraseRequestFinished();
void serverIsBusy(const bool isBusy);
void cancelInstallation();
void currentContainerUpdated();
void cachedProfileCleared(const QString &message);
void apiConfigRemoved(const QString &message);
void noInstalledContainers();
void configValidated(bool isValid);
private:
InstallController* m_installController;
ServersController* m_serversController;
SettingsController* m_settingsController;
ProtocolsModel* m_protocolModel;
UsersController* m_usersController;
AwgConfigModel* m_awgConfigModel;
WireGuardConfigModel* m_wireGuardConfigModel;
OpenVpnConfigModel* m_openVpnConfigModel;
XrayConfigModel* m_xrayConfigModel;
TorConfigModel* m_torConfigModel;
#ifdef Q_OS_WINDOWS
Ikev2ConfigModel* m_ikev2ConfigModel;
#endif
SftpConfigModel* m_sftpConfigModel;
Socks5ProxyConfigModel* m_socks5ConfigModel;
ServerCredentials m_processedServerCredentials;
QString m_privateKeyPassphrase;
void updateProtocolConfigModel(int serverIndex, int containerIndex, int protocolIndex);
};
#endif // INSTALLUICONTROLLER_H
@@ -0,0 +1,491 @@
#include "serversUiController.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/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 <QJsonDocument>
#include <QJsonArray>
#include "core/models/serverConfig.h"
#include "core/models/protocolConfig.h"
#include "core/models/containerConfig.h"
#include "core/models/protocols/awgProtocolConfig.h"
using namespace amnezia;
namespace
{
namespace configKey
{
constexpr char apiConfig[] = "api_config";
constexpr char serverCountryCode[] = "server_country_code";
constexpr char serverCountryName[] = "server_country_name";
constexpr char userCountryCode[] = "user_country_code";
constexpr char serviceType[] = "service_type";
}
}
ServersUiController::ServersUiController(ServersController* serversController,
SettingsController* settingsController,
ServersModel* serversModel,
ContainersModel* containersModel,
ContainersModel* defaultServerContainersModel,
QObject *parent)
: QObject(parent),
m_serversController(serversController),
m_settingsController(settingsController),
m_serversModel(serversModel),
m_containersModel(containersModel),
m_defaultServerContainersModel(defaultServerContainersModel)
{
}
void ServersUiController::removeServer(int index)
{
m_serversController->removeServer(index);
updateModel();
}
void ServersUiController::editServerName(int index, const QString &name)
{
ServerConfig serverConfig = m_serversController->getServerConfig(index);
if (serverConfig.isApiV1()) {
ApiV1ServerConfig* apiV1 = serverConfig.as<ApiV1ServerConfig>();
if (apiV1) {
apiV1->name = name;
}
} else if (serverConfig.isApiV2()) {
ApiV2ServerConfig* apiV2 = serverConfig.as<ApiV2ServerConfig>();
if (apiV2) {
apiV2->name = name;
apiV2->nameOverriddenByUser = true;
}
} else {
serverConfig.visit([&name](auto& arg) {
arg.description = name;
});
}
m_serversController->editServer(index, serverConfig);
updateModel();
}
void ServersUiController::setDefaultServerIndex(int index)
{
m_serversController->setDefaultServerIndex(index);
updateModel();
emit defaultServerIndexChanged(index);
}
void ServersUiController::setDefaultContainer(int serverIndex, int containerIndex)
{
auto container = static_cast<DockerContainer>(containerIndex);
m_serversController->setDefaultContainer(serverIndex, container);
updateModel();
}
void ServersUiController::toggleAmneziaDns(bool enabled)
{
m_settingsController->toggleAmneziaDns(enabled);
updateModel();
}
void ServersUiController::onDefaultServerChanged(int index)
{
setProcessedServerIndex(index);
updateModel();
updateDefaultServerContainersModel();
emit defaultServerIndexChanged(index);
}
void ServersUiController::updateModel()
{
int defaultIndex = m_serversController->getDefaultServerIndex();
bool wasEmpty = !hasServersFromGatewayApi();
int serversCount = m_serversController->getServersCount();
if (m_processedServerIndex >= serversCount) {
setProcessedServerIndex(defaultIndex);
} else if (m_processedServerIndex >= 0) {
setProcessedServerIndex(m_processedServerIndex);
}
m_serversModel->updateModel(m_serversController->getServers(), defaultIndex, m_settingsController->isAmneziaDnsEnabled());
updateContainersModel();
updateDefaultServerContainersModel();
bool isEmpty = !hasServersFromGatewayApi();
if (wasEmpty != isEmpty) {
emit hasServersFromGatewayApiChanged();
}
emit defaultServerIndexChanged(defaultIndex);
}
int ServersUiController::getDefaultServerIndex() const
{
return m_serversController->getDefaultServerIndex();
}
QString ServersUiController::getDefaultServerName() const
{
int defaultIndex = getDefaultServerIndex();
return m_serversController->getServerConfig(defaultIndex).displayName();
}
QString ServersUiController::getDefaultServerDefaultContainerName() const
{
int defaultIndex = getDefaultServerIndex();
const ServerConfig server = m_serversController->getServerConfig(defaultIndex);
return ContainerUtils::containerHumanNames().value(server.defaultContainer());
}
QString ServersUiController::getDefaultServerDescriptionCollapsed() const
{
int defaultIndex = getDefaultServerIndex();
const ServerConfig server = m_serversController->getServerConfig(defaultIndex);
QString description = getDefaultServerDescription(server, defaultIndex);
if (server.isApiConfig()) {
return description;
}
DockerContainer container = server.defaultContainer();
QString containerName = ContainerUtils::containerHumanNames().value(container);
QString protocolVersion;
QString hostName = server.hostName();
if (ContainerUtils::isAwgContainer(container)) {
ContainerConfig containerConfig = server.containerConfig(container);
if (auto* awgProtocolConfig = containerConfig.getAwgProtocolConfig()) {
QString version = awgProtocolConfig->serverConfig.protocolVersion;
if (version == protocols::awg::awgV2) {
protocolVersion = QObject::tr(" (version 2)");
} else if (version == protocols::awg::awgV1_5) {
protocolVersion = QObject::tr(" (version 1.5)");
}
if (container == DockerContainer::Awg && !awgProtocolConfig->serverConfig.isThirdPartyConfig) {
containerName = "AmneziaWG Legacy";
}
}
}
return description + containerName + protocolVersion + " | " + hostName;
}
QString ServersUiController::getDefaultServerImagePathCollapsed() const
{
int defaultIndex = getDefaultServerIndex();
const ServerConfig server = m_serversController->getServerConfig(defaultIndex);
if (server.isApiV2()) {
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
if (!apiV2) return QString();
const QString countryCode = apiV2->apiConfig.serverCountryCode;
if (countryCode.isEmpty()) {
return "";
}
return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode.toUpper());
}
return "";
}
QString ServersUiController::getDefaultServerDescriptionExpanded() const
{
int defaultIndex = getDefaultServerIndex();
const ServerConfig server = m_serversController->getServerConfig(defaultIndex);
QString description = getDefaultServerDescription(server, defaultIndex);
if (server.isApiConfig()) {
return description;
}
return description + server.hostName();
}
bool ServersUiController::isDefaultServerDefaultContainerHasSplitTunneling() const
{
int defaultIndex = getDefaultServerIndex();
const ServerConfig server = m_serversController->getServerConfig(defaultIndex);
DockerContainer defaultContainer = server.defaultContainer();
ContainerConfig containerConfig = server.containerConfig(defaultContainer);
if (defaultContainer == DockerContainer::Awg || defaultContainer == DockerContainer::WireGuard) {
auto hasSplitTunnelingFromAllowedIps = [](const QStringList& allowedIps, const QString& nativeConfig) -> bool {
bool hasSplitTunneling = !allowedIps.isEmpty() && !allowedIps.contains("0.0.0.0/0");
if (!hasSplitTunneling && !nativeConfig.isEmpty()) {
hasSplitTunneling = nativeConfig.contains("AllowedIPs")
&& !nativeConfig.contains("AllowedIPs = 0.0.0.0/0, ::/0");
}
return hasSplitTunneling;
};
if (defaultContainer == DockerContainer::Awg) {
if (const auto* awgConfig = containerConfig.getAwgProtocolConfig()) {
if (awgConfig->hasClientConfig()) {
return hasSplitTunnelingFromAllowedIps(
awgConfig->clientConfig->allowedIps,
awgConfig->clientConfig->nativeConfig
);
}
}
} else if (defaultContainer == DockerContainer::WireGuard) {
if (const auto* wgConfig = containerConfig.getWireGuardProtocolConfig()) {
if (wgConfig->hasClientConfig()) {
return hasSplitTunnelingFromAllowedIps(
wgConfig->clientConfig->allowedIps,
wgConfig->clientConfig->nativeConfig
);
}
}
}
return false;
} else if (defaultContainer == DockerContainer::OpenVpn) {
if (const auto* ovpnConfig = containerConfig.getOpenVpnProtocolConfig()) {
if (ovpnConfig->hasClientConfig()) {
return !ovpnConfig->clientConfig->nativeConfig.isEmpty()
&& !ovpnConfig->clientConfig->nativeConfig.contains("redirect-gateway");
}
}
}
return false;
}
bool ServersUiController::isDefaultServerFromApi() const
{
int defaultIndex = getDefaultServerIndex();
const ServerConfig server = m_serversController->getServerConfig(defaultIndex);
const int configVersion = server.configVersion();
return configVersion == apiDefs::ConfigSource::Telegram
|| configVersion == apiDefs::ConfigSource::AmneziaGateway;
}
int ServersUiController::getProcessedServerIndex() const
{
return m_processedServerIndex;
}
int ServersUiController::getProcessedContainerIndex() const
{
return m_processedContainerIndex;
}
void ServersUiController::setProcessedContainerIndex(int index)
{
if (m_processedContainerIndex != index) {
m_processedContainerIndex = index;
m_containersModel->setProcessedContainerIndex(index);
emit processedContainerIndexChanged(m_processedContainerIndex);
}
}
void ServersUiController::setProcessedServerIndex(int index)
{
if (index >= m_serversController->getServersCount()) {
return;
}
if (m_processedServerIndex != index) {
m_processedServerIndex = index;
m_serversModel->setProcessedServerIndex(index);
if (index >= 0) {
updateContainersModel();
ServerConfig server = m_serversController->getServerConfig(index);
setProcessedContainerIndex(static_cast<int>(server.defaultContainer()));
if (server.isApiV2()) {
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
if (apiV2 && !apiV2->apiConfig.availableCountries.isEmpty()) {
emit updateApiCountryModel();
}
emit updateApiServicesModel();
}
}
emit processedServerIndexChanged(m_processedServerIndex);
}
}
bool ServersUiController::processedServerIsPremium() const
{
ServerConfig server = m_serversController->getServerConfig(m_processedServerIndex);
if (server.isApiV1()) {
const ApiV1ServerConfig* apiV1 = server.as<ApiV1ServerConfig>();
return apiV1 ? apiV1->isPremium() : false;
} else if (server.isApiV2()) {
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
return apiV2 ? (apiV2->isPremium() || apiV2->isExternalPremium()) : false;
}
return false;
}
const ServerCredentials ServersUiController::getProcessedServerCredentials() const
{
return m_serversController->getServerCredentials(m_processedServerIndex);
}
bool ServersUiController::isDefaultServerCurrentlyProcessed() const
{
return m_serversController->getDefaultServerIndex() == m_processedServerIndex;
}
bool ServersUiController::isProcessedServerHasWriteAccess() const
{
ServerCredentials credentials = m_serversController->getServerCredentials(m_processedServerIndex);
return (!credentials.userName.isEmpty() && !credentials.secretData.isEmpty());
}
QString ServersUiController::getDefaultServerDescription(const ServerConfig& server, int index) const
{
QString description;
if (server.isApiV2()) {
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
if (!apiV2) return QString();
if (!apiV2->apiConfig.serverCountryCode.isEmpty()) {
return apiV2->apiConfig.serverCountryName;
}
return apiV2->description;
} else if (server.isApiV1()) {
const ApiV1ServerConfig* apiV1 = server.as<ApiV1ServerConfig>();
return apiV1 ? apiV1->description : QString();
} else {
ServerCredentials credentials = m_serversController->getServerCredentials(index);
if (!credentials.userName.isEmpty() && !credentials.secretData.isEmpty()) {
bool isAmneziaDnsEnabled = m_settingsController->isAmneziaDnsEnabled();
if (isAmneziaDnsEnabled && isAmneziaDnsContainerInstalled(index)) {
description += "Amnezia DNS | ";
}
} else {
if (server.dns1() == protocols::dns::amneziaDnsIp) {
description += "Amnezia DNS | ";
}
}
return description;
}
}
bool ServersUiController::isAmneziaDnsContainerInstalled(int serverIndex) const
{
const ServerConfig server = m_serversController->getServerConfig(serverIndex);
QMap<DockerContainer, ContainerConfig> containers = server.containers();
return containers.contains(DockerContainer::Dns);
}
bool ServersUiController::hasServersFromGatewayApi() const
{
QVector<ServerConfig> servers = m_serversController->getServers();
for (const ServerConfig &server : servers) {
if (server.isApiV2()) {
return true;
}
}
return false;
}
bool ServersUiController::isAdVisible() const
{
int defaultIndex = getDefaultServerIndex();
if (defaultIndex < 0) {
return false;
}
ServerConfig server = m_serversController->getServerConfig(defaultIndex);
if (server.isApiV2()) {
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
if (!apiV2) return false;
return apiV2->apiConfig.serviceInfo.isAdVisible;
}
return false;
}
QString ServersUiController::adHeader() const
{
int defaultIndex = getDefaultServerIndex();
if (defaultIndex < 0) {
return QString();
}
ServerConfig server = m_serversController->getServerConfig(defaultIndex);
if (server.isApiV2()) {
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
if (!apiV2) return QString();
return apiV2->apiConfig.serviceInfo.adHeader;
}
return QString();
}
QString ServersUiController::adDescription() const
{
int defaultIndex = getDefaultServerIndex();
if (defaultIndex < 0) {
return QString();
}
ServerConfig server = m_serversController->getServerConfig(defaultIndex);
if (server.isApiV2()) {
const ApiV2ServerConfig* apiV2 = server.as<ApiV2ServerConfig>();
if (!apiV2) return QString();
return apiV2->apiConfig.serviceInfo.adDescription;
}
return QString();
}
void ServersUiController::updateContainersModel()
{
if (m_processedServerIndex < 0 || m_processedServerIndex >= m_serversController->getServersCount()) {
return;
}
ServerConfig server = m_serversController->getServerConfig(m_processedServerIndex);
QMap<DockerContainer, ContainerConfig> containers = server.containers();
m_containersModel->updateModel(containers);
}
void ServersUiController::updateDefaultServerContainersModel()
{
int defaultIndex = m_serversController->getDefaultServerIndex();
if (defaultIndex < 0 || defaultIndex >= m_serversController->getServersCount()) {
return;
}
ServerConfig server = m_serversController->getServerConfig(defaultIndex);
QMap<DockerContainer, ContainerConfig> containers = server.containers();
m_defaultServerContainersModel->updateModel(containers);
}
QStringList ServersUiController::getAllInstalledServicesName(int serverIndex) const
{
QStringList servicesName;
ServerConfig server = m_serversController->getServerConfig(serverIndex);
QMap<DockerContainer, ContainerConfig> containers = server.containers();
for (auto it = containers.begin(); it != containers.end(); ++it) {
DockerContainer container = it.key();
if (ContainerUtils::containerService(container) == ServiceType::Other) {
if (container == DockerContainer::Dns) {
servicesName.append("DNS");
} else if (container == DockerContainer::Sftp) {
servicesName.append("SFTP");
} else if (container == DockerContainer::TorWebSite) {
servicesName.append("TOR");
} else if (container == DockerContainer::Socks5Proxy) {
servicesName.append("SOCKS5");
}
}
}
servicesName.sort();
return servicesName;
}
+115
View File
@@ -0,0 +1,115 @@
#ifndef SERVERSUICONTROLLER_H
#define SERVERSUICONTROLLER_H
#include <QObject>
#include <QSet>
#include <QJsonObject>
#include <QStringList>
#include "core/controllers/serversController.h"
#include "core/controllers/settingsController.h"
#include "ui/models/serversModel.h"
#include "ui/models/containersModel.h"
#include "core/models/serverConfig.h"
class ServersUiController : public QObject
{
Q_OBJECT
Q_PROPERTY(int defaultIndex READ getDefaultServerIndex NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString defaultServerName READ getDefaultServerName NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString defaultServerDefaultContainerName READ getDefaultServerDefaultContainerName NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString defaultServerDescriptionCollapsed READ getDefaultServerDescriptionCollapsed NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString defaultServerImagePathCollapsed READ getDefaultServerImagePathCollapsed NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString defaultServerDescriptionExpanded READ getDefaultServerDescriptionExpanded NOTIFY defaultServerIndexChanged)
Q_PROPERTY(bool isDefaultServerDefaultContainerHasSplitTunneling READ isDefaultServerDefaultContainerHasSplitTunneling NOTIFY defaultServerIndexChanged)
Q_PROPERTY(bool isDefaultServerFromApi READ isDefaultServerFromApi NOTIFY defaultServerIndexChanged)
Q_PROPERTY(int processedIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged)
Q_PROPERTY(int processedContainerIndex READ getProcessedContainerIndex WRITE setProcessedContainerIndex NOTIFY processedContainerIndexChanged)
Q_PROPERTY(bool processedServerIsPremium READ processedServerIsPremium NOTIFY processedServerIndexChanged)
Q_PROPERTY(bool hasServersFromGatewayApi READ hasServersFromGatewayApi NOTIFY hasServersFromGatewayApiChanged)
Q_PROPERTY(bool isAdVisible READ isAdVisible NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString adHeader READ adHeader NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString adDescription READ adDescription NOTIFY defaultServerIndexChanged)
public:
explicit ServersUiController(ServersController* serversController,
SettingsController* settingsController,
ServersModel* serversModel,
ContainersModel* containersModel,
ContainersModel* defaultServerContainersModel,
QObject *parent = nullptr);
public slots:
void removeServer(int index);
void editServerName(int index, const QString &name);
void setDefaultServerIndex(int index);
void setDefaultContainer(int serverIndex, int containerIndex);
void toggleAmneziaDns(bool enabled);
void onDefaultServerChanged(int index);
// Getters for properties
int getDefaultServerIndex() const;
QString getDefaultServerName() const;
QString getDefaultServerDefaultContainerName() const;
QString getDefaultServerDescriptionCollapsed() const;
QString getDefaultServerImagePathCollapsed() const;
QString getDefaultServerDescriptionExpanded() const;
bool isDefaultServerDefaultContainerHasSplitTunneling() const;
bool isDefaultServerFromApi() const;
int getProcessedServerIndex() const;
void setProcessedServerIndex(int index);
int getProcessedContainerIndex() const;
void setProcessedContainerIndex(int index);
bool processedServerIsPremium() const;
const ServerCredentials getProcessedServerCredentials() const;
bool isDefaultServerCurrentlyProcessed() const;
bool isProcessedServerHasWriteAccess() const;
bool hasServersFromGatewayApi() const;
bool isAdVisible() const;
QString adHeader() const;
QString adDescription() const;
QStringList getAllInstalledServicesName(int serverIndex) const;
signals:
void errorOccurred(const QString &errorMessage);
void finished(const QString &message);
void defaultServerIndexChanged(int index);
void processedServerIndexChanged(int index);
void processedContainerIndexChanged(int index);
void hasServersFromGatewayApiChanged();
void updateApiCountryModel();
void updateApiServicesModel();
public:
void updateModel();
private:
QString getDefaultServerDescription(const ServerConfig& server, int index) const;
bool isAmneziaDnsContainerInstalled(int serverIndex) const;
void updateContainersModel();
void updateDefaultServerContainersModel();
void updateApiModelsForProcessedServer();
ServersController* m_serversController;
SettingsController* m_settingsController;
ServersModel* m_serversModel;
ContainersModel* m_containersModel;
ContainersModel* m_defaultServerContainersModel;
int m_processedServerIndex = -1;
int m_processedContainerIndex = -1;
};
#endif // SERVERSUICONTROLLER_H
@@ -1,535 +0,0 @@
#include "settingsController.h"
#include <QStandardPaths>
#include <QOperatingSystemVersion>
#include "logger.h"
#include "systemController.h"
#include "ui/qautostart.h"
#include "amnezia_application.h"
#include "version.h"
#ifdef Q_OS_ANDROID
#include "platforms/android/android_controller.h"
#endif
#if defined(Q_OS_IOS) || defined(MACOS_NE)
#include <AmneziaVPN-Swift.h>
#endif
SettingsController::SettingsController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<LanguageModel> &languageModel,
const QSharedPointer<SitesModel> &sitesModel,
const QSharedPointer<AppSplitTunnelingModel> &appSplitTunnelingModel,
const std::shared_ptr<Settings> &settings, QObject *parent)
: QObject(parent),
m_serversModel(serversModel),
m_containersModel(containersModel),
m_languageModel(languageModel),
m_sitesModel(sitesModel),
m_appSplitTunnelingModel(appSplitTunnelingModel),
m_settings(settings)
{
m_appVersion = QString("%1 (%2, %3)").arg(QString(APP_VERSION), __DATE__, GIT_COMMIT_HASH);
checkIfNeedDisableLogs();
#ifdef Q_OS_ANDROID
connect(AndroidController::instance(), &AndroidController::notificationStateChanged, this, &SettingsController::onNotificationStateChanged);
connect(AndroidController::instance(), &AndroidController::imeInsetsChanged, this, [this](int heightDp) {
m_imeHeight = heightDp;
emit imeHeightChanged(heightDp);
emit safeAreaBottomMarginChanged();
});
connect(AndroidController::instance(), &AndroidController::systemBarsInsetsChanged, this, [this](int navBarHeightDp, int statusBarHeightDp) {
m_cachedNavigationBarHeight = navBarHeightDp;
m_cachedStatusBarHeight = statusBarHeightDp;
emit safeAreaBottomMarginChanged();
emit safeAreaTopMarginChanged();
});
connect(AndroidController::instance(), &AndroidController::activityPaused, this, &SettingsController::activityPaused);
connect(AndroidController::instance(), &AndroidController::activityResumed, this, &SettingsController::activityResumed);
#endif
m_isDevModeEnabled = m_settings->isDevGatewayEnv();
toggleDevGatewayEnv(m_isDevModeEnabled);
}
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
}
void SettingsController::toggleAmneziaDns(bool enable)
{
m_settings->setUseAmneziaDns(enable);
emit amneziaDnsToggled(enable);
}
bool SettingsController::isAmneziaDnsEnabled()
{
return m_settings->useAmneziaDns();
}
QString SettingsController::getPrimaryDns()
{
return m_settings->primaryDns();
}
void SettingsController::setPrimaryDns(const QString &dns)
{
m_settings->setPrimaryDns(dns);
emit primaryDnsChanged();
}
QString SettingsController::getSecondaryDns()
{
return m_settings->secondaryDns();
}
void SettingsController::setSecondaryDns(const QString &dns)
{
return m_settings->setSecondaryDns(dns);
emit secondaryDnsChanged();
}
bool SettingsController::isLoggingEnabled()
{
return m_settings->isSaveLogs();
}
void SettingsController::toggleLogging(bool enable)
{
m_settings->setSaveLogs(enable);
#if defined(Q_OS_IOS)
AmneziaVPN::toggleLogging(enable);
#endif
if (enable == true) {
qInfo().noquote() << QString("Logging has enabled on %1 version %2 %3").arg(APPLICATION_NAME, APP_VERSION, GIT_COMMIT_HASH);
qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture());
}
emit loggingStateChanged();
}
void SettingsController::openLogsFolder()
{
Logger::openLogsFolder(false);
}
void SettingsController::openServiceLogsFolder()
{
Logger::openLogsFolder(true);
}
void SettingsController::exportLogsFile(const QString &fileName)
{
#ifdef Q_OS_ANDROID
AndroidController::instance()->exportLogsFile(fileName);
#else
SystemController::saveFile(fileName, Logger::getLogFile());
#endif
}
void SettingsController::exportServiceLogsFile(const QString &fileName)
{
#ifdef Q_OS_ANDROID
AndroidController::instance()->exportLogsFile(fileName);
#else
SystemController::saveFile(fileName, Logger::getServiceLogFile());
#endif
}
void SettingsController::clearLogs()
{
#ifdef Q_OS_ANDROID
AndroidController::instance()->clearLogs();
#else
Logger::clearLogs(false);
Logger::clearServiceLogs();
#endif
qInfo().noquote() << QString("Started %1 version %2 %3").arg(APPLICATION_NAME, APP_VERSION, GIT_COMMIT_HASH);
qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture());
qInfo().noquote() << QString("SSL backend: %1").arg(QSslSocket::sslLibraryVersionString());
}
void SettingsController::backupAppConfig(const QString &fileName)
{
QByteArray data = m_settings->backupAppConfig();
QJsonDocument doc = QJsonDocument::fromJson(data);
QJsonObject config = doc.object();
config["AppPlatform"] = getPlatformName();
config["Conf/autoStart"] = Autostart::isAutostart();
config["Conf/killSwitchEnabled"] = isKillSwitchEnabled();
config["Conf/strictKillSwitchEnabled"] = isStrictKillSwitchEnabled();
config["Conf/useAmneziaDns"] = isAmneziaDnsEnabled();
SystemController::saveFile(fileName, QJsonDocument(config).toJson());
}
void SettingsController::restoreAppConfig(const QString &fileName)
{
QByteArray data;
if (!SystemController::readFile(fileName, data)) {
emit changeSettingsErrorOccurred(tr("Can't open file: %1").arg(fileName));
return;
}
restoreAppConfigFromData(data);
}
void SettingsController::restoreAppConfigFromData(const QByteArray &data)
{
bool ok = m_settings->restoreAppConfig(data);
if (ok) {
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
m_serversModel->resetModel();
m_languageModel->changeLanguage(
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
#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";
m_appSplitTunnelingModel->setRouteMode(appSplitTunnelingRouteMode);
#if defined(Q_OS_WINDOWS)
m_appSplitTunnelingModel->setRouteMode(static_cast<int>(Settings::AppsRouteMode::VpnAllExceptApps));
#endif
if (newConfigData.contains("AppPlatform")) { //if backup is from a new version
if (newConfigData.value("AppPlatform").toString() != getPlatformName()) {
m_appSplitTunnelingModel->clearAppsList();
}
}
m_appSplitTunnelingModel->toggleSplitTunneling(appSplittunnelingEnabled);
#endif
int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt();
bool siteSplittunnelingEnabled =
newConfigData.value("Conf/sitesSplitTunnelingEnabled").toVariant().toString().toLower() == "true";
m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode);
m_sitesModel->toggleSplitTunneling(siteSplittunnelingEnabled);
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
m_settings->setAutoConnect(false);
m_settings->setStartMinimized(false);
m_settings->setKillSwitchEnabled(false);
m_settings->setStrictKillSwitchEnabled(false);
#endif
bool amneziaDnsEnabled = newConfigData.contains("Conf/useAmneziaDns")
? newConfigData.value("Conf/useAmneziaDns").toBool()
: m_settings->useAmneziaDns();
emit amneziaDnsToggled(amneziaDnsEnabled);
emit restoreBackupFinished();
} else {
emit changeSettingsErrorOccurred(tr("Backup file is corrupted"));
}
}
QString SettingsController::getAppVersion()
{
return m_appVersion;
}
void SettingsController::clearSettings()
{
m_settings->clearSettings();
m_serversModel->resetModel();
m_languageModel->changeLanguage(m_languageModel->getSystemLanguageEnum());
m_sitesModel->setRouteMode(Settings::RouteMode::VpnOnlyForwardSites);
m_sitesModel->toggleSplitTunneling(false);
m_appSplitTunnelingModel->setRouteMode(Settings::AppsRouteMode::VpnAllExceptApps);
m_appSplitTunnelingModel->toggleSplitTunneling(false);
toggleAutoStart(false);
emit changeSettingsFinished(tr("All settings have been reset to default values"));
#if defined(Q_OS_IOS) || defined(MACOS_NE)
AmneziaVPN::clearSettings();
#endif
}
bool SettingsController::isAutoConnectEnabled()
{
return m_settings->isAutoConnect();
}
void SettingsController::toggleAutoConnect(bool enable)
{
m_settings->setAutoConnect(enable);
}
bool SettingsController::isAutoStartEnabled()
{
return Autostart::isAutostart();
}
void SettingsController::toggleAutoStart(bool enable)
{
Autostart::setAutostart(enable);
if (!enable) {
toggleStartMinimized(false);
}
}
bool SettingsController::isStartMinimizedEnabled()
{
return m_settings->isStartMinimized();
}
void SettingsController::toggleStartMinimized(bool enable)
{
m_settings->setStartMinimized(enable);
emit startMinimizedChanged();
}
bool SettingsController::isNewsNotificationsEnabled()
{
return m_settings->isNewsNotifications();
}
void SettingsController::toggleNewsNotificationsEnabled(bool enable)
{
m_settings->setNewsNotifications(enable);
}
bool SettingsController::isScreenshotsEnabled()
{
return m_settings->isScreenshotsEnabled();
}
void SettingsController::toggleScreenshotsEnabled(bool enable)
{
m_settings->setScreenshotsEnabled(enable);
}
bool SettingsController::isCameraPresent()
{
#if defined Q_OS_IOS
return true;
#elif defined Q_OS_ANDROID
return AndroidController::instance()->isCameraPresent();
#else
return false;
#endif
}
void SettingsController::checkIfNeedDisableLogs()
{
if (m_settings->isSaveLogs()) {
m_loggingDisableDate = m_settings->getLogEnableDate().addDays(14);
if (m_loggingDisableDate <= QDateTime::currentDateTime()) {
toggleLogging(false);
clearLogs();
emit loggingDisableByWatcher();
}
}
}
bool SettingsController::isKillSwitchEnabled()
{
return m_settings->isKillSwitchEnabled();
}
void SettingsController::toggleKillSwitch(bool enable)
{
m_settings->setKillSwitchEnabled(enable);
emit killSwitchEnabledChanged();
if (enable == false) {
emit strictKillSwitchEnabledChanged(false);
} else {
emit strictKillSwitchEnabledChanged(isStrictKillSwitchEnabled());
}
}
bool SettingsController::isStrictKillSwitchEnabled()
{
return m_settings->isStrictKillSwitchEnabled();
}
void SettingsController::toggleStrictKillSwitch(bool enable)
{
m_settings->setStrictKillSwitchEnabled(enable);
emit strictKillSwitchEnabledChanged(enable);
}
bool SettingsController::isNotificationPermissionGranted()
{
#ifdef Q_OS_ANDROID
return AndroidController::instance()->isNotificationPermissionGranted();
#else
return true;
#endif
}
void SettingsController::requestNotificationPermission()
{
#ifdef Q_OS_ANDROID
AndroidController::instance()->requestNotificationPermission();
#endif
}
QString SettingsController::getInstallationUuid()
{
return m_settings->getInstallationUuid(false);
}
void SettingsController::enableDevMode()
{
m_isDevModeEnabled = true;
emit devModeEnabled();
}
bool SettingsController::isDevModeEnabled()
{
return m_isDevModeEnabled;
}
void SettingsController::resetGatewayEndpoint()
{
m_settings->resetGatewayEndpoint();
emit gatewayEndpointChanged(m_settings->getGatewayEndpoint());
}
void SettingsController::setGatewayEndpoint(const QString &endpoint)
{
m_settings->setGatewayEndpoint(endpoint);
emit gatewayEndpointChanged(endpoint);
}
QString SettingsController::getGatewayEndpoint()
{
return m_settings->isDevGatewayEnv() ? "Dev endpoint" : m_settings->getGatewayEndpoint();
}
bool SettingsController::isDevGatewayEnv()
{
return m_settings->isDevGatewayEnv();
}
void SettingsController::toggleDevGatewayEnv(bool enabled)
{
m_settings->toggleDevGatewayEnv(enabled);
if (enabled) {
m_settings->setDevGatewayEndpoint();
} else {
m_settings->resetGatewayEndpoint();
}
emit gatewayEndpointChanged(m_settings->getGatewayEndpoint());
emit devGatewayEnvChanged(enabled);
}
bool SettingsController::isOnTv()
{
#ifdef Q_OS_ANDROID
return AndroidController::instance()->isOnTv();
#else
return false;
#endif
}
bool SettingsController::isEdgeToEdgeEnabled()
{
#ifdef Q_OS_ANDROID
if (!m_edgeToEdgeCached) {
m_cachedEdgeToEdgeEnabled = AndroidController::instance()->isEdgeToEdgeEnabled();
m_edgeToEdgeCached = true;
}
return m_cachedEdgeToEdgeEnabled;
#else
return false;
#endif
}
int SettingsController::getStatusBarHeight()
{
#ifdef Q_OS_ANDROID
if (m_cachedStatusBarHeight < 0) {
m_cachedStatusBarHeight = AndroidController::instance()->getStatusBarHeight();
}
return m_cachedStatusBarHeight;
#else
return 0;
#endif
}
int SettingsController::getNavigationBarHeight()
{
#ifdef Q_OS_ANDROID
if (m_cachedNavigationBarHeight < 0) {
m_cachedNavigationBarHeight = AndroidController::instance()->getNavigationBarHeight();
}
return m_cachedNavigationBarHeight;
#else
return 0;
#endif
}
int SettingsController::getSafeAreaTopMargin()
{
#ifdef Q_OS_ANDROID
if (isEdgeToEdgeEnabled()) {
int height = getStatusBarHeight();
int result = height > 0 ? height : 40; // fallback to 40 if system returns 0
return result;
}
#endif
return 0;
}
int SettingsController::getSafeAreaBottomMargin()
{
#ifdef Q_OS_ANDROID
if (isEdgeToEdgeEnabled()) {
if (m_imeHeight > 0) {
return 0;
}
int height = getNavigationBarHeight();
int result = height > 0 ? height : 56; // fallback to 56 if system returns 0
return result;
}
#endif
return 0;
}
int SettingsController::getImeHeight()
{
return m_imeHeight;
}
bool SettingsController::isHomeAdLabelVisible()
{
return m_settings->isHomeAdLabelVisible();
}
void SettingsController::disableHomeAdLabel()
{
m_settings->disableHomeAdLabel();
emit isHomeAdLabelVisibleChanged(false);
}
@@ -0,0 +1,351 @@
#include "settingsUiController.h"
#include <QDebug>
#include <QStandardPaths>
#include <QOperatingSystemVersion>
#include <QFile>
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
#include "logger.h"
#include "systemController.h"
#include "amneziaApplication.h"
#include "version.h"
#ifdef Q_OS_ANDROID
#include "platforms/android/android_controller.h"
#endif
#if defined(Q_OS_IOS) || defined(MACOS_NE)
#include <AmneziaVPN-Swift.h>
#endif
SettingsUiController::SettingsUiController(SettingsController* settingsController,
ServersController* serversController,
LanguageUiController* languageUiController,
QObject *parent)
: QObject(parent),
m_settingsController(settingsController),
m_serversController(serversController),
m_languageUiController(languageUiController)
{
#ifdef Q_OS_ANDROID
connect(AndroidController::instance(), &AndroidController::notificationStateChanged, this, &SettingsUiController::onNotificationStateChanged);
connect(AndroidController::instance(), &AndroidController::activityPaused, this, &SettingsUiController::activityPaused);
connect(AndroidController::instance(), &AndroidController::activityResumed, this, &SettingsUiController::activityResumed);
#endif
m_settingsController->checkIfNeedDisableLogs();
if (m_settingsController->isDevGatewayEnv()) {
m_settingsController->enableDevMode();
}
}
void SettingsUiController::toggleAmneziaDns(bool enable)
{
m_settingsController->toggleAmneziaDns(enable);
emit amneziaDnsToggled(enable);
}
bool SettingsUiController::isAmneziaDnsEnabled()
{
return m_settingsController->isAmneziaDnsEnabled();
}
QString SettingsUiController::getPrimaryDns()
{
return m_settingsController->getPrimaryDns();
}
void SettingsUiController::setPrimaryDns(const QString &dns)
{
m_settingsController->setPrimaryDns(dns);
emit primaryDnsChanged();
}
QString SettingsUiController::getSecondaryDns()
{
return m_settingsController->getSecondaryDns();
}
void SettingsUiController::setSecondaryDns(const QString &dns)
{
m_settingsController->setSecondaryDns(dns);
emit secondaryDnsChanged();
}
bool SettingsUiController::isLoggingEnabled()
{
return m_settingsController->isLoggingEnabled();
}
void SettingsUiController::toggleLogging(bool enable)
{
m_settingsController->toggleLogging(enable);
#if defined(Q_OS_IOS)
AmneziaVPN::toggleLogging(enable);
#endif
if (enable == true) {
qInfo().noquote() << QString("Logging has enabled on %1 version %2 %3").arg(APPLICATION_NAME, APP_VERSION, GIT_COMMIT_HASH);
qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture());
}
emit loggingStateChanged();
}
void SettingsUiController::openLogsFolder()
{
Logger::openLogsFolder(false);
}
void SettingsUiController::openServiceLogsFolder()
{
Logger::openLogsFolder(true);
}
void SettingsUiController::exportLogsFile(const QString &fileName)
{
#ifdef Q_OS_ANDROID
AndroidController::instance()->exportLogsFile(fileName);
#else
SystemController::saveFile(fileName, Logger::getLogFile());
#endif
}
void SettingsUiController::exportServiceLogsFile(const QString &fileName)
{
#ifdef Q_OS_ANDROID
AndroidController::instance()->exportLogsFile(fileName);
#else
SystemController::saveFile(fileName, Logger::getServiceLogFile());
#endif
}
void SettingsUiController::clearLogs()
{
m_settingsController->clearLogs();
qInfo().noquote() << QString("Started %1 version %2 %3").arg(APPLICATION_NAME, APP_VERSION, GIT_COMMIT_HASH);
qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture());
qInfo().noquote() << QString("SSL backend: %1").arg(QSslSocket::sslLibraryVersionString());
}
void SettingsUiController::backupAppConfig(const QString &fileName)
{
QByteArray data = m_settingsController->backupAppConfig();
SystemController::saveFile(fileName, data);
}
void SettingsUiController::restoreAppConfig(const QString &fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
emit errorOccurred(ErrorCode::OpenError);
return;
}
restoreAppConfigFromData(file.readAll());
}
void SettingsUiController::restoreAppConfigFromData(const QByteArray &data)
{
ErrorCode errorCode = m_settingsController->restoreAppConfigFromData(data);
if (errorCode == ErrorCode::NoError) {
emit appLanguageChanged(
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageUiController->getCurrentLanguageIndex()));
bool amneziaDnsEnabled = m_settingsController->isAmneziaDnsEnabled();
emit amneziaDnsToggled(amneziaDnsEnabled);
emit restoreBackupFinished();
} else {
emit errorOccurred(errorCode);
}
}
QString SettingsUiController::getAppVersion()
{
return m_settingsController->getAppVersion();
}
void SettingsUiController::clearSettings()
{
m_settingsController->clearSettings();
emit resetLanguageToSystem();
emit changeSettingsFinished(tr("All settings have been reset to default values"));
#if defined(Q_OS_IOS) || defined(MACOS_NE)
AmneziaVPN::clearSettings();
#endif
}
bool SettingsUiController::isAutoConnectEnabled()
{
return m_settingsController->isAutoConnectEnabled();
}
void SettingsUiController::toggleAutoConnect(bool enable)
{
m_settingsController->toggleAutoConnect(enable);
}
bool SettingsUiController::isAutoStartEnabled()
{
return m_settingsController->isAutoStartEnabled();
}
void SettingsUiController::toggleAutoStart(bool enable)
{
m_settingsController->toggleAutoStart(enable);
}
bool SettingsUiController::isStartMinimizedEnabled()
{
return m_settingsController->isStartMinimizedEnabled();
}
void SettingsUiController::toggleStartMinimized(bool enable)
{
m_settingsController->toggleStartMinimized(enable);
emit startMinimizedChanged();
}
bool SettingsUiController::isScreenshotsEnabled()
{
return m_settingsController->isScreenshotsEnabled();
}
void SettingsUiController::toggleScreenshotsEnabled(bool enable)
{
m_settingsController->toggleScreenshotsEnabled(enable);
}
bool SettingsUiController::isNewsNotificationsEnabled()
{
return m_settingsController->isNewsNotificationsEnabled();
}
void SettingsUiController::toggleNewsNotificationsEnabled(bool enable)
{
m_settingsController->toggleNewsNotificationsEnabled(enable);
}
bool SettingsUiController::isCameraPresent()
{
#if defined Q_OS_IOS
return true;
#elif defined Q_OS_ANDROID
return AndroidController::instance()->isCameraPresent();
#else
return false;
#endif
}
bool SettingsUiController::isKillSwitchEnabled()
{
return m_settingsController->isKillSwitchEnabled();
}
void SettingsUiController::toggleKillSwitch(bool enable)
{
m_settingsController->toggleKillSwitch(enable);
emit killSwitchEnabledChanged();
if (enable == false) {
emit strictKillSwitchEnabledChanged(false);
} else {
emit strictKillSwitchEnabledChanged(isStrictKillSwitchEnabled());
}
}
bool SettingsUiController::isStrictKillSwitchEnabled()
{
return m_settingsController->isStrictKillSwitchEnabled();
}
void SettingsUiController::toggleStrictKillSwitch(bool enable)
{
m_settingsController->toggleStrictKillSwitch(enable);
emit strictKillSwitchEnabledChanged(enable);
}
bool SettingsUiController::isNotificationPermissionGranted()
{
#ifdef Q_OS_ANDROID
return AndroidController::instance()->isNotificationPermissionGranted();
#else
return true;
#endif
}
void SettingsUiController::requestNotificationPermission()
{
#ifdef Q_OS_ANDROID
AndroidController::instance()->requestNotificationPermission();
#endif
}
QString SettingsUiController::getInstallationUuid()
{
return m_settingsController->getInstallationUuid();
}
void SettingsUiController::enableDevMode()
{
m_settingsController->enableDevMode();
emit devModeEnabled();
}
bool SettingsUiController::isDevModeEnabled()
{
return m_settingsController->isDevModeEnabled();
}
void SettingsUiController::resetGatewayEndpoint()
{
m_settingsController->resetGatewayEndpoint();
emit gatewayEndpointChanged(m_settingsController->getGatewayEndpoint());
}
void SettingsUiController::setGatewayEndpoint(const QString &endpoint)
{
m_settingsController->setGatewayEndpoint(endpoint);
emit gatewayEndpointChanged(endpoint);
}
QString SettingsUiController::getGatewayEndpoint()
{
return m_settingsController->getGatewayEndpoint();
}
bool SettingsUiController::isDevGatewayEnv()
{
return m_settingsController->isDevGatewayEnv();
}
void SettingsUiController::toggleDevGatewayEnv(bool enabled)
{
m_settingsController->toggleDevGatewayEnv(enabled);
emit gatewayEndpointChanged(m_settingsController->getGatewayEndpoint());
emit devGatewayEnvChanged(enabled);
}
bool SettingsUiController::isOnTv()
{
#ifdef Q_OS_ANDROID
return AndroidController::instance()->isOnTv();
#else
return false;
#endif
}
bool SettingsUiController::isHomeAdLabelVisible()
{
return m_settingsController->isHomeAdLabelVisible();
}
void SettingsUiController::disableHomeAdLabel()
{
m_settingsController->disableHomeAdLabel();
emit isHomeAdLabelVisibleChanged(false);
}
@@ -1,24 +1,24 @@
#ifndef SETTINGSCONTROLLER_H
#define SETTINGSCONTROLLER_H
#ifndef SETTINGSUICONTROLLER_H
#define SETTINGSUICONTROLLER_H
#include <QObject>
#include "ui/models/containers_model.h"
#include "core/controllers/settingsController.h"
#include "core/controllers/serversController.h"
#include "ui/controllers/languageUiController.h"
#include "ui/models/languageModel.h"
#include "ui/models/servers_model.h"
#include "ui/models/sites_model.h"
#include "ui/models/appSplitTunnelingModel.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
class SettingsController : public QObject
class SettingsUiController : public QObject
{
Q_OBJECT
public:
explicit SettingsController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<LanguageModel> &languageModel,
const QSharedPointer<SitesModel> &sitesModel,
const QSharedPointer<AppSplitTunnelingModel> &appSplitTunnelingModel,
const std::shared_ptr<Settings> &settings, QObject *parent = nullptr);
explicit SettingsUiController(SettingsController* settingsController,
ServersController* serversController,
LanguageUiController* languageUiController,
QObject *parent = nullptr);
Q_PROPERTY(QString primaryDns READ getPrimaryDns WRITE setPrimaryDns NOTIFY primaryDnsChanged)
Q_PROPERTY(QString secondaryDns READ getSecondaryDns WRITE setSecondaryDns NOTIFY secondaryDnsChanged)
@@ -33,9 +33,6 @@ public:
Q_PROPERTY(bool isHomeAdLabelVisible READ isHomeAdLabelVisible NOTIFY isHomeAdLabelVisibleChanged)
Q_PROPERTY(bool startMinimized READ isStartMinimizedEnabled NOTIFY startMinimizedChanged)
Q_PROPERTY(int safeAreaTopMargin READ getSafeAreaTopMargin NOTIFY safeAreaTopMarginChanged)
Q_PROPERTY(int safeAreaBottomMargin READ getSafeAreaBottomMargin NOTIFY safeAreaBottomMarginChanged)
Q_PROPERTY(int imeHeight READ getImeHeight NOTIFY imeHeightChanged)
public slots:
void toggleAmneziaDns(bool enable);
@@ -102,12 +99,6 @@ public slots:
void toggleDevGatewayEnv(bool enabled);
bool isOnTv();
bool isEdgeToEdgeEnabled();
int getStatusBarHeight();
int getNavigationBarHeight();
int getSafeAreaTopMargin();
int getSafeAreaBottomMargin();
int getImeHeight();
bool isHomeAdLabelVisible();
void disableHomeAdLabel();
@@ -121,7 +112,7 @@ signals:
void restoreBackupFinished();
void changeSettingsFinished(const QString &finishedMessage);
void changeSettingsErrorOccurred(const QString &errorMessage);
void errorOccurred(ErrorCode errorCode);
void saveFile(const QString &fileName, const QString &data);
@@ -131,15 +122,14 @@ signals:
void loggingDisableByWatcher();
void appLanguageChanged(const LanguageSettings::AvailableLanguageEnum language);
void resetLanguageToSystem();
void onNotificationStateChanged();
void devModeEnabled();
void gatewayEndpointChanged(const QString &endpoint);
void devGatewayEnvChanged(bool enabled);
void imeHeightChanged(int height);
void safeAreaTopMarginChanged();
void safeAreaBottomMarginChanged();
void activityPaused();
void activityResumed();
@@ -148,28 +138,9 @@ signals:
void startMinimizedChanged();
private:
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<LanguageModel> m_languageModel;
QSharedPointer<SitesModel> m_sitesModel;
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
mutable int m_cachedStatusBarHeight = -1;
mutable int m_cachedNavigationBarHeight = -1;
mutable bool m_cachedEdgeToEdgeEnabled = false;
mutable bool m_edgeToEdgeCached = false;
int m_imeHeight = 0;
std::shared_ptr<Settings> m_settings;
QString m_appVersion;
QString getPlatform();
QDateTime m_loggingDisableDate;
bool m_isDevModeEnabled = false;
void checkIfNeedDisableLogs();
SettingsController* m_settingsController;
ServersController* m_serversController;
LanguageUiController* m_languageUiController;
};
#endif // SETTINGSCONTROLLER_H
#endif
-134
View File
@@ -1,134 +0,0 @@
#include "sitesController.h"
#include <QFile>
#include <QHostInfo>
#include <QStandardPaths>
#include "systemController.h"
#include "core/networkUtilities.h"
SitesController::SitesController(const std::shared_ptr<Settings> &settings, const QSharedPointer<SitesModel> &sitesModel, QObject *parent)
: QObject(parent), m_settings(settings), m_sitesModel(sitesModel)
{
}
void SitesController::addSite(QString hostname)
{
if (hostname.isEmpty()) {
return;
}
if (!hostname.contains(".")) {
emit errorOccurred(tr("Hostname not look like ip adress or domain name"));
return;
}
if (!NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
// get domain name if it present
hostname.replace("https://", "");
hostname.replace("http://", "");
hostname.replace("ftp://", "");
hostname = hostname.split("/", Qt::SkipEmptyParts).first();
}
const auto &resolveCallback = [this](const QHostInfo &hostInfo) {
const QList<QHostAddress> &addresses = hostInfo.addresses();
for (const QHostAddress &addr : hostInfo.addresses()) {
if (addr.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol) {
m_sitesModel->addSite(hostInfo.hostName(), addr.toString());
break;
}
}
};
if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
m_sitesModel->addSite(hostname, "");
} else {
m_sitesModel->addSite(hostname, "");
QHostInfo::lookupHost(hostname, this, resolveCallback);
}
emit finished(tr("New site added: %1").arg(hostname));
}
void SitesController::removeSite(int index)
{
auto modelIndex = m_sitesModel->index(index);
auto hostname = m_sitesModel->data(modelIndex, SitesModel::Roles::UrlRole).toString();
m_sitesModel->removeSite(modelIndex);
emit finished(tr("Site removed: %1").arg(hostname));
}
void SitesController::removeSites()
{
m_sitesModel->removeSites();
emit finished(tr("Site list cleared!"));
}
void SitesController::importSites(const QString &fileName, bool replaceExisting)
{
QByteArray jsonData;
if (!SystemController::readFile(fileName, jsonData)) {
emit errorOccurred(tr("Can't open file: %1").arg(fileName));
return;
}
QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData);
if (jsonDocument.isNull()) {
emit errorOccurred(tr("Failed to parse JSON data from file: %1").arg(fileName));
return;
}
if (!jsonDocument.isArray()) {
emit errorOccurred(tr("The JSON data is not an array in file: %1").arg(fileName));
return;
}
auto jsonArray = jsonDocument.array();
QMap<QString, QString> sites;
QStringList ips;
for (auto jsonValue : jsonArray) {
auto jsonObject = jsonValue.toObject();
auto hostname = jsonObject.value("hostname").toString("");
auto ip = jsonObject.value("ip").toString("");
if (!hostname.contains(".") && !NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
qDebug() << hostname << " not look like ip adress or domain name";
continue;
}
if (ip.isEmpty()) {
ips.append(hostname);
} else {
ips.append(ip);
}
sites.insert(hostname, ip);
}
m_sitesModel->addSites(sites, replaceExisting);
emit finished(tr("Import completed"));
}
void SitesController::exportSites(const QString &fileName)
{
auto sites = m_sitesModel->getCurrentSites();
QJsonArray jsonArray;
for (const auto &site : sites) {
QJsonObject jsonObject { { "hostname", site.first }, { "ip", site.second } };
jsonArray.append(jsonObject);
}
QJsonDocument jsonDocument(jsonArray);
QByteArray jsonData = jsonDocument.toJson();
SystemController::saveFile(fileName, jsonData);
emit finished(tr("Export completed"));
}
-36
View File
@@ -1,36 +0,0 @@
#ifndef SITESCONTROLLER_H
#define SITESCONTROLLER_H
#include <QObject>
#include "settings.h"
#include "ui/models/sites_model.h"
#include "vpnconnection.h"
class SitesController : public QObject
{
Q_OBJECT
public:
explicit SitesController(const std::shared_ptr<Settings> &settings, const QSharedPointer<SitesModel> &sitesModel,
QObject *parent = nullptr);
public slots:
void addSite(QString hostname);
void removeSite(int index);
void removeSites();
void importSites(const QString &fileName, bool replaceExisting);
void exportSites(const QString &fileName);
signals:
void errorOccurred(const QString &errorMessage);
void finished(const QString &message);
void saveFile(const QString &fileName, const QString &data);
private:
std::shared_ptr<Settings> m_settings;
QSharedPointer<SitesModel> m_sitesModel;
};
#endif // SITESCONTROLLER_H
+5 -4
View File
@@ -19,8 +19,8 @@
#include <CoreFoundation/CoreFoundation.h>
#endif
SystemController::SystemController(const std::shared_ptr<Settings> &settings, QObject *parent)
: QObject(parent), m_settings(settings)
SystemController::SystemController(QObject *parent)
: QObject(parent)
{
}
@@ -38,8 +38,9 @@ void SystemController::saveFile(const QString &fileName, const QString &data)
QFile file(fileName);
#endif
// todo check if save successful
file.open(QIODevice::WriteOnly);
if (!file.open(QIODevice::WriteOnly)) {
return;
}
file.write(data.toUtf8());
file.close();
+1 -5
View File
@@ -3,13 +3,11 @@
#include <QObject>
#include "settings.h"
class SystemController : public QObject
{
Q_OBJECT
public:
explicit SystemController(const std::shared_ptr<Settings> &setting, QObject *parent = nullptr);
explicit SystemController(QObject *parent = nullptr);
static void saveFile(const QString &fileName, const QString &data);
static bool readFile(const QString &fileName, QByteArray &data);
@@ -28,8 +26,6 @@ signals:
void fileDialogClosed(const bool isAccepted);
private:
std::shared_ptr<Settings> m_settings;
QObject *m_qmlRoot;
};