Compare commits

...

9 Commits

Author SHA1 Message Date
vkamn ce06e451bc fix: fixed header link in xray settings page 2026-05-28 12:14:52 +08:00
vkamn e8cad3afe1 Merge branch 'dev' of github-amnezia:amnezia-vpn/amnezia-client into HEAD 2026-05-28 11:53:38 +08:00
yp 0a659a2d74 fix: various fixes for MTProxy & Telemt (#2653)
* fix color & fix enabled

* fixed remove base secret

* fix mtproxy/telemt 'base secret'

* fixed button back

* fixed loader

* fixed reload loader

* fixed dd secret

* fixed qml

* fix: fixed header link in mtproxy/telemt page

---------

Co-authored-by: vkamn <vk@amnezia.org>
2026-05-28 11:46:26 +08:00
vkamn 6f119cd083 fix: various fixes (#2662)
* fix: fixed dns processing

* fix: fixed proceesed index/id selection

* refactor: stop using the server index as state

* fix: fixed autostart and start minimized

* fix: fixed typo

* fix: add socks5 extractConfigFromContainer

* fix: remove unused currentContainerUpdated

* fix: fixed clear cached profile order
2026-05-28 10:57:08 +08:00
Yaroslav Gurov 1753aed3fc fix: use shared openssl on Android (#2657)
* feat(conan): clone openssl and patch it for Android

* fix(conan): build shared libssl for Android
2026-05-26 21:59:47 +08:00
dranik b7d378ecb7 remove comment 2026-05-25 14:33:19 +03:00
dranik 6e167e998b fixed save button 2026-05-22 16:08:34 +03:00
dranik dec410016e fixed default var 2026-05-22 15:17:39 +03:00
dranik ba2382924d fixed vless 2026-05-20 19:38:20 +03:00
96 changed files with 2432 additions and 1124 deletions
+278 -149
View File
@@ -4,6 +4,7 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QThread>
#include <QUuid>
#include "logger.h"
@@ -137,117 +138,326 @@ amnezia::ProtocolConfig XrayConfigurator::processConfigWithLocalSettings(const a
return protocolConfig;
}
QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentials, DockerContainer container,
const ContainerConfig &containerConfig,
const DnsSettings &dnsSettings,
ErrorCode &errorCode)
ErrorCode XrayConfigurator::uploadServerConfigJson(const ServerCredentials &credentials, DockerContainer container,
const DnsSettings &dnsSettings, const QJsonObject &serverConfig) const
{
// Generate new UUID for client
QString clientId = QUuid::createUuid().toString(QUuid::WithoutBraces);
const QString updatedConfig = QJsonDocument(serverConfig).toJson();
ErrorCode errorCode = m_sshSession->uploadTextFileToContainer(
container, credentials, updatedConfig, amnezia::protocols::xray::serverConfigPath,
libssh::ScpOverwriteMode::ScpOverwriteExisting);
if (errorCode != ErrorCode::NoError) {
logger.error() << "Failed to upload updated config";
return errorCode;
}
// Get flow value from settings (default xtls-rprx-vision)
QString flowValue = "xtls-rprx-vision";
if (const auto *xrayCfg = containerConfig.protocolConfig.as<XrayProtocolConfig>()) {
if (!xrayCfg->serverConfig.flow.isEmpty()) {
flowValue = xrayCfg->serverConfig.flow;
const QString restartScript = QStringLiteral("sudo docker restart $CONTAINER_NAME");
errorCode = m_sshSession->runScript(
credentials,
m_sshSession->replaceVars(restartScript,
amnezia::genBaseVars(credentials, container, dnsSettings.primaryDns,
dnsSettings.secondaryDns)));
if (errorCode != ErrorCode::NoError) {
logger.error() << "Failed to restart container";
}
return errorCode;
}
ErrorCode XrayConfigurator::readRealityKeyFiles(const DockerContainer container, const ServerCredentials &credentials,
QString &outPublicKey, QString &outShortId) const
{
outPublicKey.clear();
outShortId.clear();
auto readKeyFile = [&](const QString &path, QString &out) -> ErrorCode {
for (int attempt = 0; attempt < 3; ++attempt) {
ErrorCode fileError = ErrorCode::NoError;
out = QString::fromUtf8(m_sshSession->getTextFileFromContainer(container, credentials, path, fileError));
out.replace(QLatin1Char('\n'), QString());
out.replace(QLatin1Char('\r'), QString());
if (fileError == ErrorCode::NoError && !out.isEmpty()) {
return ErrorCode::NoError;
}
if (attempt < 2) {
QThread::msleep(500);
}
}
logger.error() << "Xray readRealityKeyFiles: failed path=" << path;
return ErrorCode::XrayRealityKeysReadFailed;
};
ErrorCode errorCode = readKeyFile(QString::fromLatin1(amnezia::protocols::xray::PublicKeyPath), outPublicKey);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
return readKeyFile(QString::fromLatin1(amnezia::protocols::xray::shortidPath), outShortId);
}
QJsonObject XrayConfigurator::mergeStreamSettingsForServerInbound(const XrayServerConfig &srv,
const QJsonObject &existingStreamSettings) const
{
QJsonObject streamSettings = buildStreamSettings(srv, QString());
if (srv.security != QLatin1String("reality")) {
return streamSettings;
}
const QJsonObject newRs = streamSettings[amnezia::protocols::xray::realitySettings].toObject();
QJsonObject oldRs = existingStreamSettings[amnezia::protocols::xray::realitySettings].toObject();
QJsonObject merged = oldRs.isEmpty() ? newRs : oldRs;
const QString siteEff = srv.site.isEmpty() ? QString::fromLatin1(amnezia::protocols::xray::defaultSite) : srv.site;
const QString sniEff = srv.sni.isEmpty() ? siteEff : srv.sni;
if (newRs.contains(amnezia::protocols::xray::fingerprint)) {
merged[amnezia::protocols::xray::fingerprint] = newRs[amnezia::protocols::xray::fingerprint];
}
merged[amnezia::protocols::xray::serverNames] = QJsonArray { sniEff };
if (!merged.contains(QStringLiteral("dest"))) {
merged[QStringLiteral("dest")] = siteEff + QStringLiteral(":443");
}
streamSettings[amnezia::protocols::xray::realitySettings] = merged;
return streamSettings;
}
ErrorCode XrayConfigurator::applyServerSettingsToRemote(const ServerCredentials &credentials, DockerContainer container,
ContainerConfig &containerConfig, const DnsSettings &dnsSettings,
bool appendNewClient, QString *outClientId)
{
ErrorCode errorCode = ErrorCode::NoError;
const auto *xrayCfg = containerConfig.protocolConfig.as<XrayProtocolConfig>();
if (!xrayCfg) {
logger.error() << "Xray applyServerSettings: missing XrayProtocolConfig";
return ErrorCode::InternalError;
}
const XrayServerConfig &srv = xrayCfg->serverConfig;
if (srv.isThirdPartyConfig) {
logger.info() << "Xray applyServerSettings: skipped (third-party/native profile)";
if (outClientId && xrayCfg->hasClientConfig()) {
*outClientId = xrayCfg->clientConfig->id;
}
return ErrorCode::NoError;
}
logger.info() << "Xray applyServerSettings: start"
<< "container=" << static_cast<int>(container) << "host=" << credentials.hostName
<< "transport=" << srv.transport << "security=" << srv.security << "port=" << srv.port
<< "appendClient=" << appendNewClient;
QString flowValue = srv.flow;
if (flowValue.isEmpty() && srv.security == QLatin1String("reality")) {
flowValue = QStringLiteral("xtls-rprx-vision");
}
QString realityPublicKey;
QString realityShortId;
if (srv.security == QLatin1String("reality")) {
errorCode = readRealityKeyFiles(container, credentials, realityPublicKey, realityShortId);
if (errorCode != ErrorCode::NoError) {
logger.error() << "Xray applyServerSettings: readRealityKeyFiles failed, error="
<< static_cast<int>(errorCode);
return errorCode;
}
}
// Get current server config
QString currentConfig = m_sshSession->getTextFileFromContainer(
container, credentials, amnezia::protocols::xray::serverConfigPath, errorCode);
container, credentials, amnezia::protocols::xray::serverConfigPath, errorCode);
if (errorCode != ErrorCode::NoError) {
logger.error() << "Failed to get server config file";
return "";
logger.error() << "Xray applyServerSettings: getTextFileFromContainer failed, error="
<< static_cast<int>(errorCode) << "path=" << amnezia::protocols::xray::serverConfigPath;
return errorCode;
}
logger.info() << "Xray applyServerSettings: read server config, bytes=" << currentConfig.size();
// Parse current config as JSON
QJsonDocument doc = QJsonDocument::fromJson(currentConfig.toUtf8());
if (doc.isNull() || !doc.isObject()) {
logger.error() << "Failed to parse server config JSON";
errorCode = ErrorCode::InternalError;
return "";
return ErrorCode::XrayServerConfigInvalid;
}
QJsonObject serverConfig = doc.object();
// Validate server config structure
if (!serverConfig.contains(amnezia::protocols::xray::inbounds)) {
logger.error() << "Server config missing 'inbounds' field";
errorCode = ErrorCode::InternalError;
return "";
return ErrorCode::XrayServerConfigInvalid;
}
QJsonArray inbounds = serverConfig[amnezia::protocols::xray::inbounds].toArray();
if (inbounds.isEmpty()) {
logger.error() << "Server config has empty 'inbounds' array";
errorCode = ErrorCode::InternalError;
return "";
return ErrorCode::XrayServerConfigInvalid;
}
QJsonObject inbound = inbounds[0].toObject();
if (!inbound.contains(amnezia::protocols::xray::settings)) {
logger.error() << "Inbound missing 'settings' field";
errorCode = ErrorCode::InternalError;
return "";
return ErrorCode::XrayServerConfigInvalid;
}
const QJsonObject existingStream = inbound[amnezia::protocols::xray::streamSettings].toObject();
inbound[amnezia::protocols::xray::streamSettings] = mergeStreamSettingsForServerInbound(srv, existingStream);
if (!srv.port.isEmpty()) {
inbound[amnezia::protocols::xray::port] = srv.port.toInt();
}
QJsonObject settings = inbound[amnezia::protocols::xray::settings].toObject();
if (!settings.contains(amnezia::protocols::xray::clients)) {
logger.error() << "Settings missing 'clients' field";
errorCode = ErrorCode::InternalError;
return "";
settings[amnezia::protocols::xray::clients] = QJsonArray {};
}
QJsonArray clients = settings[amnezia::protocols::xray::clients].toArray();
QString clientId;
// Create configuration for new client
QJsonObject clientConfig {
{amnezia::protocols::xray::id, clientId},
};
clientConfig[amnezia::protocols::xray::id] = clientId;
if (!flowValue.isEmpty()) {
clientConfig[amnezia::protocols::xray::flow] = flowValue;
if (appendNewClient) {
clientId = QUuid::createUuid().toString(QUuid::WithoutBraces);
QJsonObject clientEntry;
clientEntry[amnezia::protocols::xray::id] = clientId;
if (!flowValue.isEmpty()) {
clientEntry[amnezia::protocols::xray::flow] = flowValue;
}
clients.append(clientEntry);
} else {
if (clients.isEmpty()) {
logger.error() << "Server config has no VLESS clients";
return ErrorCode::XrayServerNoVlessClients;
}
clientId = clients[0].toObject()[amnezia::protocols::xray::id].toString();
if (clientId.isEmpty()) {
logger.error() << "Server config VLESS client has empty id";
return ErrorCode::XrayServerNoVlessClients;
}
QJsonArray updatedClients;
for (const QJsonValue &v : clients) {
QJsonObject c = v.toObject();
if (flowValue.isEmpty()) {
c.remove(amnezia::protocols::xray::flow);
} else {
c[amnezia::protocols::xray::flow] = flowValue;
}
updatedClients.append(c);
}
clients = updatedClients;
}
clients.append(clientConfig);
// Update config
settings[amnezia::protocols::xray::clients] = clients;
inbound[amnezia::protocols::xray::settings] = settings;
inbounds[0] = inbound;
serverConfig[amnezia::protocols::xray::inbounds] = inbounds;
// Save updated config to server
QString updatedConfig = QJsonDocument(serverConfig).toJson();
errorCode = m_sshSession->uploadTextFileToContainer(
container,
credentials,
updatedConfig,
amnezia::protocols::xray::serverConfigPath,
libssh::ScpOverwriteMode::ScpOverwriteExisting
);
errorCode = uploadServerConfigJson(credentials, container, dnsSettings, serverConfig);
if (errorCode != ErrorCode::NoError) {
logger.error() << "Failed to upload updated config";
return "";
logger.error() << "Xray applyServerSettings: upload/restart failed, error=" << static_cast<int>(errorCode);
return errorCode;
}
logger.info() << "Xray applyServerSettings: server config uploaded and container restarted";
if (outClientId) {
*outClientId = clientId;
}
// Restart container
QString restartScript = QString("sudo docker restart $CONTAINER_NAME");
errorCode = m_sshSession->runScript(
credentials,
m_sshSession->replaceVars(restartScript, amnezia::genBaseVars(credentials, container, dnsSettings.primaryDns, dnsSettings.secondaryDns))
);
XrayProtocolConfig updated =
buildClientProtocolConfig(credentials, container, srv, clientId, errorCode, realityPublicKey, realityShortId);
if (errorCode != ErrorCode::NoError) {
logger.error() << "Failed to restart container";
return "";
logger.error() << "Xray applyServerSettings: buildClientProtocolConfig failed, error="
<< static_cast<int>(errorCode);
return errorCode;
}
containerConfig.protocolConfig = updated;
logger.info() << "Xray applyServerSettings: done, clientId=" << clientId;
return ErrorCode::NoError;
}
QString XrayConfigurator::prepareServerConfig(const ServerCredentials &credentials, DockerContainer container,
const ContainerConfig &containerConfig,
const DnsSettings &dnsSettings,
ErrorCode &errorCode)
{
ContainerConfig mutableConfig = containerConfig;
QString clientId;
const ErrorCode applyError =
applyServerSettingsToRemote(credentials, container, mutableConfig, dnsSettings, true, &clientId);
errorCode = applyError;
if (applyError != ErrorCode::NoError || clientId.isEmpty()) {
return QString();
}
return clientId;
}
XrayProtocolConfig XrayConfigurator::buildClientProtocolConfig(const ServerCredentials &credentials,
DockerContainer container,
const XrayServerConfig &srv, const QString &clientId,
ErrorCode &errorCode,
const QString &prefetchedRealityPublicKey,
const QString &prefetchedRealityShortId) const
{
QString xrayPublicKey = prefetchedRealityPublicKey;
QString xrayShortId = prefetchedRealityShortId;
if (srv.security == QLatin1String("reality")) {
if (xrayPublicKey.isEmpty() || xrayShortId.isEmpty()) {
errorCode = readRealityKeyFiles(container, credentials, xrayPublicKey, xrayShortId);
if (errorCode != ErrorCode::NoError) {
return {};
}
}
}
QJsonObject userObj;
userObj[amnezia::protocols::xray::id] = clientId;
userObj[amnezia::protocols::xray::encryption] = QStringLiteral("none");
if (!srv.flow.isEmpty()) {
userObj[amnezia::protocols::xray::flow] = srv.flow;
}
QJsonObject vnextEntry;
vnextEntry[amnezia::protocols::xray::address] = credentials.hostName;
vnextEntry[amnezia::protocols::xray::port] =
srv.port.isEmpty() ? QString(amnezia::protocols::xray::defaultPort).toInt() : srv.port.toInt();
vnextEntry[amnezia::protocols::xray::users] = QJsonArray { userObj };
QJsonObject outboundSettings;
outboundSettings[amnezia::protocols::xray::vnext] = QJsonArray { vnextEntry };
QJsonObject outbound;
outbound[QStringLiteral("protocol")] = QStringLiteral("vless");
outbound[amnezia::protocols::xray::settings] = outboundSettings;
QJsonObject streamObj = buildStreamSettings(srv, clientId);
if (srv.security == QLatin1String("reality")) {
QJsonObject rs = streamObj[amnezia::protocols::xray::realitySettings].toObject();
rs[amnezia::protocols::xray::publicKey] = xrayPublicKey;
rs[amnezia::protocols::xray::shortId] = xrayShortId;
rs[amnezia::protocols::xray::spiderX] = QString();
streamObj[amnezia::protocols::xray::realitySettings] = rs;
}
outbound[amnezia::protocols::xray::streamSettings] = streamObj;
QJsonObject inboundObj;
inboundObj[QStringLiteral("listen")] = amnezia::protocols::xray::defaultLocalListenAddr;
inboundObj[amnezia::protocols::xray::port] = amnezia::protocols::xray::defaultLocalProxyPort;
inboundObj[QStringLiteral("protocol")] = QStringLiteral("socks");
inboundObj[amnezia::protocols::xray::settings] = QJsonObject { { QStringLiteral("udp"), true } };
QJsonObject clientJson;
clientJson[QStringLiteral("log")] = QJsonObject { { QStringLiteral("loglevel"), QStringLiteral("error") } };
clientJson[amnezia::protocols::xray::inbounds] = QJsonArray { inboundObj };
clientJson[amnezia::protocols::xray::outbounds] = QJsonArray { outbound };
const QString config = QString::fromUtf8(QJsonDocument(clientJson).toJson(QJsonDocument::Compact));
XrayProtocolConfig protocolConfig;
protocolConfig.serverConfig = srv;
XrayClientConfig clientConfig;
clientConfig.nativeConfig = config;
clientConfig.localPort = QString(amnezia::protocols::xray::defaultLocalProxyPort);
clientConfig.id = clientId;
protocolConfig.setClientConfig(clientConfig);
return protocolConfig;
}
QJsonObject XrayConfigurator::buildStreamSettings(const XrayServerConfig &srv, const QString &clientId) const
{
QJsonObject streamSettings;
@@ -419,6 +629,13 @@ ProtocolConfig XrayConfigurator::createConfig(const ServerCredentials &credentia
const DnsSettings &dnsSettings,
ErrorCode &errorCode)
{
if (const auto *xrayCfg = containerConfig.protocolConfig.as<XrayProtocolConfig>()) {
if (xrayCfg->serverConfig.isThirdPartyConfig && xrayCfg->hasClientConfig()) {
logger.info() << "Xray createConfig: returning existing third-party client config without server SSH";
return *xrayCfg;
}
}
const XrayServerConfig *serverConfig = nullptr;
if (const auto *xrayCfg = containerConfig.protocolConfig.as<XrayProtocolConfig>()) {
serverConfig = &xrayCfg->serverConfig;
@@ -441,93 +658,5 @@ ProtocolConfig XrayConfigurator::createConfig(const ServerCredentials &credentia
return XrayProtocolConfig{};
}
// Fetch server keys (Reality only)
QString xrayPublicKey;
QString xrayShortId;
if (srv.security == "reality") {
xrayPublicKey = m_sshSession->getTextFileFromContainer(container, credentials,
amnezia::protocols::xray::PublicKeyPath, errorCode);
if (errorCode != ErrorCode::NoError || xrayPublicKey.isEmpty()) {
logger.error() << "Failed to get public key";
if (errorCode == ErrorCode::NoError) {
errorCode = ErrorCode::InternalError;
}
return XrayProtocolConfig{};
}
xrayPublicKey.replace("\n", "");
xrayShortId = m_sshSession->getTextFileFromContainer(container, credentials,
amnezia::protocols::xray::shortidPath, errorCode);
if (errorCode != ErrorCode::NoError || xrayShortId.isEmpty()) {
logger.error() << "Failed to get short ID";
if (errorCode == ErrorCode::NoError) {
errorCode = ErrorCode::InternalError;
}
return XrayProtocolConfig{};
}
xrayShortId.replace("\n", "");
}
// Build outbound
QJsonObject userObj;
userObj[amnezia::protocols::xray::id] = xrayClientId;
userObj[amnezia::protocols::xray::encryption] = "none";
if (!srv.flow.isEmpty()) {
userObj[amnezia::protocols::xray::flow] = srv.flow;
}
QJsonObject vnextEntry;
vnextEntry[amnezia::protocols::xray::address] = credentials.hostName;
vnextEntry[amnezia::protocols::xray::port] = srv.port.toInt();
vnextEntry[amnezia::protocols::xray::users] = QJsonArray { userObj };
QJsonObject outboundSettings;
outboundSettings[amnezia::protocols::xray::vnext] = QJsonArray { vnextEntry };
QJsonObject outbound;
outbound["protocol"] = "vless";
outbound[amnezia::protocols::xray::settings] = outboundSettings;
// Build streamSettings
QJsonObject streamObj = buildStreamSettings(srv, xrayClientId);
// Inject Reality keys
if (srv.security == "reality") {
QJsonObject rs = streamObj[amnezia::protocols::xray::realitySettings].toObject();
rs[amnezia::protocols::xray::publicKey] = xrayPublicKey;
rs[amnezia::protocols::xray::shortId] = xrayShortId;
rs[amnezia::protocols::xray::spiderX] = "";
streamObj[amnezia::protocols::xray::realitySettings] = rs;
}
outbound[amnezia::protocols::xray::streamSettings] = streamObj;
// Build full client config
QJsonObject inboundObj;
inboundObj["listen"] = amnezia::protocols::xray::defaultLocalListenAddr;
inboundObj[amnezia::protocols::xray::port] = amnezia::protocols::xray::defaultLocalProxyPort;
inboundObj["protocol"] = "socks";
inboundObj[amnezia::protocols::xray::settings] = QJsonObject { { "udp", true } };
QJsonObject clientJson;
clientJson["log"] = QJsonObject { { "loglevel", "error" } };
clientJson[amnezia::protocols::xray::inbounds] = QJsonArray { inboundObj };
clientJson[amnezia::protocols::xray::outbounds] = QJsonArray { outbound };
QString config = QString::fromUtf8(QJsonDocument(clientJson).toJson(QJsonDocument::Compact));
// Return
XrayProtocolConfig protocolConfig;
protocolConfig.serverConfig = srv;
XrayClientConfig clientConfig;
clientConfig.nativeConfig = config;
qDebug() << "config:" << config;
clientConfig.localPort = QString(amnezia::protocols::xray::defaultLocalProxyPort);
clientConfig.id = xrayClientId;
protocolConfig.setClientConfig(clientConfig);
return protocolConfig;
return buildClientProtocolConfig(credentials, container, srv, xrayClientId, errorCode);
}
+26 -1
View File
@@ -23,12 +23,37 @@ public:
amnezia::ProtocolConfig processConfigWithLocalSettings(const amnezia::ConnectionSettings &settings,
amnezia::ProtocolConfig protocolConfig) override;
amnezia::ErrorCode applyServerSettingsToRemote(const amnezia::ServerCredentials &credentials,
amnezia::DockerContainer container,
amnezia::ContainerConfig &containerConfig,
const amnezia::DnsSettings &dnsSettings,
bool appendNewClient,
QString *outClientId = nullptr);
private:
QString prepareServerConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container, const amnezia::ContainerConfig &containerConfig,
const amnezia::DnsSettings &dnsSettings,
amnezia::ErrorCode &errorCode);
// Builds the native xray "streamSettings" JSON object from XrayServerConfig
amnezia::ErrorCode uploadServerConfigJson(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container,
const amnezia::DnsSettings &dnsSettings, const QJsonObject &serverConfig) const;
amnezia::XrayProtocolConfig buildClientProtocolConfig(const amnezia::ServerCredentials &credentials,
amnezia::DockerContainer container,
const amnezia::XrayServerConfig &srv,
const QString &clientId,
amnezia::ErrorCode &errorCode,
const QString &prefetchedRealityPublicKey = {},
const QString &prefetchedRealityShortId = {}) const;
amnezia::ErrorCode readRealityKeyFiles(amnezia::DockerContainer container,
const amnezia::ServerCredentials &credentials,
QString &outPublicKey,
QString &outShortId) const;
QJsonObject mergeStreamSettingsForServerInbound(const amnezia::XrayServerConfig &srv,
const QJsonObject &existingStreamSettings) const;
QJsonObject buildStreamSettings(const amnezia::XrayServerConfig &srv,
const QString &clientId) const;
};
@@ -6,9 +6,7 @@
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/utils/utilities.h"
#include "core/utils/networkUtilities.h"
#include "core/utils/serverConfigUtils.h"
#include "version.h"
#include "core/utils/containerEnum.h"
@@ -67,13 +65,15 @@ ErrorCode ConnectionController::prepareConnection(const QString &serverId,
bool isApiConfig = false;
const auto kind = m_serversRepository->serverKind(serverId);
const QString primaryDns = m_appSettingsRepository->primaryDns();
const QString secondaryDns = m_appSettingsRepository->secondaryDns();
switch (kind) {
case serverConfigUtils::ConfigType::SelfHostedAdmin: {
const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId);
if (!cfg.has_value()) return ErrorCode::InternalError;
container = cfg->defaultContainer;
containerConfigModel = cfg->containerConfig(container);
dns = { cfg->dns1, cfg->dns2 };
dns = cfg->getDnsPair(m_appSettingsRepository->useAmneziaDns(), primaryDns, secondaryDns);
hostName = cfg->hostName;
description = cfg->description;
break;
@@ -83,7 +83,7 @@ ErrorCode ConnectionController::prepareConnection(const QString &serverId,
if (!cfg.has_value()) return ErrorCode::InternalError;
container = cfg->defaultContainer;
containerConfigModel = cfg->containerConfig(container);
dns = { cfg->dns1, cfg->dns2 };
dns = cfg->getDnsPair(primaryDns, secondaryDns);
hostName = cfg->hostName;
description = cfg->description;
break;
@@ -93,7 +93,7 @@ ErrorCode ConnectionController::prepareConnection(const QString &serverId,
if (!cfg.has_value()) return ErrorCode::InternalError;
container = cfg->defaultContainer;
containerConfigModel = cfg->containerConfig(container);
dns = { cfg->dns1, cfg->dns2 };
dns = cfg->getDnsPair(primaryDns, secondaryDns);
hostName = cfg->hostName;
description = cfg->description;
break;
@@ -105,7 +105,7 @@ ErrorCode ConnectionController::prepareConnection(const QString &serverId,
if (!cfg.has_value()) return ErrorCode::InternalError;
container = cfg->defaultContainer;
containerConfigModel = cfg->containerConfig(container);
dns = { cfg->dns1, cfg->dns2 };
dns = cfg->getDnsPair(primaryDns, secondaryDns);
hostName = cfg->hostName;
description = cfg->description;
configVersion = serverConfigUtils::ConfigSource::AmneziaGateway;
@@ -123,16 +123,6 @@ ErrorCode ConnectionController::prepareConnection(const QString &serverId,
if (!isContainerSupported(container)) {
return ErrorCode::NotSupportedOnThisPlatform;
}
if (dns.first.isEmpty() || !NetworkUtilities::checkIPv4Format(dns.first)) {
if (m_appSettingsRepository->useAmneziaDns()) {
dns.first = protocols::dns::amneziaDnsIp;
} else {
dns.first = m_appSettingsRepository->primaryDns();
}
}
if (dns.second.isEmpty() || !NetworkUtilities::checkIPv4Format(dns.second)) {
dns.second = m_appSettingsRepository->secondaryDns();
}
vpnConfiguration = createConnectionConfiguration(dns, isApiConfig, hostName, description, configVersion,
containerConfigModel, container);
+4 -5
View File
@@ -178,7 +178,8 @@ void CoreController::initControllers()
#ifdef Q_OS_WINDOWS
m_ikev2ConfigModel,
#endif
m_sftpConfigModel, m_socks5ConfigModel, m_mtProxyConfigModel, m_telemtConfigModel, this);
m_sftpConfigModel, m_socks5ConfigModel, m_mtProxyConfigModel, m_telemtConfigModel,
m_connectionController, this);
setQmlContextProperty("InstallController", m_installUiController);
m_importController = new ImportUiController(m_importCoreController, this);
@@ -220,7 +221,8 @@ void CoreController::initControllers()
m_subscriptionUiController = new SubscriptionUiController(m_serversController, m_apiServicesModel, m_servicesCatalogController, m_subscriptionController,
m_apiSubscriptionPlansModel, m_apiBenefitsModel, m_apiAccountInfoModel,
m_apiCountryModel, m_apiDevicesModel, m_settingsController, this);
m_apiCountryModel, m_apiDevicesModel, m_settingsController,
m_connectionController, this);
setQmlContextProperty("SubscriptionUiController", m_subscriptionUiController);
m_apiNewsUiController = new ApiNewsUiController(m_newsModel, m_newsController, this);
@@ -342,9 +344,6 @@ void CoreController::openConnectionByIndex(int serverIndex)
if (serverId.isEmpty()) {
return;
}
if (m_serversModel) {
m_serversModel->setProcessedServerIndex(serverIndex);
}
if (m_serversController) {
m_serversController->setDefaultServer(serverId);
}
@@ -125,9 +125,9 @@ void CoreSignalHandlers::initInstallControllerHandler()
{
connect(m_coreController->m_installController, &InstallController::serverIsBusy, m_coreController->m_installUiController, &InstallUiController::serverIsBusy);
connect(m_coreController->m_installUiController, &InstallUiController::cancelInstallation, m_coreController->m_installController, &InstallController::cancelInstallation);
connect(m_coreController->m_serversUiController, &ServersUiController::processedServerIndexChanged,
m_coreController->m_installUiController, [this](int serverIndex) {
if (serverIndex >= 0) {
connect(m_coreController->m_serversUiController, &ServersUiController::processedServerIdChanged,
m_coreController->m_installUiController, [this](const QString &serverId) {
if (!serverId.isEmpty()) {
m_coreController->m_installUiController->clearProcessedServerCredentials();
}
});
@@ -20,6 +20,7 @@
#include "core/installers/sftpInstaller.h"
#include "core/installers/socks5Installer.h"
#include "core/installers/mtProxyInstaller.h"
#include "core/configurators/xrayConfigurator.h"
#include "core/installers/telemtInstaller.h"
#include "core/installers/torInstaller.h"
#include "core/installers/wireguardInstaller.h"
@@ -119,9 +120,14 @@ ErrorCode InstallController::setupContainer(const ServerCredentials &credentials
return e;
qDebug().noquote() << "InstallController::setupContainer prepareHostWorker finished";
amnezia::ScriptVars removeContainerVars =
amnezia::genBaseVars(credentials, container, QString(), QString());
if (!isUpdate) {
removeContainerVars.append({ { "$REMOVE_CONTAINER_DATA", QStringLiteral("1") } });
}
sshSession.runScript(credentials,
sshSession.replaceVars(amnezia::scriptData(SharedScriptType::remove_container),
amnezia::genBaseVars(credentials, container, QString(), QString())));
sshSession.replaceVars(amnezia::scriptData(SharedScriptType::remove_container),
removeContainerVars));
qDebug().noquote() << "InstallController::setupContainer removeContainer finished";
qDebug().noquote() << "buildContainerWorker start";
@@ -181,6 +187,16 @@ ErrorCode InstallController::updateContainer(const QString &serverId, DockerCont
bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig);
qDebug() << "InstallController::updateContainer for container" << container << "reinstall required is" << reinstallRequired;
bool xrayServerSettingsChanged = false;
if (container == DockerContainer::Xray || container == DockerContainer::SSXray) {
const auto *oldXrayConfig = oldConfig.getXrayProtocolConfig();
const auto *newXrayConfig = newConfig.getXrayProtocolConfig();
if (oldXrayConfig && newXrayConfig) {
xrayServerSettingsChanged =
!oldXrayConfig->serverConfig.hasEqualServerSettings(newXrayConfig->serverConfig);
}
}
ErrorCode errorCode = ErrorCode::NoError;
if (reinstallRequired) {
errorCode = setupContainer(credentials, container, newConfig, true);
@@ -191,6 +207,21 @@ ErrorCode InstallController::updateContainer(const QString &serverId, DockerCont
}
}
const bool skipXrayInboundSync =
newConfig.getXrayProtocolConfig() && newConfig.getXrayProtocolConfig()->serverConfig.isThirdPartyConfig;
if (errorCode == ErrorCode::NoError && xrayServerSettingsChanged && !skipXrayInboundSync) {
DnsSettings dnsSettings = { m_appSettingsRepository->primaryDns(), m_appSettingsRepository->secondaryDns() };
XrayConfigurator xrayConfigurator(&sshSession);
qDebug() << "InstallController::updateContainer applying Xray server inbound sync, reinstall="
<< reinstallRequired;
errorCode = xrayConfigurator.applyServerSettingsToRemote(credentials, container, newConfig, dnsSettings, false);
if (errorCode != ErrorCode::NoError) {
qDebug() << "InstallController::updateContainer Xray inbound sync failed, error="
<< static_cast<int>(errorCode);
}
}
if (errorCode == ErrorCode::NoError) {
if (container == DockerContainer::MtProxy) {
MtProxyInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
@@ -216,9 +247,9 @@ void InstallController::clearCachedProfile(const QString &serverId, DockerContai
return;
}
adminConfig->clearCachedClientProfile(container);
const ContainerConfig containerConfigModel = adminConfig->containerConfig(container);
adminConfig->clearCachedClientProfile(container);
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
emit clientRevocationRequested(serverId, containerConfigModel, container);
@@ -638,12 +669,19 @@ bool InstallController::isReinstallContainerRequired(DockerContainer container,
}
if (container == DockerContainer::Xray || container == DockerContainer::SSXray) {
const auto* oldXrayConfig = oldConfig.getXrayProtocolConfig();
const auto* newXrayConfig = newConfig.getXrayProtocolConfig();
const auto *oldXrayConfig = oldConfig.getXrayProtocolConfig();
const auto *newXrayConfig = newConfig.getXrayProtocolConfig();
if (oldXrayConfig && newXrayConfig) {
if (oldXrayConfig->serverConfig.port != newXrayConfig->serverConfig.port)
const QString oldPort = oldXrayConfig->serverConfig.port.isEmpty()
? QString(protocols::xray::defaultPort)
: oldXrayConfig->serverConfig.port;
const QString newPort = newXrayConfig->serverConfig.port.isEmpty()
? QString(protocols::xray::defaultPort)
: newXrayConfig->serverConfig.port;
if (oldPort != newPort) {
return true;
}
}
}
@@ -942,10 +980,12 @@ ErrorCode InstallController::removeContainer(const QString &serverId, DockerCont
return ErrorCode::InternalError;
}
SshSession sshSession(this);
amnezia::ScriptVars removeContainerVars =
amnezia::genBaseVars(credentials, container, QString(), QString());
removeContainerVars.append({ { "$REMOVE_CONTAINER_DATA", QStringLiteral("1") } });
ErrorCode errorCode = sshSession.runScript(
credentials,
sshSession.replaceVars(amnezia::scriptData(SharedScriptType::remove_container),
amnezia::genBaseVars(credentials, container, QString(), QString())));
sshSession.replaceVars(amnezia::scriptData(SharedScriptType::remove_container), removeContainerVars));
if (errorCode == ErrorCode::NoError) {
QMap<DockerContainer, ContainerConfig> containers = adminConfig->containers;
@@ -217,6 +217,11 @@ void SettingsController::toggleAutoStart(bool enable)
bool SettingsController::isStartMinimizedEnabled() const
{
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
if (!isAutoStartEnabled()) {
return false;
}
#endif
return m_appSettingsRepository->isStartMinimized();
}
+27 -6
View File
@@ -1,15 +1,17 @@
#include "socks5Installer.h"
#include "core/models/protocols/socks5ProxyProtocolConfig.h"
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/utils/utilities.h"
#include <QRegularExpression>
using namespace amnezia;
using namespace ProtocolUtils;
@@ -33,10 +35,29 @@ ContainerConfig Socks5Installer::generateConfig(DockerContainer container, int p
ErrorCode Socks5Installer::extractConfigFromContainer(DockerContainer container, const ServerCredentials &credentials,
SshSession* sshSession, ContainerConfig &config)
{
Q_UNUSED(container);
Q_UNUSED(credentials);
Q_UNUSED(sshSession);
Q_UNUSED(config);
if (container != DockerContainer::Socks5Proxy || !sshSession) {
return ErrorCode::NoError;
}
Socks5ProxyProtocolConfig *socks5Config = config.getSocks5ProxyProtocolConfig();
if (!socks5Config) {
return ErrorCode::NoError;
}
ErrorCode readError = ErrorCode::NoError;
const QByteArray configRaw = sshSession->getTextFileFromContainer(
container, credentials, QString::fromUtf8(protocols::socks5Proxy::proxyConfigPath), readError);
if (readError != ErrorCode::NoError || configRaw.trimmed().isEmpty()) {
return ErrorCode::NoError;
}
const QString proxyConfig = QString::fromUtf8(configRaw);
static const QRegularExpression usernameAndPasswordRegExp(QStringLiteral("users (\\w+):CL:(\\w+)"));
const QRegularExpressionMatch usernameAndPasswordMatch = usernameAndPasswordRegExp.match(proxyConfig);
if (usernameAndPasswordMatch.hasMatch()) {
socks5Config->userName = usernameAndPasswordMatch.captured(1);
socks5Config->password = usernameAndPasswordMatch.captured(2);
}
return ErrorCode::NoError;
}
@@ -13,6 +13,7 @@
#include "core/utils/api/apiUtils.h"
#include "core/models/api/apiConfig.h"
#include "core/models/api/authData.h"
#include "core/utils/networkUtilities.h"
namespace amnezia
{
@@ -67,6 +68,20 @@ ContainerConfig ApiV2ServerConfig::containerConfig(DockerContainer container) co
return containers.value(container);
}
QPair<QString, QString> ApiV2ServerConfig::getDnsPair(const QString &primaryDns, const QString &secondaryDns) const
{
QString d1 = dns1;
QString d2 = dns2;
if (d1.isEmpty() || !NetworkUtilities::checkIPv4Format(d1)) {
d1 = primaryDns;
}
if (d2.isEmpty() || !NetworkUtilities::checkIPv4Format(d2)) {
d2 = secondaryDns;
}
return { d1, d2 };
}
QJsonObject ApiV2ServerConfig::toJson() const
{
QJsonObject obj;
@@ -3,6 +3,7 @@
#include <QJsonObject>
#include <QMap>
#include <QPair>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
@@ -43,6 +44,9 @@ struct ApiV2ServerConfig {
bool isExternalPremium() const;
bool hasContainers() const;
ContainerConfig containerConfig(DockerContainer container) const;
QPair<QString, QString> getDnsPair(const QString &primaryDns, const QString &secondaryDns) const;
QJsonObject toJson() const;
static ApiV2ServerConfig fromJson(const QJsonObject& json);
};
@@ -108,35 +108,114 @@ QJsonObject XrayXhttpConfig::toJson() const
return obj;
}
namespace
{
XrayXhttpConfig clearedXhttpConfig()
{
XrayXhttpConfig c;
c.mode = QString();
c.host = QString();
c.path = QString();
c.headersTemplate = QString();
c.uplinkMethod = QString();
c.disableGrpc = false;
c.disableSse = false;
c.sessionPlacement = QString();
c.sessionKey = QString();
c.seqPlacement = QString();
c.seqKey = QString();
c.uplinkDataPlacement = QString();
c.uplinkDataKey = QString();
c.uplinkChunkSize = QString();
c.scMaxBufferedPosts = QString();
c.scMaxEachPostBytesMin = QString();
c.scMaxEachPostBytesMax = QString();
c.scMinPostsIntervalMsMin = QString();
c.scMinPostsIntervalMsMax = QString();
c.scStreamUpServerSecsMin = QString();
c.scStreamUpServerSecsMax = QString();
return c;
}
} // namespace
XrayXhttpConfig XrayXhttpConfig::fromJson(const QJsonObject &json)
{
XrayXhttpConfig c;
c.mode = json.value(configKey::xhttpMode).toString(protocols::xray::defaultXhttpMode);
c.host = json.value(configKey::xhttpHost).toString(protocols::xray::defaultSite);
c.path = json.value(configKey::xhttpPath).toString();
c.headersTemplate = json.value(configKey::xhttpHeadersTemplate).toString(protocols::xray::defaultXhttpHeadersTemplate);
c.uplinkMethod = json.value(configKey::xhttpUplinkMethod).toString(protocols::xray::defaultXhttpUplinkMethod);
c.disableGrpc = json.value(configKey::xhttpDisableGrpc).toBool(true);
c.disableSse = json.value(configKey::xhttpDisableSse).toBool(true);
if (json.isEmpty()) {
return clearedXhttpConfig();
}
c.sessionPlacement = json.value(configKey::xhttpSessionPlacement).toString(protocols::xray::defaultXhttpSessionPlacement);
c.sessionKey = json.value(configKey::xhttpSessionKey).toString();
c.seqPlacement = json.value(configKey::xhttpSeqPlacement).toString(protocols::xray::defaultXhttpSessionPlacement);
c.seqKey = json.value(configKey::xhttpSeqKey).toString();
c.uplinkDataPlacement = json.value(configKey::xhttpUplinkDataPlacement).toString(protocols::xray::defaultXhttpUplinkDataPlacement);
c.uplinkDataKey = json.value(configKey::xhttpUplinkDataKey).toString();
XrayXhttpConfig c = clearedXhttpConfig();
c.uplinkChunkSize = json.value(configKey::xhttpUplinkChunkSize).toString("0");
c.scMaxBufferedPosts = json.value(configKey::xhttpScMaxBufferedPosts).toString();
c.scMaxEachPostBytesMin = json.value(configKey::xhttpScMaxEachPostBytesMin).toString("1");
c.scMaxEachPostBytesMax = json.value(configKey::xhttpScMaxEachPostBytesMax).toString("100");
c.scMinPostsIntervalMsMin = json.value(configKey::xhttpScMinPostsIntervalMsMin).toString("100");
c.scMinPostsIntervalMsMax = json.value(configKey::xhttpScMinPostsIntervalMsMax).toString("800");
c.scStreamUpServerSecsMin = json.value(configKey::xhttpScStreamUpServerSecsMin).toString("1");
c.scStreamUpServerSecsMax = json.value(configKey::xhttpScStreamUpServerSecsMax).toString("100");
if (json.contains(configKey::xhttpMode)) {
c.mode = json.value(configKey::xhttpMode).toString();
}
if (json.contains(configKey::xhttpHost)) {
c.host = json.value(configKey::xhttpHost).toString();
}
if (json.contains(configKey::xhttpPath)) {
c.path = json.value(configKey::xhttpPath).toString();
}
if (json.contains(configKey::xhttpHeadersTemplate)) {
c.headersTemplate = json.value(configKey::xhttpHeadersTemplate).toString();
}
if (json.contains(configKey::xhttpUplinkMethod)) {
c.uplinkMethod = json.value(configKey::xhttpUplinkMethod).toString();
}
if (json.contains(configKey::xhttpDisableGrpc)) {
c.disableGrpc = json.value(configKey::xhttpDisableGrpc).toBool();
}
if (json.contains(configKey::xhttpDisableSse)) {
c.disableSse = json.value(configKey::xhttpDisableSse).toBool();
}
if (json.contains(configKey::xhttpSessionPlacement)) {
c.sessionPlacement = json.value(configKey::xhttpSessionPlacement).toString();
}
if (json.contains(configKey::xhttpSessionKey)) {
c.sessionKey = json.value(configKey::xhttpSessionKey).toString();
}
if (json.contains(configKey::xhttpSeqPlacement)) {
c.seqPlacement = json.value(configKey::xhttpSeqPlacement).toString();
}
if (json.contains(configKey::xhttpSeqKey)) {
c.seqKey = json.value(configKey::xhttpSeqKey).toString();
}
if (json.contains(configKey::xhttpUplinkDataPlacement)) {
c.uplinkDataPlacement = json.value(configKey::xhttpUplinkDataPlacement).toString();
}
if (json.contains(configKey::xhttpUplinkDataKey)) {
c.uplinkDataKey = json.value(configKey::xhttpUplinkDataKey).toString();
}
if (json.contains(configKey::xhttpUplinkChunkSize)) {
c.uplinkChunkSize = json.value(configKey::xhttpUplinkChunkSize).toString();
}
if (json.contains(configKey::xhttpScMaxBufferedPosts)) {
c.scMaxBufferedPosts = json.value(configKey::xhttpScMaxBufferedPosts).toString();
}
if (json.contains(configKey::xhttpScMaxEachPostBytesMin)) {
c.scMaxEachPostBytesMin = json.value(configKey::xhttpScMaxEachPostBytesMin).toString();
}
if (json.contains(configKey::xhttpScMaxEachPostBytesMax)) {
c.scMaxEachPostBytesMax = json.value(configKey::xhttpScMaxEachPostBytesMax).toString();
}
if (json.contains(configKey::xhttpScMinPostsIntervalMsMin)) {
c.scMinPostsIntervalMsMin = json.value(configKey::xhttpScMinPostsIntervalMsMin).toString();
}
if (json.contains(configKey::xhttpScMinPostsIntervalMsMax)) {
c.scMinPostsIntervalMsMax = json.value(configKey::xhttpScMinPostsIntervalMsMax).toString();
}
if (json.contains(configKey::xhttpScStreamUpServerSecsMin)) {
c.scStreamUpServerSecsMin = json.value(configKey::xhttpScStreamUpServerSecsMin).toString();
}
if (json.contains(configKey::xhttpScStreamUpServerSecsMax)) {
c.scStreamUpServerSecsMax = json.value(configKey::xhttpScStreamUpServerSecsMax).toString();
}
c.xPadding = XrayXPaddingConfig::fromJson(json.value("xPadding").toObject());
c.xmux = XrayXmuxConfig::fromJson(json.value("xmux").toObject());
if (json.contains(QLatin1String("xPadding"))) {
c.xPadding = XrayXPaddingConfig::fromJson(json.value(QLatin1String("xPadding")).toObject());
}
if (json.contains(QLatin1String("xmux"))) {
c.xmux = XrayXmuxConfig::fromJson(json.value(QLatin1String("xmux")).toObject());
}
return c;
}
@@ -156,12 +235,27 @@ QJsonObject XrayMkcpConfig::toJson() const
XrayMkcpConfig XrayMkcpConfig::fromJson(const QJsonObject &json)
{
XrayMkcpConfig c;
c.tti = json.value(configKey::mkcpTti).toString();
c.uplinkCapacity = json.value(configKey::mkcpUplinkCapacity).toString();
c.downlinkCapacity = json.value(configKey::mkcpDownlinkCapacity).toString();
c.readBufferSize = json.value(configKey::mkcpReadBufferSize).toString();
c.writeBufferSize = json.value(configKey::mkcpWriteBufferSize).toString();
c.congestion = json.value(configKey::mkcpCongestion).toBool(true);
if (json.isEmpty()) {
return c;
}
if (json.contains(configKey::mkcpTti)) {
c.tti = json.value(configKey::mkcpTti).toString();
}
if (json.contains(configKey::mkcpUplinkCapacity)) {
c.uplinkCapacity = json.value(configKey::mkcpUplinkCapacity).toString();
}
if (json.contains(configKey::mkcpDownlinkCapacity)) {
c.downlinkCapacity = json.value(configKey::mkcpDownlinkCapacity).toString();
}
if (json.contains(configKey::mkcpReadBufferSize)) {
c.readBufferSize = json.value(configKey::mkcpReadBufferSize).toString();
}
if (json.contains(configKey::mkcpWriteBufferSize)) {
c.writeBufferSize = json.value(configKey::mkcpWriteBufferSize).toString();
}
if (json.contains(configKey::mkcpCongestion)) {
c.congestion = json.value(configKey::mkcpCongestion).toBool();
}
return c;
}
@@ -208,8 +302,14 @@ QJsonObject XrayServerConfig::toJson() const
if (!transport.isEmpty()) {
obj[configKey::xrayTransport] = transport;
}
obj["xhttp"] = xhttp.toJson();
obj["mkcp"] = mkcp.toJson();
const QJsonObject xhttpObj = xhttp.toJson();
if (!xhttpObj.isEmpty()) {
obj[QStringLiteral("xhttp")] = xhttpObj;
}
const QJsonObject mkcpObj = mkcp.toJson();
if (!mkcpObj.isEmpty()) {
obj[QStringLiteral("mkcp")] = mkcpObj;
}
return obj;
}
@@ -225,20 +325,39 @@ XrayServerConfig XrayServerConfig::fromJson(const QJsonObject &json)
c.site = json.value(configKey::site).toString();
c.isThirdPartyConfig = json.value(configKey::isThirdPartyConfig).toBool(false);
// New: Security
c.security = json.value(configKey::xraySecurity).toString(protocols::xray::defaultSecurity);
c.flow = json.value(configKey::xrayFlow).toString(protocols::xray::defaultFlow);
c.fingerprint = json.value(configKey::xrayFingerprint).toString(protocols::xray::defaultFingerprint);
if (c.fingerprint.contains(QLatin1String("Mozilla/5.0"), Qt::CaseInsensitive)) {
c.fingerprint = QString::fromLatin1(protocols::xray::defaultFingerprint);
if (json.contains(configKey::xraySecurity)) {
c.security = json.value(configKey::xraySecurity).toString();
}
if (json.contains(configKey::xrayFlow)) {
c.flow = json.value(configKey::xrayFlow).toString();
}
if (json.contains(configKey::xrayFingerprint)) {
c.fingerprint = json.value(configKey::xrayFingerprint).toString();
if (c.fingerprint.contains(QLatin1String("Mozilla/5.0"), Qt::CaseInsensitive)) {
c.fingerprint = QString::fromLatin1(protocols::xray::defaultFingerprint);
}
}
if (json.contains(configKey::xraySni)) {
c.sni = json.value(configKey::xraySni).toString();
}
if (json.contains(configKey::xrayAlpn)) {
c.alpn = json.value(configKey::xrayAlpn).toString();
}
if (json.contains(configKey::xrayTransport)) {
c.transport = json.value(configKey::xrayTransport).toString();
}
if (json.contains(QLatin1String("xhttp"))) {
const QJsonObject xhttpJson = json.value(QLatin1String("xhttp")).toObject();
if (!xhttpJson.isEmpty()) {
c.xhttp = XrayXhttpConfig::fromJson(xhttpJson);
}
}
if (json.contains(QLatin1String("mkcp"))) {
const QJsonObject mkcpJson = json.value(QLatin1String("mkcp")).toObject();
if (!mkcpJson.isEmpty()) {
c.mkcp = XrayMkcpConfig::fromJson(mkcpJson);
}
}
c.sni = json.value(configKey::xraySni).toString(protocols::xray::defaultSni);
c.alpn = json.value(configKey::xrayAlpn).toString(protocols::xray::defaultAlpn);
// New: Transport
c.transport = json.value(configKey::xrayTransport).toString(protocols::xray::defaultTransport);
c.xhttp = XrayXhttpConfig::fromJson(json.value("xhttp").toObject());
c.mkcp = XrayMkcpConfig::fromJson(json.value("mkcp").toObject());
return c;
}
@@ -251,7 +370,10 @@ bool XrayServerConfig::hasEqualServerSettings(const XrayServerConfig &other) con
&& flow == other.flow
&& transport == other.transport
&& fingerprint == other.fingerprint
&& sni == other.sni;
&& sni == other.sni
&& alpn == other.alpn
&& xhttp.toJson() == other.xhttp.toJson()
&& mkcp.toJson() == other.mkcp.toJson();
}
QJsonObject XrayClientConfig::toJson() const
@@ -351,9 +473,154 @@ XrayProtocolConfig XrayProtocolConfig::fromJson(const QJsonObject &json)
}
}
c.needsClientHydration =
c.hasClientConfig()
&& (!json.contains(configKey::xrayTransport) || c.serverConfig.isThirdPartyConfig);
if (c.needsClientHydration) {
c.hydrateServerConfigFromClientNative();
}
return c;
}
bool XrayProtocolConfig::hydrateServerConfigFromClientNative()
{
if (!clientConfig.has_value() || clientConfig->nativeConfig.isEmpty()) {
return false;
}
QJsonDocument doc = QJsonDocument::fromJson(clientConfig->nativeConfig.toUtf8());
if (doc.isNull() || !doc.isObject()) {
return false;
}
const QJsonObject root = doc.object();
const QJsonArray outbounds = root.value(protocols::xray::outbounds).toArray();
if (outbounds.isEmpty()) {
return false;
}
const QJsonObject outbound = outbounds[0].toObject();
const QJsonObject streamSettings = outbound.value(protocols::xray::streamSettings).toObject();
if (streamSettings.isEmpty()) {
return false;
}
XrayServerConfig &srv = serverConfig;
const QJsonObject settings = outbound.value(protocols::xray::settings).toObject();
const QJsonArray vnext = settings.value(protocols::xray::vnext).toArray();
if (!vnext.isEmpty()) {
const QJsonObject vnextEntry = vnext[0].toObject();
if (vnextEntry.contains(protocols::xray::port)) {
srv.port = QString::number(vnextEntry.value(protocols::xray::port).toInt());
}
const QJsonArray users = vnextEntry.value(protocols::xray::users).toArray();
if (!users.isEmpty()) {
srv.flow = users[0].toObject().value(protocols::xray::flow).toString();
}
}
const QString networkVal = streamSettings.value(protocols::xray::network).toString(QStringLiteral("tcp"));
if (networkVal == QLatin1String("xhttp")) {
srv.transport = QStringLiteral("xhttp");
} else if (networkVal == QLatin1String("kcp")) {
srv.transport = QStringLiteral("mkcp");
} else {
srv.transport = QStringLiteral("raw");
}
if (streamSettings.contains(protocols::xray::security)) {
srv.security = streamSettings.value(protocols::xray::security).toString();
}
if (srv.security == QLatin1String("reality")) {
const QJsonObject rs = streamSettings.value(protocols::xray::realitySettings).toObject();
srv.sni = rs.value(protocols::xray::serverName).toString();
srv.site = srv.sni.isEmpty() ? srv.site : srv.sni;
const QString fp = rs.value(protocols::xray::fingerprint).toString();
if (!fp.isEmpty()) {
srv.fingerprint = fp.contains(QLatin1String("Mozilla/5.0"), Qt::CaseInsensitive)
? QString::fromLatin1(protocols::xray::defaultFingerprint)
: fp;
}
}
if (srv.security == QLatin1String("tls")) {
const QJsonObject tls = streamSettings.value(QStringLiteral("tlsSettings")).toObject();
srv.sni = tls.value(protocols::xray::serverName).toString();
const QString fp = tls.value(protocols::xray::fingerprint).toString();
if (!fp.isEmpty()) {
srv.fingerprint = fp;
}
QStringList alpnList;
for (const QJsonValue &v : tls.value(QStringLiteral("alpn")).toArray()) {
alpnList << v.toString();
}
if (!alpnList.isEmpty()) {
srv.alpn = alpnList.join(QLatin1Char(','));
}
}
if (srv.transport == QLatin1String("xhttp")) {
const QJsonObject xhttpObj = streamSettings.value(QStringLiteral("xhttpSettings")).toObject();
QJsonObject xhttpJson;
const QString mode = xhttpObj.value(QStringLiteral("mode")).toString();
if (!mode.isEmpty()) {
if (mode == QLatin1String("auto")) {
xhttpJson[configKey::xhttpMode] = QStringLiteral("Auto");
} else if (mode == QLatin1String("packet-up")) {
xhttpJson[configKey::xhttpMode] = QStringLiteral("Packet-up");
} else if (mode == QLatin1String("stream-up")) {
xhttpJson[configKey::xhttpMode] = QStringLiteral("Stream-up");
} else if (mode == QLatin1String("stream-one")) {
xhttpJson[configKey::xhttpMode] = QStringLiteral("Stream-one");
} else {
xhttpJson[configKey::xhttpMode] = mode;
}
}
if (xhttpObj.contains(QStringLiteral("host"))) {
xhttpJson[configKey::xhttpHost] = xhttpObj.value(QStringLiteral("host")).toString();
}
if (xhttpObj.contains(QStringLiteral("path"))) {
xhttpJson[configKey::xhttpPath] = xhttpObj.value(QStringLiteral("path")).toString();
}
if (xhttpObj.contains(QStringLiteral("uplinkHTTPMethod"))) {
xhttpJson[configKey::xhttpUplinkMethod] = xhttpObj.value(QStringLiteral("uplinkHTTPMethod")).toString();
}
xhttpJson[configKey::xhttpDisableGrpc] = xhttpObj.value(QStringLiteral("noGRPCHeader")).toBool(true);
xhttpJson[configKey::xhttpDisableSse] = xhttpObj.value(QStringLiteral("noSSEHeader")).toBool(true);
srv.xhttp = XrayXhttpConfig::fromJson(xhttpJson);
}
if (srv.transport == QLatin1String("mkcp")) {
const QJsonObject kcpObj = streamSettings.value(QStringLiteral("kcpSettings")).toObject();
XrayMkcpConfig mk;
if (kcpObj.contains(QStringLiteral("tti"))) {
mk.tti = QString::number(kcpObj.value(QStringLiteral("tti")).toInt());
}
if (kcpObj.contains(QStringLiteral("uplinkCapacity"))) {
mk.uplinkCapacity = QString::number(kcpObj.value(QStringLiteral("uplinkCapacity")).toInt());
}
if (kcpObj.contains(QStringLiteral("downlinkCapacity"))) {
mk.downlinkCapacity = QString::number(kcpObj.value(QStringLiteral("downlinkCapacity")).toInt());
}
if (kcpObj.contains(QStringLiteral("readBufferSize"))) {
mk.readBufferSize = QString::number(kcpObj.value(QStringLiteral("readBufferSize")).toInt());
}
if (kcpObj.contains(QStringLiteral("writeBufferSize"))) {
mk.writeBufferSize = QString::number(kcpObj.value(QStringLiteral("writeBufferSize")).toInt());
}
if (kcpObj.contains(QStringLiteral("congestion"))) {
mk.congestion = kcpObj.value(QStringLiteral("congestion")).toBool(true);
}
srv.mkcp = mk;
}
needsClientHydration = false;
return true;
}
bool XrayProtocolConfig::hasClientConfig() const
{
return clientConfig.has_value();
@@ -75,6 +75,7 @@ struct XrayXhttpConfig {
XrayXmuxConfig xmux;
QJsonObject toJson() const;
/// Reads only keys present in JSON (no Amnezia UI defaults). Use XrayConfigModel::applyDefaultsToServerConfig for UI.
static XrayXhttpConfig fromJson(const QJsonObject &json);
};
@@ -99,15 +100,13 @@ struct XrayServerConfig {
QString site;
bool isThirdPartyConfig = false;
// New: Security
QString security = protocols::xray::defaultSecurity;
QString flow = protocols::xray::defaultFlow;
QString fingerprint = protocols::xray::defaultFingerprint;
QString sni = protocols::xray::defaultSni;
QString alpn = protocols::xray::defaultAlpn;
QString security;
QString flow;
QString fingerprint;
QString sni;
QString alpn;
// New: Transport
QString transport = protocols::xray::defaultTransport;
QString transport;
XrayXhttpConfig xhttp;
XrayMkcpConfig mkcp;
@@ -139,6 +138,10 @@ struct XrayProtocolConfig {
bool hasClientConfig() const;
void setClientConfig(const XrayClientConfig &config);
void clearClientConfig();
bool needsClientHydration = false;
bool hydrateServerConfigFromClientNative();
};
} // namespace amnezia
@@ -9,6 +9,7 @@
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/utils/networkUtilities.h"
namespace amnezia
{
@@ -28,6 +29,20 @@ ContainerConfig NativeServerConfig::containerConfig(DockerContainer container) c
return containers.value(container);
}
QPair<QString, QString> NativeServerConfig::getDnsPair(const QString &primaryDns, const QString &secondaryDns) const
{
QString d1 = dns1;
QString d2 = dns2;
if (d1.isEmpty() || !NetworkUtilities::checkIPv4Format(d1)) {
d1 = primaryDns;
}
if (d2.isEmpty() || !NetworkUtilities::checkIPv4Format(d2)) {
d2 = secondaryDns;
}
return { d1, d2 };
}
QJsonObject NativeServerConfig::toJson() const
{
QJsonObject obj;
@@ -3,6 +3,7 @@
#include <QJsonObject>
#include <QMap>
#include <QPair>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
@@ -25,6 +26,9 @@ struct NativeServerConfig {
bool hasContainers() const;
ContainerConfig containerConfig(DockerContainer container) const;
QPair<QString, QString> getDnsPair(const QString &primaryDns, const QString &secondaryDns) const;
QJsonObject toJson() const;
static NativeServerConfig fromJson(const QJsonObject& json);
};
@@ -8,6 +8,7 @@
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
#include "core/utils/networkUtilities.h"
namespace amnezia
{
@@ -42,6 +43,21 @@ ContainerConfig SelfHostedUserServerConfig::containerConfig(DockerContainer cont
return containers.value(container);
}
QPair<QString, QString> SelfHostedUserServerConfig::getDnsPair(const QString &primaryDns,
const QString &secondaryDns) const
{
QString d1 = dns1;
QString d2 = dns2;
if (d1.isEmpty() || !NetworkUtilities::checkIPv4Format(d1)) {
d1 = primaryDns;
}
if (d2.isEmpty() || !NetworkUtilities::checkIPv4Format(d2)) {
d2 = secondaryDns;
}
return { d1, d2 };
}
QJsonObject SelfHostedUserServerConfig::toJson() const
{
QJsonObject obj;
@@ -3,6 +3,7 @@
#include <QJsonObject>
#include <QMap>
#include <QPair>
#include <optional>
#include "core/utils/containerEnum.h"
@@ -30,6 +31,9 @@ struct SelfHostedUserServerConfig {
std::optional<ServerCredentials> credentials() const;
bool hasContainers() const;
ContainerConfig containerConfig(DockerContainer container) const;
QPair<QString, QString> getDnsPair(const QString &primaryDns, const QString &secondaryDns) const;
QJsonObject toJson() const;
static SelfHostedUserServerConfig fromJson(const QJsonObject &json);
};
+3
View File
@@ -35,6 +35,9 @@ namespace amnezia
ServerCgroupMountpoint = 212,
DockerPullRateLimit = 213,
ServerLinuxKernelTooOld = 214,
XrayServerConfigInvalid = 215,
XrayServerNoVlessClients = 216,
XrayRealityKeysReadFailed = 217,
// Ssh connection errors
SshRequestDeniedError = 300,
+9
View File
@@ -30,6 +30,15 @@ QString errorString(ErrorCode code) {
case(ErrorCode::ServerCgroupMountpoint): errorMessage = QObject::tr("Server error: cgroup mountpoint does not exist"); break;
case(ErrorCode::DockerPullRateLimit): errorMessage = QObject::tr("Docker error: The pull rate limit has been reached"); break;
case(ErrorCode::ServerLinuxKernelTooOld): errorMessage = QObject::tr("Server error: Linux kernel is too old"); break;
case(ErrorCode::XrayServerConfigInvalid):
errorMessage = QObject::tr("Server error: invalid or unreadable XRay server configuration");
break;
case(ErrorCode::XrayServerNoVlessClients):
errorMessage = QObject::tr("Server error: XRay server has no VLESS clients");
break;
case(ErrorCode::XrayRealityKeysReadFailed):
errorMessage = QObject::tr("Server error: failed to read XRay Reality keys from the server");
break;
// Libssh errors
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
@@ -295,6 +295,8 @@ amnezia::ScriptVars amnezia::genMtProxyVars(const ContainerConfig &containerConf
vars.append({{"$MTPROXY_PORT", c.port.isEmpty() ? QString(protocols::mtProxy::defaultPort) : c.port}});
vars.append({{"$MTPROXY_SECRET", c.secret}});
vars.append({{"$MTPROXY_REGENERATE_SECRET",
c.secret.isEmpty() ? QStringLiteral("1") : QStringLiteral("0")}});
vars.append({{"$MTPROXY_TAG", c.tag}});
vars.append({{"$MTPROXY_TRANSPORT_MODE",
c.transportMode.isEmpty() ? QString(protocols::mtProxy::transportModeStandard)
@@ -350,6 +352,8 @@ amnezia::ScriptVars amnezia::genTelemtVars(const ContainerConfig &containerConfi
vars.append({ { "$TELEMT_TOML_TLS", faketls ? QLatin1String("true") : QLatin1String("false") } });
vars.append({ { "$TELEMT_PORT", c.port.isEmpty() ? QString(protocols::telemt::defaultPort) : c.port } });
vars.append({ { "$TELEMT_SECRET", c.secret } });
vars.append({ { "$TELEMT_REGENERATE_SECRET",
c.secret.isEmpty() ? QStringLiteral("1") : QStringLiteral("0") } });
vars.append({ { "$TELEMT_TAG", c.tag } });
QString tlsDomain = c.tlsDomain;
if (tlsDomain.isEmpty()) {
@@ -4,8 +4,10 @@
curl -s https://core.telegram.org/getProxySecret -o /data/proxy-secret
curl -s https://core.telegram.org/getProxyConfig -o /data/proxy-multi.conf
# Determine secret: env var -> saved file -> generate new
if [ -n "$MTPROXY_SECRET" ]; then
# Determine secret: regenerate (fresh install) -> env var -> saved file -> generate new
if [ "$MTPROXY_REGENERATE_SECRET" = "1" ]; then
SECRET=$(openssl rand -hex 16)
elif [ -n "$MTPROXY_SECRET" ]; then
SECRET="$MTPROXY_SECRET"
elif [ -f /data/secret ]; then
SECRET=$(cat /data/secret)
+2 -1
View File
@@ -1,3 +1,4 @@
sudo docker stop $CONTAINER_NAME;\
sudo docker rm -fv $CONTAINER_NAME;\
sudo docker rmi $CONTAINER_NAME
sudo docker rmi $CONTAINER_NAME;\
test "$REMOVE_CONTAINER_DATA" = "1" && sudo docker volume rm -f ${CONTAINER_NAME}-data 2>/dev/null || true
@@ -4,8 +4,10 @@
echo "[*] Amnezia Telemt: configure script start"
mkdir -p /data/tlsfront
# Secret: substituted $TELEMT_SECRET -> saved file -> openssl (same rules as MTProxy configure)
if [ -n "$TELEMT_SECRET" ]; then
# Secret: regenerate (fresh install) -> env var -> saved file -> openssl
if [ "$TELEMT_REGENERATE_SECRET" = "1" ]; then
SECRET=$(openssl rand -hex 16)
elif [ -n "$TELEMT_SECRET" ]; then
SECRET="$TELEMT_SECRET"
elif [ -f /data/secret ]; then
SECRET=$(cat /data/secret)
+1 -1
View File
@@ -38,7 +38,7 @@ private slots:
void init() {
m_settings->clearSettings();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), QString());
}
}
+1 -1
View File
@@ -40,7 +40,7 @@ private slots:
m_settings->clearSettings();
m_coreController->m_serversRepository->invalidateCache();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), QString());
}
}
+1 -1
View File
@@ -41,7 +41,7 @@ private slots:
m_settings->clearSettings();
m_coreController->m_serversRepository->invalidateCache();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), QString());
}
}
+1 -1
View File
@@ -146,7 +146,7 @@ private slots:
void init() {
m_settings->clearSettings();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), QString());
}
}
+1 -1
View File
@@ -40,7 +40,7 @@ private slots:
m_settings->clearSettings();
m_coreController->m_serversRepository->invalidateCache();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), QString());
}
}
+1 -1
View File
@@ -40,7 +40,7 @@ private slots:
m_settings->clearSettings();
m_coreController->m_serversRepository->invalidateCache();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), QString());
}
}
+1 -1
View File
@@ -39,7 +39,7 @@ private slots:
void init() {
m_settings->clearSettings();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), QString());
}
}
+17
View File
@@ -133,6 +133,8 @@ private slots:
void testStartMinimizedSignals() {
QSignalSpy startMinimizedChangedSpy(m_coreController->m_settingsUiController, &SettingsUiController::startMinimizedChanged);
m_coreController->m_settingsUiController->toggleAutoStart(true);
bool initialStartMinimized = m_coreController->m_settingsController->isStartMinimizedEnabled();
m_coreController->m_settingsUiController->toggleStartMinimized(!initialStartMinimized);
@@ -140,6 +142,21 @@ private slots:
QVERIFY2(m_coreController->m_settingsController->isStartMinimizedEnabled() == !initialStartMinimized, "Start minimized state should be updated in SettingsController");
QVERIFY2(m_coreController->m_settingsUiController->isStartMinimizedEnabled() == !initialStartMinimized, "Start minimized state should be available in SettingsUiController");
QVERIFY2(m_coreController->m_appSettingsRepository->isStartMinimized() == !initialStartMinimized, "Start minimized state should be available in SecureAppSettingsRepository");
m_coreController->m_settingsUiController->toggleAutoStart(false);
}
void testAutoStartDisablesStartMinimizedUi() {
QSignalSpy startMinimizedChangedSpy(m_coreController->m_settingsUiController, &SettingsUiController::startMinimizedChanged);
m_coreController->m_settingsUiController->toggleAutoStart(true);
m_coreController->m_settingsUiController->toggleStartMinimized(true);
QVERIFY2(m_coreController->m_settingsUiController->isStartMinimizedEnabled(), "Start minimized should be enabled with autostart");
m_coreController->m_settingsUiController->toggleAutoStart(false);
QVERIFY2(startMinimizedChangedSpy.count() >= 1, "startMinimizedChanged signal should be emitted when autostart is disabled");
QVERIFY2(!m_coreController->m_settingsUiController->isStartMinimizedEnabled(), "Start minimized should be disabled when autostart is disabled");
QVERIFY2(!m_coreController->m_appSettingsRepository->isStartMinimized(), "Start minimized setting should be cleared when autostart is disabled");
}
void testAutoConnectSignals() {
+1 -1
View File
@@ -38,7 +38,7 @@ private slots:
m_settings->clearSettings();
m_coreController->m_serversRepository->invalidateCache();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), QString());
}
}
@@ -23,18 +23,6 @@ using namespace amnezia;
using namespace amnezia;
namespace {
int defaultServerRow(const QVector<ServerDescription> &descriptions, const QString &defaultServerId)
{
for (int i = 0; i < descriptions.size(); ++i) {
if (descriptions.at(i).serverId == defaultServerId) {
return i;
}
}
return -1;
}
} // namespace
class TestUiServersModelAndController : public QObject
{
Q_OBJECT
@@ -131,7 +119,7 @@ private slots:
void init() {
m_settings->clearSettings();
if (m_coreController->m_serversModel) {
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), -1);
m_coreController->m_serversModel->updateModel(QVector<ServerDescription>(), QString());
}
}
@@ -274,7 +262,7 @@ private slots:
QVector<ServerDescription> descriptionsNoDns = m_coreController->m_serversController->buildServerDescriptions(
m_coreController->m_appSettingsRepository->useAmneziaDns());
const QString defIdNoDns = m_coreController->m_serversRepository->defaultServerId();
m_coreController->m_serversModel->updateModel(descriptionsNoDns, defaultServerRow(descriptionsNoDns, defIdNoDns));
m_coreController->m_serversModel->updateModel(descriptionsNoDns, defIdNoDns);
QString descNoDns = m_coreController->m_serversModel->data(
m_coreController->m_serversModel->index(0, 0), ServersModel::ServerDescriptionRole).toString();
@@ -293,7 +281,7 @@ private slots:
QVector<ServerDescription> descriptionsWithDns = m_coreController->m_serversController->buildServerDescriptions(
m_coreController->m_appSettingsRepository->useAmneziaDns());
const QString defIdWithDns = m_coreController->m_serversRepository->defaultServerId();
m_coreController->m_serversModel->updateModel(descriptionsWithDns, defaultServerRow(descriptionsWithDns, defIdWithDns));
m_coreController->m_serversModel->updateModel(descriptionsWithDns, defIdWithDns);
QString descWithDns = m_coreController->m_serversModel->data(
m_coreController->m_serversModel->index(0, 0), ServersModel::ServerDescriptionRole).toString();
@@ -65,6 +65,7 @@ SubscriptionUiController::SubscriptionUiController(ServersController* serversCon
ApiCountryModel* apiCountryModel,
ApiDevicesModel* apiDevicesModel,
SettingsController* settingsController,
ConnectionController* connectionController,
QObject *parent)
: QObject(parent),
m_serversController(serversController),
@@ -76,13 +77,29 @@ SubscriptionUiController::SubscriptionUiController(ServersController* serversCon
m_apiAccountInfoModel(apiAccountInfoModel),
m_apiCountryModel(apiCountryModel),
m_apiDevicesModel(apiDevicesModel),
m_settingsController(settingsController)
m_settingsController(settingsController),
m_connectionController(connectionController)
{
connect(m_apiServicesModel, &ApiServicesModel::serviceSelectionChanged, this, [this]() {
ApiServicesModel::ApiServicesData selectedServiceData = m_apiServicesModel->selectedServiceData();
m_apiSubscriptionPlansModel->updateModel(selectedServiceData.subscriptionPlansJson);
m_apiBenefitsModel->updateModel(selectedServiceData.benefits);
});
connect(this, &SubscriptionUiController::installServerFromApiFinished, this,
[this](const QString &, int preferredDefaultServerIndex) {
if (m_connectionController->isConnected()) {
return;
}
const int selectedServerIndex = preferredDefaultServerIndex >= 0
? preferredDefaultServerIndex
: (m_serversController->getServersCount() - 1);
const QString serverId = m_serversController->getServerId(selectedServerIndex);
if (!serverId.isEmpty()) {
m_serversController->setDefaultServer(serverId);
}
});
}
bool SubscriptionUiController::exportVpnKey(const QString &serverId, const QString &fileName)
@@ -5,6 +5,7 @@
#include "core/controllers/serversController.h"
#include "core/controllers/settingsController.h"
#include "core/controllers/connectionController.h"
#include "core/controllers/api/servicesCatalogController.h"
#include "core/controllers/api/subscriptionController.h"
#include "ui/models/api/apiSubscriptionPlansModel.h"
@@ -28,6 +29,7 @@ public:
ApiCountryModel* apiCountryModel,
ApiDevicesModel* apiDevicesModel,
SettingsController* settingsController,
ConnectionController* connectionController,
QObject *parent = nullptr);
Q_PROPERTY(QList<QString> qrCodes READ getQrCodes NOTIFY vpnKeyExportReady)
@@ -104,6 +106,7 @@ private:
ApiCountryModel* m_apiCountryModel;
ApiDevicesModel* m_apiDevicesModel;
SettingsController* m_settingsController;
ConnectionController* m_connectionController;
};
#endif // SUBSCRIPTIONUICONTROLLER_H
@@ -44,7 +44,6 @@ signals:
void connectionStateChanged();
void connectionErrorOccurred(ErrorCode errorCode);
void reconnectWithUpdatedContainer(const QString &message);
void connectButtonClicked();
void preparingConfig();
@@ -12,6 +12,7 @@
#include "core/utils/api/apiUtils.h"
#include "core/controllers/selfhosted/installController.h"
#include "core/controllers/connectionController.h"
#include "core/utils/networkUtilities.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
@@ -51,6 +52,7 @@ InstallUiController::InstallUiController(InstallController *installController,
Socks5ProxyConfigModel *socks5ConfigModel,
MtProxyConfigModel* mtConfigModel,
TelemtConfigModel *telemtConfigModel,
ConnectionController *connectionController,
QObject *parent)
: QObject(parent),
m_installController(installController),
@@ -69,7 +71,8 @@ InstallUiController::InstallUiController(InstallController *installController,
m_sftpConfigModel(sftpConfigModel),
m_socks5ConfigModel(socks5ConfigModel),
m_mtProxyConfigModel(mtConfigModel),
m_telemtConfigModel(telemtConfigModel)
m_telemtConfigModel(telemtConfigModel),
m_connectionController(connectionController)
{
connect(m_installController, &InstallController::configValidated, this, &InstallUiController::configValidated);
connect(m_installController, &InstallController::validationErrorOccurred, this, [this](ErrorCode errorCode) {
@@ -133,6 +136,10 @@ void InstallUiController::install(DockerContainer container, int port, Transport
finishMessage += tr("\nAdded containers that were already installed on the server");
}
if (!m_connectionController->isConnected()) {
m_serversController->setDefaultServer(newServerId);
}
emit installServerFinished(finishMessage);
} else {
const auto adminBefore = m_serversController->selfHostedAdminConfig(serverId);
@@ -172,7 +179,12 @@ void InstallUiController::install(DockerContainer container, int port, Transport
"All installed containers have been added to the application");
}
emit installContainerFinished(finishMessage, ContainerUtils::containerService(container) == ServiceType::Other);
const bool isServiceInstall = ContainerUtils::containerService(container) == ServiceType::Other;
if (!m_connectionController->isConnected() && !isServiceInstall) {
m_serversController->setDefaultContainer(serverId, container);
}
emit installContainerFinished(finishMessage, isServiceInstall);
}
}
@@ -263,11 +275,15 @@ void InstallUiController::updateContainer(const QString &serverId, int container
}
ContainerConfig oldContainerConfig = m_serversController->getContainerConfig(serverId, container);
if (container == DockerContainer::MtProxy || container == DockerContainer::Telemt) {
const bool asyncUpdate = container == DockerContainer::MtProxy || container == DockerContainer::Telemt
|| container == DockerContainer::Xray || container == DockerContainer::SSXray;
if (asyncUpdate) {
emit serverIsBusy(true);
auto *watcher = new QFutureWatcher<ErrorCode>(this);
const Proto protocolTypeCopy = protocolType;
QObject::connect(watcher, &QFutureWatcher<ErrorCode>::finished, this,
[this, watcher, serverId, container, closePage]() {
[this, watcher, serverId, container, closePage, protocolTypeCopy]() {
const ErrorCode errorCode = watcher->result();
watcher->deleteLater();
emit serverIsBusy(false);
@@ -276,15 +292,8 @@ void InstallUiController::updateContainer(const QString &serverId, int container
const ContainerConfig updatedConfig =
m_serversController->getContainerConfig(serverId, container);
m_protocolModel->updateModel(updatedConfig);
const auto defaultContainer =
m_serversController->getDefaultContainer(serverId);
if ((serverId == m_serversController->getDefaultServerId())
&& (container == defaultContainer)) {
emit currentContainerUpdated();
} else {
emit updateContainerFinished(tr("Settings updated successfully"), closePage);
}
updateProtocolConfigModel(serverId, static_cast<int>(container), static_cast<int>(protocolTypeCopy));
emit updateContainerFinished(tr("Settings updated successfully"), closePage);
} else {
emit installationErrorOccurred(errorCode);
}
@@ -295,7 +304,7 @@ void InstallUiController::updateContainer(const QString &serverId, int container
InstallController *installController = m_installController;
QFuture<ErrorCode> future =
QtConcurrent::run([installController, serverId, container, oldConfigCopy,
newConfigCopy]() mutable -> ErrorCode {
newConfigCopy]() mutable -> ErrorCode {
return installController->updateContainer(serverId, container, oldConfigCopy, newConfigCopy);
});
watcher->setFuture(future);
@@ -307,13 +316,8 @@ void InstallUiController::updateContainer(const QString &serverId, int container
if (errorCode == ErrorCode::NoError) {
ContainerConfig updatedConfig = m_serversController->getContainerConfig(serverId, container);
m_protocolModel->updateModel(updatedConfig);
const auto defaultContainer = m_serversController->getDefaultContainer(serverId);
if ((serverId == m_serversController->getDefaultServerId()) && (container == defaultContainer)) {
emit currentContainerUpdated();
} else {
emit updateContainerFinished(tr("Settings updated successfully"), closePage);
}
updateProtocolConfigModel(serverId, static_cast<int>(container), static_cast<int>(protocolType));
emit updateContainerFinished(tr("Settings updated successfully"), closePage);
return;
}
@@ -427,6 +431,34 @@ void InstallUiController::removeContainer(const QString &serverId, int container
DockerContainer container = static_cast<DockerContainer>(containerIndex);
QString containerName = ContainerUtils::containerHumanNames().value(container);
const bool asyncRemove = container == DockerContainer::Xray || container == DockerContainer::SSXray;
if (asyncRemove) {
emit serverIsBusy(true);
auto *watcher = new QFutureWatcher<ErrorCode>(this);
QObject::connect(watcher, &QFutureWatcher<ErrorCode>::finished, this,
[this, watcher, serverId, container, containerName, serverName]() {
const ErrorCode errorCode = watcher->result();
watcher->deleteLater();
emit serverIsBusy(false);
if (errorCode == ErrorCode::NoError) {
emit removeContainerFinished(
tr("%1 has been removed from the server '%2'").arg(containerName, serverName));
} else {
emit installationErrorOccurred(errorCode);
}
});
InstallController *installController = m_installController;
QFuture<ErrorCode> future = QtConcurrent::run(
[installController, serverId, container]() -> ErrorCode {
return installController->removeContainer(serverId, container);
});
watcher->setFuture(future);
return;
}
ErrorCode errorCode = m_installController->removeContainer(serverId, container);
if (errorCode == ErrorCode::NoError) {
@@ -517,6 +549,12 @@ void InstallUiController::setEncryptedPassphrase(QString passphrase)
void InstallUiController::addEmptyServer()
{
m_installController->addEmptyServer(m_processedServerCredentials);
if (!m_connectionController->isConnected()) {
const QString newServerId = m_serversController->getServerId(m_serversController->getServersCount() - 1);
if (!newServerId.isEmpty()) {
m_serversController->setDefaultServer(newServerId);
}
}
emit installServerFinished(tr("Server added successfully"));
}
@@ -9,6 +9,7 @@
#include "core/utils/protocolEnum.h"
#include "core/controllers/serversController.h"
#include "core/controllers/settingsController.h"
#include "core/controllers/connectionController.h"
#include "core/controllers/selfhosted/usersController.h"
#include "core/controllers/selfhosted/installController.h"
#include "core/utils/errorCodes.h"
@@ -52,6 +53,7 @@ public:
Socks5ProxyConfigModel* socks5ConfigModel,
MtProxyConfigModel* mtConfigModel,
TelemtConfigModel* telemtConfigModel,
ConnectionController* connectionController,
QObject *parent = nullptr);
~InstallUiController();
@@ -127,8 +129,6 @@ signals:
void serverIsBusy(const bool isBusy);
void cancelInstallation();
void currentContainerUpdated();
void cachedProfileCleared(const QString &message);
void apiConfigRemoved(const QString &message);
@@ -155,6 +155,7 @@ private:
Socks5ProxyConfigModel* m_socks5ConfigModel;
MtProxyConfigModel* m_mtProxyConfigModel;
TelemtConfigModel* m_telemtConfigModel;
ConnectionController* m_connectionController;
ServerCredentials m_processedServerCredentials;
+111 -113
View File
@@ -31,6 +31,12 @@ bool descriptionsHaveGatewayServers(const QVector<ServerDescription> &list)
}
return false;
}
const ServerDescription &emptyServerDescription()
{
static const ServerDescription s_emptyDescription;
return s_emptyDescription;
}
} // namespace
ServersUiController::ServersUiController(ServersController* serversController,
SettingsController* settingsController,
@@ -100,8 +106,6 @@ void ServersUiController::setDefaultServer(const QString &serverId)
return;
}
m_serversController->setDefaultServer(serverId);
updateModel();
emit defaultServerIdChanged(serverId);
}
void ServersUiController::setDefaultContainer(const QString &serverId, int containerIndex)
@@ -120,12 +124,12 @@ void ServersUiController::toggleAmneziaDns(bool enabled)
updateModel();
}
void ServersUiController::onDefaultServerChanged(const QString &/*defaultServerId*/)
void ServersUiController::onDefaultServerChanged(const QString &defaultServerId)
{
updateModel();
setProcessedServerId(m_serversController->getDefaultServerId());
m_serversModel->setDefaultServerId(defaultServerId);
updateDefaultServerContainersModel();
emit defaultServerIdChanged(m_serversController->getDefaultServerId());
emit defaultServerIdChanged(defaultServerId);
}
void ServersUiController::updateModel()
@@ -136,27 +140,21 @@ void ServersUiController::updateModel()
const QString defaultServerId = m_serversController->getDefaultServerId();
const bool hadServersFromGatewayBefore = descriptionsHaveGatewayServers(m_orderedServerDescriptions);
const bool hasServersFromGatewayNow = descriptionsHaveGatewayServers(descriptions);
const int listCount = descriptions.size();
const int defaultRowInDescriptions = rowForServerId(descriptions, defaultServerId);
m_orderedServerDescriptions = descriptions;
if (listCount == 0) {
setProcessedServerId(QString());
} else if (m_processedServerIndex >= listCount) {
setProcessedServerId(defaultServerId);
if (m_orderedServerDescriptions.isEmpty()) {
if (!m_processedServerId.isEmpty()) {
setProcessedServerId(QString());
}
} else if (!m_processedServerId.isEmpty()) {
const int row = rowForServerId(m_orderedServerDescriptions, m_processedServerId);
if (row < 0) {
setProcessedServerId(defaultServerId);
} else {
setProcessedServerId(m_processedServerId);
setProcessedServerId(QString());
}
} else if (defaultRowInDescriptions >= 0) {
setProcessedServerId(defaultServerId);
}
m_serversModel->updateModel(m_orderedServerDescriptions, defaultRowInDescriptions);
m_serversModel->updateModel(m_orderedServerDescriptions, defaultServerId);
updateContainersModel();
updateDefaultServerContainersModel();
@@ -166,7 +164,6 @@ void ServersUiController::updateModel()
}
emit defaultServerIdChanged(defaultServerId);
emit defaultServerIndexChanged(defaultServerIndex());
}
QString ServersUiController::getDefaultServerId() const
@@ -176,60 +173,35 @@ QString ServersUiController::getDefaultServerId() const
QString ServersUiController::getDefaultServerName() const
{
const QString defaultServerId = m_serversController->getDefaultServerId();
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == defaultServerId) {
return description.serverName;
}
}
return QString();
return serverName(getDefaultServerId());
}
QString ServersUiController::getDefaultServerDefaultContainerName() const
{
const QString defaultServerId = m_serversController->getDefaultServerId();
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == defaultServerId) {
return ContainerUtils::containerHumanNames().value(description.defaultContainer);
}
const auto &description = serverDescriptionById(getDefaultServerId());
if (description.serverId.isEmpty()) {
return QString();
}
return QString();
return ContainerUtils::containerHumanNames().value(description.defaultContainer);
}
QString ServersUiController::getDefaultServerDescriptionCollapsed() const
{
const QString defaultServerId = m_serversController->getDefaultServerId();
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == defaultServerId) {
return description.collapsedServerDescription;
}
}
return QString();
return serverDescriptionById(getDefaultServerId()).collapsedServerDescription;
}
QString ServersUiController::getDefaultServerImagePathCollapsed() const
{
const QString defaultServerId = m_serversController->getDefaultServerId();
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == defaultServerId) {
if (!description.isApiV2 || description.apiServerCountryCode.isEmpty()) {
return "";
}
return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(description.apiServerCountryCode.toUpper());
}
const auto &description = serverDescriptionById(getDefaultServerId());
if (!description.isApiV2 || description.apiServerCountryCode.isEmpty()) {
return "";
}
return "";
return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(description.apiServerCountryCode.toUpper());
}
QString ServersUiController::getDefaultServerDescriptionExpanded() const
{
const QString defaultServerId = m_serversController->getDefaultServerId();
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == defaultServerId) {
return description.expandedServerDescription;
}
}
return QString();
return serverDescriptionById(getDefaultServerId()).expandedServerDescription;
}
bool ServersUiController::isDefaultServerDefaultContainerHasSplitTunneling() const
@@ -281,15 +253,75 @@ bool ServersUiController::isDefaultServerDefaultContainerHasSplitTunneling() con
bool ServersUiController::isDefaultServerFromApi() const
{
const QString defaultServerId = m_serversController->getDefaultServerId();
return isServerFromApi(getDefaultServerId());
}
bool ServersUiController::hasServerWithWriteAccess() const
{
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == defaultServerId) {
return description.isApiV2;
if (description.hasWriteAccess) {
return true;
}
}
return false;
}
QString ServersUiController::serverName(const QString &serverId) const
{
return serverDescriptionById(serverId).serverName;
}
QString ServersUiController::serverHostName(const QString &serverId) const
{
return serverDescriptionById(serverId).hostName;
}
int ServersUiController::serverDefaultContainer(const QString &serverId) const
{
const auto &description = serverDescriptionById(serverId);
return description.serverId.isEmpty() ? -1 : static_cast<int>(description.defaultContainer);
}
bool ServersUiController::isServerFromApi(const QString &serverId) const
{
return serverDescriptionById(serverId).isServerFromGatewayApi;
}
bool ServersUiController::isServerCountrySelectionAvailable(const QString &serverId) const
{
return serverDescriptionById(serverId).isCountrySelectionAvailable;
}
bool ServersUiController::isServerHasWriteAccess(const QString &serverId) const
{
return serverDescriptionById(serverId).hasWriteAccess;
}
bool ServersUiController::serverHasInstalledContainers(const QString &serverId) const
{
return serverDescriptionById(serverId).hasInstalledVpnContainers;
}
QString ServersUiController::serverAdEndpoint(const QString &serverId) const
{
return serverDescriptionById(serverId).adEndpoint;
}
bool ServersUiController::isServerRenewalAvailable(const QString &serverId) const
{
return serverDescriptionById(serverId).isRenewalAvailable;
}
bool ServersUiController::isServerSubscriptionExpired(const QString &serverId) const
{
return serverDescriptionById(serverId).isSubscriptionExpired;
}
bool ServersUiController::isServerSubscriptionExpiringSoon(const QString &serverId) const
{
return serverDescriptionById(serverId).isSubscriptionExpiringSoon;
}
int ServersUiController::getProcessedContainerIndex() const
{
return m_processedContainerIndex;
@@ -311,27 +343,17 @@ QString ServersUiController::getProcessedServerId() const
void ServersUiController::setProcessedServerId(const QString &serverId)
{
const int index = serverId.isEmpty() ? -1 : serverIndexForId(serverId);
if (!serverId.isEmpty() && index < 0) {
return;
}
const int newIndex = serverId.isEmpty() ? -1 : serverIndexForId(serverId);
const QString normalizedServerId = newIndex >= 0 ? serverId : QString();
if (m_processedServerIndex != index || m_processedServerId != serverId) {
m_processedServerIndex = index;
m_processedServerId = serverId;
m_serversModel->setProcessedServerIndex(index);
if (m_processedServerId != normalizedServerId) {
m_processedServerId = normalizedServerId;
if (index >= 0) {
if (newIndex >= 0) {
updateContainersModel();
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == serverId) {
setProcessedContainerIndex(static_cast<int>(description.defaultContainer));
break;
}
}
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId != serverId) {
if (description.serverId != normalizedServerId) {
continue;
}
if (description.isApiV2) {
@@ -345,45 +367,12 @@ void ServersUiController::setProcessedServerId(const QString &serverId)
}
emit processedServerIdChanged(m_processedServerId);
emit processedServerIndexChanged(m_processedServerIndex);
}
}
int ServersUiController::getProcessedServerIndex() const
{
return m_processedServerIndex;
}
void ServersUiController::setProcessedServerIndex(int index)
{
if (index < 0) {
setProcessedServerId(QString());
return;
}
const QString id = getServerId(index);
if (!id.isEmpty()) {
setProcessedServerId(id);
}
}
int ServersUiController::defaultServerIndex() const
{
return rowForServerId(m_orderedServerDescriptions, getDefaultServerId());
}
bool ServersUiController::processedServerIsPremium() const
{
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == m_processedServerId) {
return description.isPremium;
}
}
return false;
}
const ServerCredentials ServersUiController::getProcessedServerCredentials() const
{
return m_serversController->getServerCredentials(m_processedServerId);
return processedServerDescription().isPremium;
}
bool ServersUiController::isDefaultServerCurrentlyProcessed() const
@@ -393,18 +382,22 @@ bool ServersUiController::isDefaultServerCurrentlyProcessed() const
bool ServersUiController::isProcessedServerHasWriteAccess() const
{
ServerCredentials credentials = m_serversController->getServerCredentials(m_processedServerId);
return (!credentials.userName.isEmpty() && !credentials.secretData.isEmpty());
return isServerHasWriteAccess(m_processedServerId);
}
QString ServersUiController::getDefaultServerDescription(const QString &serverId) const
const ServerDescription &ServersUiController::processedServerDescription() const
{
return serverDescriptionById(m_processedServerId);
}
const ServerDescription &ServersUiController::serverDescriptionById(const QString &serverId) const
{
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId == serverId) {
return description.baseDescription;
return description;
}
}
return QString();
return emptyServerDescription();
}
bool ServersUiController::hasServersFromGatewayApi() const
@@ -467,6 +460,11 @@ int ServersUiController::getServerIndexById(const QString &serverId) const
return rowForServerId(m_orderedServerDescriptions, serverId);
}
int ServersUiController::getServersCount() const
{
return m_orderedServerDescriptions.size();
}
void ServersUiController::updateContainersModel()
{
if (m_processedServerId.isEmpty()) {
+17 -13
View File
@@ -19,7 +19,6 @@ class ServersUiController : public QObject
Q_OBJECT
Q_PROPERTY(QString defaultServerId READ getDefaultServerId NOTIFY defaultServerIdChanged)
Q_PROPERTY(int defaultServerIndex READ defaultServerIndex NOTIFY defaultServerIndexChanged)
Q_PROPERTY(QString defaultServerName READ getDefaultServerName NOTIFY defaultServerIdChanged)
Q_PROPERTY(QString defaultServerDefaultContainerName READ getDefaultServerDefaultContainerName NOTIFY defaultServerIdChanged)
@@ -30,9 +29,8 @@ class ServersUiController : public QObject
Q_PROPERTY(bool isDefaultServerFromApi READ isDefaultServerFromApi NOTIFY defaultServerIdChanged)
Q_PROPERTY(QString processedServerId READ getProcessedServerId WRITE setProcessedServerId NOTIFY processedServerIdChanged)
Q_PROPERTY(int processedServerIndex 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 processedServerIsPremium READ processedServerIsPremium NOTIFY processedServerIdChanged)
Q_PROPERTY(bool hasServersFromGatewayApi READ hasServersFromGatewayApi NOTIFY hasServersFromGatewayApiChanged)
@@ -72,20 +70,27 @@ public slots:
QString getDefaultServerDescriptionExpanded() const;
bool isDefaultServerDefaultContainerHasSplitTunneling() const;
bool isDefaultServerFromApi() const;
bool hasServerWithWriteAccess() const;
QString serverName(const QString &serverId) const;
QString serverHostName(const QString &serverId) const;
int serverDefaultContainer(const QString &serverId) const;
bool isServerFromApi(const QString &serverId) const;
bool isServerCountrySelectionAvailable(const QString &serverId) const;
bool isServerHasWriteAccess(const QString &serverId) const;
bool serverHasInstalledContainers(const QString &serverId) const;
QString serverAdEndpoint(const QString &serverId) const;
bool isServerRenewalAvailable(const QString &serverId) const;
bool isServerSubscriptionExpired(const QString &serverId) const;
bool isServerSubscriptionExpiringSoon(const QString &serverId) const;
QString getProcessedServerId() const;
void setProcessedServerId(const QString &serverId);
int getProcessedServerIndex() const;
void setProcessedServerIndex(int index);
int defaultServerIndex() const;
int getProcessedContainerIndex() const;
void setProcessedContainerIndex(int index);
bool processedServerIsPremium() const;
const ServerCredentials getProcessedServerCredentials() const;
bool isDefaultServerCurrentlyProcessed() const;
bool isProcessedServerHasWriteAccess() const;
@@ -97,15 +102,14 @@ public slots:
QString getServerId(int index) const;
int getServerIndexById(const QString &serverId) const;
int getServersCount() const;
QStringList getAllInstalledServicesName(int serverIndex) const;
signals:
void errorOccurred(const QString &errorMessage);
void finished(const QString &message);
void defaultServerIdChanged(const QString &serverId);
void defaultServerIndexChanged(int index);
void processedServerIdChanged(const QString &serverId);
void processedServerIndexChanged(int index);
void processedContainerIndexChanged(int index);
void hasServersFromGatewayApiChanged();
void updateApiCountryModel();
@@ -115,7 +119,8 @@ public:
void updateModel();
private:
QString getDefaultServerDescription(const QString &serverId) const;
const ServerDescription &serverDescriptionById(const QString &serverId) const;
const ServerDescription &processedServerDescription() const;
int serverIndexForId(const QString &serverId) const;
bool listHasServersFromGatewayApi() const;
@@ -130,7 +135,6 @@ private:
QVector<amnezia::ServerDescription> m_orderedServerDescriptions;
int m_processedServerIndex = -1;
QString m_processedServerId;
int m_processedContainerIndex = -1;
};
@@ -164,6 +164,7 @@ void SettingsUiController::restoreAppConfigFromData(const QByteArray &data)
emit amneziaDnsToggled(amneziaDnsEnabled);
emit restoreBackupFinished();
emit startMinimizedChanged();
} else {
emit errorOccurred(errorCode);
}
@@ -177,6 +178,7 @@ QString SettingsUiController::getAppVersion()
void SettingsUiController::clearSettings()
{
m_settingsController->clearSettings();
emit startMinimizedChanged();
emit resetLanguageToSystem();
emit changeSettingsFinished(tr("All settings have been reset to default values"));
@@ -204,6 +206,9 @@ bool SettingsUiController::isAutoStartEnabled()
void SettingsUiController::toggleAutoStart(bool enable)
{
m_settingsController->toggleAutoStart(enable);
if (!enable) {
emit startMinimizedChanged();
}
}
bool SettingsUiController::isStartMinimizedEnabled()
@@ -267,8 +267,13 @@ void XrayConfigModel::updateModel(amnezia::DockerContainer container, const amne
m_container = container;
m_protocolConfig = protocolConfig;
if (m_protocolConfig.needsClientHydration) {
m_protocolConfig.hydrateServerConfigFromClientNative();
}
applyDefaultsToServerConfig(m_protocolConfig.serverConfig);
if (!m_protocolConfig.serverConfig.isThirdPartyConfig) {
applyDefaultsToServerConfig(m_protocolConfig.serverConfig);
}
m_originalProtocolConfig = m_protocolConfig;
+37 -154
View File
@@ -20,20 +20,25 @@
using namespace amnezia;
namespace {
int rowForServerId(const QVector<ServerDescription> &descriptions, const QString &serverId)
{
if (serverId.isEmpty()) {
return -1;
}
for (int i = 0; i < descriptions.size(); ++i) {
if (descriptions.at(i).serverId == serverId) {
return i;
}
}
return -1;
}
} // namespace
ServersModel::ServersModel(QObject *parent) : QAbstractListModel(parent)
{
connect(this, &ServersModel::defaultServerIndexChanged, this, &ServersModel::defaultServerNameChanged);
connect(this, &ServersModel::defaultServerIndexChanged, this, [this](const int serverIndex) {
if (serverIndex < 0 || serverIndex >= m_descriptions.size()) {
return;
}
auto defaultContainer = m_descriptions.at(serverIndex).defaultContainer;
emit ServersModel::defaultServerDefaultContainerChanged(defaultContainer);
emit ServersModel::defaultServerNameChanged();
});
connect(this, &ServersModel::processedServerIndexChanged, this, &ServersModel::processedServerChanged);
}
int ServersModel::rowCount(const QModelIndex &parent) const
@@ -56,52 +61,22 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const
return row.serverName;
case ServerDescriptionRole:
return configVersion ? row.baseDescription : (row.baseDescription + row.hostName);
case CollapsedServerDescriptionRole:
return row.collapsedServerDescription;
case ExpandedServerDescriptionRole:
return row.expandedServerDescription;
case HostNameRole:
return row.hostName;
case CredentialsRole:
return QVariant::fromValue(serverCredentials(index.row()));
case ServerIdRole:
return row.serverId;
case CredentialsLoginRole:
return serverCredentials(index.row()).userName;
case IsDefaultRole:
return index.row() == m_defaultServerIndex;
case IsCurrentlyProcessedRole:
return index.row() == m_processedServerIndex;
return row.serverId == m_defaultServerId;
case HasWriteAccessRole:
return row.hasWriteAccess;
case ContainsAmneziaDnsRole:
return row.primaryDnsIsAmnezia;
case DefaultContainerRole:
return QVariant::fromValue(row.defaultContainer);
case HasInstalledContainers:
return row.hasInstalledVpnContainers;
case IsServerFromTelegramApiRole:
return false;
case IsServerFromGatewayApiRole:
return row.isServerFromGatewayApi;
case ApiConfigRole:
return QVariant();
case IsCountrySelectionAvailableRole:
return row.isCountrySelectionAvailable;
case ApiAvailableCountriesRole:
return row.apiAvailableCountries;
case ApiServerCountryCodeRole:
return row.apiServerCountryCode;
case HasAmneziaDns:
return row.primaryDnsIsAmnezia;
case IsAdVisibleRole:
return row.isAdVisible;
case AdHeaderRole:
return row.adHeader;
case AdDescriptionRole:
return row.adDescription;
case AdEndpointRole:
return row.adEndpoint;
case IsRenewalAvailableRole:
return row.isRenewalAvailable;
case IsSubscriptionExpiredRole:
return row.isSubscriptionExpired;
case IsSubscriptionExpiringSoonRole:
@@ -117,68 +92,32 @@ QVariant ServersModel::data(const int index, int role) const
return data(modelIndex, role);
}
void ServersModel::updateModel(const QVector<ServerDescription> &descriptions, int defaultServerIndex)
void ServersModel::updateModel(const QVector<ServerDescription> &descriptions,
const QString &defaultServerId)
{
beginResetModel();
m_descriptions = descriptions;
m_defaultServerIndex = defaultServerIndex;
m_defaultServerId = defaultServerId;
endResetModel();
emit defaultServerIndexChanged(m_defaultServerIndex);
emit processedServerChanged();
}
const int ServersModel::getDefaultServerIndex()
void ServersModel::setDefaultServerId(const QString &serverId)
{
return m_defaultServerIndex;
}
const int ServersModel::getServersCount()
{
return m_descriptions.size();
}
bool ServersModel::hasServerWithWriteAccess()
{
for (size_t i = 0; i < getServersCount(); i++) {
if (qvariant_cast<bool>(data(static_cast<int>(i), HasWriteAccessRole))) {
return true;
}
if (m_defaultServerId == serverId) {
return;
}
return false;
}
void ServersModel::setProcessedServerIndex(const int index)
{
if (m_processedServerIndex != index) {
m_processedServerIndex = index;
emit processedServerIndexChanged(m_processedServerIndex);
const int oldIndex = rowForServerId(m_descriptions, m_defaultServerId);
const int newIndex = rowForServerId(m_descriptions, serverId);
m_defaultServerId = serverId;
const QVector<int> roles = { IsDefaultRole };
if (oldIndex >= 0 && oldIndex < m_descriptions.size()) {
emit dataChanged(this->index(oldIndex), this->index(oldIndex), roles);
}
if (newIndex >= 0 && newIndex < m_descriptions.size()) {
emit dataChanged(this->index(newIndex), this->index(newIndex), roles);
}
}
const ServerCredentials ServersModel::getProcessedServerCredentials()
{
return serverCredentials(m_processedServerIndex);
}
bool ServersModel::isDefaultServerCurrentlyProcessed()
{
return m_defaultServerIndex == m_processedServerIndex;
}
bool ServersModel::isDefaultServerFromApi()
{
return data(m_defaultServerIndex, IsServerFromTelegramApiRole).toBool()
|| data(m_defaultServerIndex, IsServerFromGatewayApiRole).toBool();
}
bool ServersModel::isProcessedServerHasWriteAccess()
{
return qvariant_cast<bool>(data(m_processedServerIndex, HasWriteAccessRole));
}
bool ServersModel::isDefaultServerHasWriteAccess()
{
return qvariant_cast<bool>(data(m_defaultServerIndex, HasWriteAccessRole));
}
QHash<int, QByteArray> ServersModel::roleNames() const
@@ -187,41 +126,22 @@ QHash<int, QByteArray> ServersModel::roleNames() const
roles[NameRole] = "name";
roles[ServerDescriptionRole] = "serverDescription";
roles[CollapsedServerDescriptionRole] = "collapsedServerDescription";
roles[ExpandedServerDescriptionRole] = "expandedServerDescription";
roles[HostNameRole] = "hostName";
roles[ServerIdRole] = "serverId";
roles[CredentialsRole] = "credentials";
roles[CredentialsLoginRole] = "credentialsLogin";
roles[IsDefaultRole] = "isDefault";
roles[IsCurrentlyProcessedRole] = "isCurrentlyProcessed";
roles[HasWriteAccessRole] = "hasWriteAccess";
roles[ContainsAmneziaDnsRole] = "containsAmneziaDns";
roles[DefaultContainerRole] = "defaultContainer";
roles[HasInstalledContainers] = "hasInstalledContainers";
roles[IsServerFromTelegramApiRole] = "isServerFromTelegramApi";
roles[IsServerFromGatewayApiRole] = "isServerFromGatewayApi";
roles[ApiConfigRole] = "apiConfig";
roles[IsCountrySelectionAvailableRole] = "isCountrySelectionAvailable";
roles[ApiAvailableCountriesRole] = "apiAvailableCountries";
roles[ApiServerCountryCodeRole] = "apiServerCountryCode";
roles[IsAdVisibleRole] = "isAdVisible";
roles[AdHeaderRole] = "adHeader";
roles[AdDescriptionRole] = "adDescription";
roles[AdEndpointRole] = "adEndpoint";
roles[IsRenewalAvailableRole] = "isRenewalAvailable";
roles[IsSubscriptionExpiredRole] = "isSubscriptionExpired";
roles[IsSubscriptionExpiringSoonRole] = "isSubscriptionExpiringSoon";
roles[HasAmneziaDns] = "hasAmneziaDns";
return roles;
}
@@ -233,40 +153,3 @@ ServerCredentials ServersModel::serverCredentials(int index) const
return m_descriptions.at(index).selfHostedSshCredentials;
}
bool ServersModel::isServerFromApi(const int serverIndex)
{
return data(serverIndex, IsServerFromTelegramApiRole).toBool()
|| data(serverIndex, IsServerFromGatewayApiRole).toBool();
}
QVariant ServersModel::getDefaultServerData(const QString roleString)
{
auto roles = roleNames();
for (auto it = roles.begin(); it != roles.end(); it++) {
if (QString(it.value()) == roleString) {
return data(m_defaultServerIndex, it.key());
}
}
return {};
}
QVariant ServersModel::getProcessedServerData(const QString &roleString)
{
auto roles = roleNames();
for (auto it = roles.begin(); it != roles.end(); it++) {
if (QString(it.value()) == roleString) {
return data(m_processedServerIndex, it.key());
}
}
return {};
}
bool ServersModel::serverHasInstalledContainers(const int serverIndex) const
{
if (serverIndex < 0 || serverIndex >= m_descriptions.size()) {
return false;
}
return m_descriptions.at(serverIndex).hasInstalledVpnContainers;
}
+5 -55
View File
@@ -14,39 +14,22 @@ public:
enum Roles {
NameRole = Qt::UserRole + 1,
ServerDescriptionRole,
CollapsedServerDescriptionRole,
ExpandedServerDescriptionRole,
HostNameRole,
ServerIdRole,
CredentialsRole,
CredentialsLoginRole,
IsDefaultRole,
IsCurrentlyProcessedRole,
HasWriteAccessRole,
ContainsAmneziaDnsRole,
DefaultContainerRole,
HasInstalledContainers,
IsServerFromTelegramApiRole,
IsServerFromGatewayApiRole,
ApiConfigRole,
IsCountrySelectionAvailableRole,
ApiAvailableCountriesRole,
ApiServerCountryCodeRole,
IsAdVisibleRole,
AdHeaderRole,
AdDescriptionRole,
AdEndpointRole,
IsRenewalAvailableRole,
IsSubscriptionExpiredRole,
IsSubscriptionExpiringSoonRole,
HasAmneziaDns
};
ServersModel(QObject *parent = nullptr);
@@ -56,52 +39,19 @@ public:
QVariant data(const int index, int role = Qt::DisplayRole) const;
public slots:
const int getDefaultServerIndex();
bool isDefaultServerCurrentlyProcessed();
bool isDefaultServerFromApi();
bool isProcessedServerHasWriteAccess();
bool isDefaultServerHasWriteAccess();
bool hasServerWithWriteAccess();
const int getServersCount();
void setProcessedServerIndex(const int index);
const ServerCredentials getProcessedServerCredentials();
QVariant getProcessedServerData(const QString &roleString);
QVariant getDefaultServerData(const QString roleString);
bool isServerFromApi(const int serverIndex);
void updateModel(const QVector<amnezia::ServerDescription> &descriptions, int defaultServerIndex);
void updateModel(const QVector<amnezia::ServerDescription> &descriptions,
const QString &defaultServerId);
void setDefaultServerId(const QString &serverId);
protected:
QHash<int, QByteArray> roleNames() const override;
signals:
void processedServerIndexChanged(const int index);
void processedServerChanged();
void defaultServerIndexChanged(const int index);
void defaultServerNameChanged();
void defaultServerDescriptionChanged();
void defaultServerDefaultContainerChanged(const int containerIndex);
void updateApiCountryModel();
void updateApiServicesModel();
private:
ServerCredentials serverCredentials(int index) const;
bool serverHasInstalledContainers(const int serverIndex) const;
QVector<amnezia::ServerDescription> m_descriptions;
int m_defaultServerIndex = -1;
int m_processedServerIndex = -1;
QString m_defaultServerId;
};
#endif // SERVERSMODEL_H
+3 -3
View File
@@ -51,11 +51,11 @@ Rectangle {
}
Keys.onEnterPressed: {
Qt.openUrlExternally(ServersModel.getDefaultServerData("adEndpoint"))
Qt.openUrlExternally(ServersUiController.serverAdEndpoint(ServersUiController.defaultServerId))
}
Keys.onReturnPressed: {
Qt.openUrlExternally(ServersModel.getDefaultServerData("adEndpoint"))
Qt.openUrlExternally(ServersUiController.serverAdEndpoint(ServersUiController.defaultServerId))
}
RowLayout {
@@ -144,7 +144,7 @@ Rectangle {
onClicked: function() {
root.forceActiveFocus()
Qt.openUrlExternally(ServersModel.getDefaultServerData("adEndpoint"))
Qt.openUrlExternally(ServersUiController.serverAdEndpoint(ServersUiController.defaultServerId))
}
}
}
@@ -182,7 +182,6 @@ Button {
}
onClicked: {
ServersUiController.setProcessedServerIndex(ServersUiController.defaultServerIndex)
ConnectionController.connectButtonClicked()
}
@@ -13,7 +13,6 @@ Item {
onButtonStartChanged: {
if (buttonStart) {
ServersUiController.setProcessedServerIndex(ServersUiController.defaultServerIndex)
ConnectionController.connectButtonClicked()
}
}
@@ -48,7 +48,7 @@ ListViewType {
showImage: !isInstalled
checkable: isInstalled && !ConnectionController.isConnected
checked: proxyDefaultServerContainersModel.mapToSource(index) === ServersModel.getDefaultServerData("defaultContainer")
checked: proxyDefaultServerContainersModel.mapToSource(index) === ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId)
onClicked: {
if (ConnectionController.isConnected && isInstalled) {
@@ -58,7 +58,7 @@ ListViewType {
if (checked) {
containersDropDown.closeTriggered()
ServersUiController.setDefaultContainer(ServersUiController.getServerId(ServersUiController.defaultServerIndex), proxyDefaultServerContainersModel.mapToSource(index))
ServersUiController.setDefaultContainer(ServersUiController.defaultServerId, proxyDefaultServerContainersModel.mapToSource(index))
} else {
ServersUiController.processedContainerIndex = proxyDefaultServerContainersModel.mapToSource(index)
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
@@ -46,7 +46,7 @@ DrawerType2 {
}
if (serverName.textField.text !== root.serverNameText) {
ServersUiController.editServerName(ServersUiController.getServerId(ServersUiController.processedServerIndex), serverName.textField.text);
ServersUiController.editServerName(ServersUiController.processedServerId, serverName.textField.text);
}
root.closeTriggered()
}
+7 -7
View File
@@ -17,7 +17,7 @@ import "../Config"
ListViewType {
id: root
property int selectedIndex: ServersUiController.defaultServerIndex
property int selectedIndex: ServersUiController.getServerIndexById(ServersUiController.defaultServerId)
anchors.top: serversMenuHeader.bottom
anchors.right: parent.right
@@ -29,8 +29,8 @@ ListViewType {
Connections {
target: ServersUiController
function onDefaultServerIndexChanged() {
root.selectedIndex = ServersUiController.defaultServerIndex
function onDefaultServerIdChanged() {
root.selectedIndex = ServersUiController.getServerIndexById(ServersUiController.defaultServerId)
}
}
@@ -106,14 +106,14 @@ ListViewType {
z: 1
onClicked: function() {
ServersUiController.processedServerIndex = index
ServersUiController.setProcessedServerId(serverId)
if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) {
if (ServersModel.getProcessedServerData("isCountrySelectionAvailable")) {
if (ServersUiController.isServerFromApi(ServersUiController.processedServerId)) {
if (ServersUiController.isServerCountrySelectionAvailable(ServersUiController.processedServerId)) {
PageController.goToPage(PageEnum.PageSettingsApiAvailableCountries)
} else {
PageController.showBusyIndicator(true)
let result = SubscriptionUiController.getAccountInfo(ServersUiController.getServerId(ServersUiController.processedServerIndex), false)
let result = SubscriptionUiController.getAccountInfo(ServersUiController.processedServerId, false)
PageController.showBusyIndicator(false)
if (!result) {
return
@@ -34,25 +34,25 @@ ListViewType {
if (isVpnContainer) {
// var isThirdPartyConfig = root.model.data(index, ContainersModel.IsThirdPartyConfigRole)
if (isThirdPartyConfig) {
InstallController.updateProtocols(ServersUiController.getServerId(ServersUiController.processedServerIndex), containerIndex)
InstallController.updateProtocols(ServersUiController.processedServerId, containerIndex)
PageController.goToPage(PageEnum.PageProtocolRaw)
return
}
}
if (isIpsec) {
InstallController.updateProtocols(ServersUiController.getServerId(ServersUiController.processedServerIndex), containerIndex)
InstallController.updateProtocols(ServersUiController.processedServerId, containerIndex)
PageController.goToPage(PageEnum.PageProtocolRaw)
} else if (isDns) {
PageController.goToPage(PageEnum.PageServiceDnsSettings)
} else if (isMtProxy) {
MtProxyConfigModel.updateModel(config)
PageController.goToPage(PageEnum.PageServiceMtProxySettings)
PageController.goToPage(PageEnum.PageServiceMtProxySettings, false)
} else if (isTelemt) {
TelemtConfigModel.updateModel(config)
PageController.goToPage(PageEnum.PageServiceTelemtSettings)
PageController.goToPage(PageEnum.PageServiceTelemtSettings, false)
} else {
InstallController.updateProtocols(ServersUiController.getServerId(ServersUiController.processedServerIndex), containerIndex)
InstallController.updateProtocols(ServersUiController.processedServerId, containerIndex)
PageController.goToPage(PageEnum.PageSettingsServerProtocol)
}
@@ -15,7 +15,7 @@ DrawerType2 {
property bool isRenewalAvailable: false
onOpened: {
isRenewalAvailable = ServersModel.getDefaultServerData("isRenewalAvailable") && !ApiAccountInfoModel.data("isInAppPurchase")
isRenewalAvailable = ServersUiController.isServerRenewalAvailable(ServersUiController.defaultServerId) && !ApiAccountInfoModel.data("isInAppPurchase")
}
expandedStateContent: ColumnLayout {
@@ -43,7 +43,7 @@ DrawerType2 {
anchors.left: parent.left
anchors.right: parent.right
text: ServersModel.getDefaultServerData("name") + qsTr(" subscription has expired")
text: ServersUiController.serverName(ServersUiController.defaultServerId) + qsTr(" subscription has expired")
horizontalAlignment: Text.AlignLeft
}
}
@@ -76,7 +76,7 @@ DrawerType2 {
textColor: AmneziaStyle.color.midnightBlack
clickedFunc: function() {
SubscriptionUiController.getRenewalLink(ServersUiController.getServerId(ServersUiController.defaultServerIndex))
SubscriptionUiController.getRenewalLink(ServersUiController.defaultServerId)
}
}
@@ -96,7 +96,7 @@ DrawerType2 {
clickedFunc: function() {
PageController.showBusyIndicator(true)
let result = SubscriptionUiController.getAccountInfo(ServersUiController.getServerId(ServersUiController.defaultServerIndex), false)
let result = SubscriptionUiController.getAccountInfo(ServersUiController.defaultServerId, false)
PageController.showBusyIndicator(false)
if (result) {
root.closeTriggered()
@@ -12,6 +12,8 @@ Item {
property int headerTextMaximumLineCount: 2
property int headerTextElide: Qt.ElideRight
property string descriptionText
property string descriptionLinkText
property string descriptionLinkUrl
property alias headerRow: headerRow
implicitWidth: content.implicitWidth
@@ -43,5 +45,26 @@ Item {
color: AmneziaStyle.color.mutedGray
visible: root.descriptionText !== ""
}
ParagraphTextType {
id: descriptionLink
Layout.topMargin: 16
Layout.fillWidth: true
text: root.descriptionLinkText !== "" && root.descriptionLinkUrl !== ""
? ("<a href=\"" + root.descriptionLinkUrl + "\" style=\"color: " + AmneziaStyle.color.goldenApricotString + ";\">" + root.descriptionLinkText + "</a>")
: ""
textFormat: Text.RichText
visible: root.descriptionLinkText !== ""
onLinkActivated: function(link) {
Qt.openUrlExternally(link)
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
}
}
+6 -6
View File
@@ -344,14 +344,14 @@ PageType {
Keys.onReturnPressed: this.clicked()
onClicked: {
ServersUiController.setProcessedServerIndex(ServersUiController.defaultServerIndex)
ServersUiController.setProcessedServerId(ServersUiController.defaultServerId)
if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) {
if (ServersModel.getProcessedServerData("isCountrySelectionAvailable")) {
if (ServersUiController.isServerFromApi(ServersUiController.processedServerId)) {
if (ServersUiController.isServerCountrySelectionAvailable(ServersUiController.processedServerId)) {
PageController.goToPage(PageEnum.PageSettingsApiAvailableCountries)
} else {
PageController.showBusyIndicator(true)
let result = SubscriptionUiController.getAccountInfo(ServersUiController.getServerId(ServersUiController.processedServerIndex), false)
let result = SubscriptionUiController.getAccountInfo(ServersUiController.processedServerId, false)
PageController.showBusyIndicator(false)
if (!result) {
return
@@ -420,13 +420,13 @@ PageType {
target: ServersUiController
function onDefaultServerIndexChanged() {
function onDefaultServerIdChanged() {
updateContainersModelFilters()
}
}
function updateContainersModelFilters() {
if (ServersModel.isDefaultServerHasWriteAccess()) {
if (ServersUiController.isServerHasWriteAccess(ServersUiController.defaultServerId)) {
proxyDefaultServerContainersModel.filters = ContainersModelFilters.getWriteAccessProtocolsListFilters()
} else {
proxyDefaultServerContainersModel.filters = ContainersModelFilters.getReadAccessProtocolsListFilters()
@@ -435,13 +435,13 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ServersUiController.processedContainerIndex) {
if (ConnectionController.isConnected && ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId) === ServersUiController.processedContainerIndex) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, ProtocolEnum.Awg)
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Awg)
}
var noButtonFunction = function() {}
@@ -555,13 +555,13 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ServersUiController.processedContainerIndex) {
if (ConnectionController.isConnected && ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId) === ServersUiController.processedContainerIndex) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, ProtocolEnum.Awg)
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Awg)
}
var noButtonFunction = function() {}
@@ -428,13 +428,13 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ServersUiController.processedContainerIndex) {
if (ConnectionController.isConnected && ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId) === ServersUiController.processedContainerIndex) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, ProtocolEnum.OpenVpn)
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.OpenVpn)
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
+1 -1
View File
@@ -184,7 +184,7 @@ PageType {
var yesButtonFunction = function() {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
InstallController.removeContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex)
}
var noButtonFunction = function() {}
@@ -123,13 +123,13 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ServersUiController.processedContainerIndex) {
if (ConnectionController.isConnected && ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId) === ServersUiController.processedContainerIndex) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, ProtocolEnum.WireGuard)
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.WireGuard)
}
var noButtonFunction = function() {}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
@@ -123,13 +123,13 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ServersUiController.processedContainerIndex) {
if (ConnectionController.isConnected && ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId) === ServersUiController.processedContainerIndex) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, ProtocolEnum.WireGuard)
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.WireGuard)
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
@@ -107,7 +107,7 @@ PageType {
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function () {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ServersUiController.processedContainerIndex) {
if (ConnectionController.isConnected && ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId) === ServersUiController.processedContainerIndex) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
@@ -274,7 +274,7 @@ PageType {
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function () {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ServersUiController.processedContainerIndex) {
if (ConnectionController.isConnected && ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId) === ServersUiController.processedContainerIndex) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
@@ -64,6 +64,18 @@ PageType {
spacing: 0
Text {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 8
visible: !listView.enabled
wrapMode: Text.WordWrap
color: AmneziaStyle.color.paleGray
font.pixelSize: 14
text: qsTr("You have read-only access to this server. XRay settings cannot be edited.")
}
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: 16
@@ -73,6 +85,8 @@ PageType {
BaseHeaderType {
Layout.fillWidth: true
headerText: qsTr("XRay VLESS settings")
descriptionLinkText: qsTr("More about settings")
descriptionLinkUrl: "https://docs.amnezia.org"
}
ImageButtonType {
@@ -85,22 +99,6 @@ PageType {
}
}
LabelTextType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 4
text: qsTr("More about settings")
color: AmneziaStyle.color.goldenApricot
font.pixelSize: 16
lineHeight: 24 + LanguageUiController.getLineHeightAppend()
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: Qt.openUrlExternally("https://docs.amnezia.org")
}
}
TextFieldWithHeaderType {
id: textFieldWithHeaderType
Layout.fillWidth: true
@@ -173,8 +171,9 @@ PageType {
Layout.bottomMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
// Show Save immediately while user edits port, even before focus loss.
visible: listView.enabled && (XrayConfigModel.hasUnsavedChanges || textFieldWithHeaderType.textField.text !== port)
visible: listView.enabled
&& (XrayConfigModel.hasUnsavedChanges
|| textFieldWithHeaderType.textField.text !== port)
enabled: visible && textFieldWithHeaderType.errorText === ""
text: qsTr("Save")
onClicked: function() {
@@ -184,13 +183,17 @@ PageType {
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ServersUiController.processedContainerIndex) {
if (ConnectionController.isConnected && ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId) === ServersUiController.processedContainerIndex) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
if (textFieldWithHeaderType.textField.text !== port) {
port = textFieldWithHeaderType.textField.text
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
}
var noButtonFunction = function() {
if (!GC.isMobile()) saveButton.forceActiveFocus()
@@ -209,6 +212,8 @@ PageType {
clickedFunction: function() {
var yesButtonFunction = function() {
XrayConfigModel.resetToDefaults()
PageController.showNotificationMessage(
qsTr("Settings were reset to defaults. Tap Save to apply them on the server."))
}
showQuestionDrawer(qsTr("Reset settings?"), qsTr("All XRay settings will be restored to defaults."),
qsTr("Reset"), qsTr("Cancel"), yesButtonFunction, function() {
@@ -737,7 +737,7 @@ PageType {
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function () {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ServersUiController.processedContainerIndex) {
if (ConnectionController.isConnected && ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId) === ServersUiController.processedContainerIndex) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
@@ -90,7 +90,7 @@ PageType {
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function () {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ServersUiController.processedContainerIndex) {
if (ConnectionController.isConnected && ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId) === ServersUiController.processedContainerIndex) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
@@ -206,7 +206,7 @@ PageType {
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function () {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ServersUiController.processedContainerIndex) {
if (ConnectionController.isConnected && ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId) === ServersUiController.processedContainerIndex) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
@@ -203,7 +203,7 @@ PageType {
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function () {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ServersUiController.processedContainerIndex) {
if (ConnectionController.isConnected && ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId) === ServersUiController.processedContainerIndex) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
@@ -79,7 +79,7 @@ PageType {
PageController.showNotificationMessage(qsTr("Cannot remove AmneziaDNS from running server"))
} else {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
InstallController.removeContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex)
}
}
var noButtonFunction = function() {}
@@ -20,15 +20,10 @@ import "../Components"
PageType {
id: root
Rectangle {
anchors.fill: parent
z: -1
color: AmneziaStyle.color.onyxBlack
}
property int containerStatus: 1
property bool isUpdating: false
property bool isCheckingStatus: false
property bool isFetchingSecret: false
property bool previousEnabled: true
property int previousContainerStatus: 1
@@ -50,7 +45,7 @@ PageType {
onSavedTransportModeChanged: {
if (savedTransportMode === "faketls") {
root.syncedSecretTabIndex = 2
root.syncedSecretTabIndex = 1
} else if (savedTransportMode !== "") {
root.syncedSecretTabIndex = 0
}
@@ -68,9 +63,96 @@ PageType {
readonly property bool mtProxyNetworkBlocked: !NetworkReachabilityController.hasInternetAccess
readonly property bool navigationBlockedWhileBusy: isUpdating || diagLoading
property bool remoteOperationBusy: false
readonly property bool operationInProgress: isCheckingStatus || isFetchingSecret || isUpdating || diagLoading
readonly property bool pageBusy: operationInProgress || remoteOperationBusy
readonly property bool navigationBlockedWhileBusy: pageBusy
property bool pageOpenHandled: false
property bool busyIndicatorShown: false
function syncPageBusyIndicator() {
if (!root.pageOpenHandled) {
return
}
var wantBusy = root.pageBusy
if (wantBusy === root.busyIndicatorShown) {
return
}
root.busyIndicatorShown = wantBusy
PageController.showBusyIndicator(wantBusy)
}
onPageBusyChanged: syncPageBusyIndicator()
function mtProxyDomainToHex(domain) {
var hex = ""
for (var i = 0; i < domain.length; i++) {
var code = domain.charCodeAt(i).toString(16)
hex += (code.length < 2 ? "0" : "") + code
}
return hex
}
function mtProxyClientSecret(baseHex32, mode, tlsDomain) {
if (baseHex32 === "") {
return ""
}
if (mode === "faketls") {
return "ee" + baseHex32 + mtProxyDomainToHex(tlsDomain)
}
return "dd" + baseHex32
}
function mtProxyClientSecretForTabIndex(baseHex32, tabIndex, tlsDomain, defaultTlsDomain) {
var domain = tlsDomain !== "" ? tlsDomain : defaultTlsDomain
if (tabIndex === 1) {
return mtProxyClientSecret(baseHex32, "faketls", domain)
}
return mtProxyClientSecret(baseHex32, "standard", domain)
}
property bool containerStatusRefreshCallPending: false
function mtProxyRequestContainerStatusRefresh() {
if (!NetworkReachabilityController.hasInternetAccess) {
isCheckingStatus = false
syncPageBusyIndicator()
return
}
isCheckingStatus = true
syncPageBusyIndicator()
InstallController.refreshContainerStatus(ServersUiController.processedServerId, ServersUiController.processedContainerIndex)
}
function mtProxyScheduleContainerStatusRefresh() {
if (containerStatusRefreshCallPending) {
return
}
containerStatusRefreshCallPending = true
Qt.callLater(function () {
containerStatusRefreshCallPending = false
root.mtProxyRequestContainerStatusRefresh()
})
}
function mtProxyOnPageShown() {
if (root.pageOpenHandled) {
return
}
root.pageOpenHandled = true
PageController.disableControls(navigationBlockedWhileBusy)
if (!NetworkReachabilityController.hasInternetAccess) {
isCheckingStatus = false
} else {
isCheckingStatus = true
}
syncPageBusyIndicator()
root.mtProxyScheduleContainerStatusRefresh()
}
// Hex values that exist in last loaded / last successfully saved config show link panel only for these.
property var mtProxyPersistedAdditionalHex: []
function mtProxyRefreshPersistedAdditionalSecrets() {
@@ -92,19 +174,15 @@ PageType {
return false
}
// Rejects garbage like "123123123123"; only dotted IPv4 shape (3 digits per octet, 4 octets).
readonly property var natIpv4InputFormat: /^(\d{1,3}\.){0,3}\d{0,3}$/
// Defer SSH/updateContainer so QML control handlers return before nested event loops run;
// avoids "Object destroyed while one of its QML signal handlers is in progress".
function mtProxyScheduleUpdate(closePage) {
var cp = closePage === undefined ? false : closePage
Qt.callLater(function () {
InstallController.updateContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, ProtocolEnum.MtProxy, cp)
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.MtProxy, cp)
})
}
// Optional IPv4: show invalid while typing only when the string looks complete (four octets), so partial entry is not nagged.
function natIpv4FieldShowInvalidError(text) {
var t = text ? String(text).replace(/^\s+|\s+$/g, '') : ""
if (t === "")
@@ -167,15 +245,9 @@ PageType {
root.mtProxyRefreshPersistedAdditionalSecrets()
})
if (!NetworkReachabilityController.hasInternetAccess) {
isCheckingStatus = false
return
}
isCheckingStatus = true
InstallController.refreshContainerStatus(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
Qt.callLater(root.mtProxyOnPageShown)
}
// Block back navigation and Escape (via PageStart.isControlsDisabled) while SSH/update or diagnostics refresh runs.
onNavigationBlockedWhileBusyChanged: {
if (root.visible) {
PageController.disableControls(navigationBlockedWhileBusy)
@@ -184,10 +256,16 @@ PageType {
onVisibleChanged: {
if (!visible) {
root.pageOpenHandled = false
containerStatusRefreshCallPending = false
isCheckingStatus = false
isFetchingSecret = false
busyIndicatorShown = false
PageController.disableControls(false)
PageController.showBusyIndicator(false)
diagLoading = false
} else {
PageController.disableControls(navigationBlockedWhileBusy)
root.mtProxyOnPageShown()
}
}
@@ -199,8 +277,7 @@ PageType {
return
}
if (NetworkReachabilityController.hasInternetAccess) {
isCheckingStatus = true
InstallController.refreshContainerStatus(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
root.mtProxyScheduleContainerStatusRefresh()
}
}
}
@@ -208,10 +285,15 @@ PageType {
Connections {
target: InstallController
function onServerIsBusy(busy) {
remoteOperationBusy = busy
}
function onUpdateContainerFinished(message, closePage) {
if (!root.visible) {
isUpdating = false
isCheckingStatus = false
isFetchingSecret = false
return
}
isUpdating = false
@@ -227,9 +309,11 @@ PageType {
if (!root.visible) {
isUpdating = false
isCheckingStatus = false
isFetchingSecret = false
return
}
isUpdating = false
isFetchingSecret = false
containerStatus = previousContainerStatus
MtProxyConfigModel.setEnabled(previousEnabled)
MtProxyConfigModel.setPort(previousPort)
@@ -254,6 +338,7 @@ PageType {
}
if (enabled && pendingUpdateAfterEnable) {
pendingUpdateAfterEnable = false
isUpdating = true
root.mtProxyScheduleUpdate(false)
return
}
@@ -266,9 +351,9 @@ PageType {
function onContainerStatusRefreshed(status) {
if (!root.visible) {
isCheckingStatus = false
isFetchingSecret = false
return
}
isCheckingStatus = false
containerStatus = status
root.savedTransportMode = MtProxyConfigModel.getTransportMode()
@@ -276,10 +361,17 @@ PageType {
root.savedPublicHost = MtProxyConfigModel.getPublicHost()
if (status === 1) {
MtProxyConfigModel.setEnabled(true)
InstallController.fetchContainerSecret(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
} else if (status === 2) {
MtProxyConfigModel.setEnabled(false)
isFetchingSecret = true
isCheckingStatus = false
InstallController.fetchContainerSecret(ServersUiController.processedServerId, ServersUiController.processedContainerIndex)
} else {
isFetchingSecret = false
isCheckingStatus = false
if (status === 2) {
MtProxyConfigModel.setEnabled(false)
}
}
syncPageBusyIndicator()
}
function onContainerDiagnosticsRefreshed(portReachable, upstreamReachable, clientsConnected, lastConfigRefresh, statsEndpoint) {
@@ -296,20 +388,35 @@ PageType {
function onContainerSecretFetched(secret) {
if (!root.visible) {
isFetchingSecret = false
return
}
isFetchingSecret = false
syncPageBusyIndicator()
MtProxyConfigModel.validateAndSetSecret(secret)
}
}
Item {
id: contentLayer
anchors.fill: parent
enabled: !root.pageBusy
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
anchors.topMargin: 20 + PageController.safeAreaTopMargin
onFocusChanged: {
if (this.activeFocus) connectionListView.positionViewAtBeginning()
if (this.activeFocus) {
if (mainTabBar.currentIndex === 0) {
connectionListView.positionViewAtBeginning()
} else {
settingsListView.positionViewAtBeginning()
}
}
}
}
@@ -318,57 +425,62 @@ PageType {
anchors.top: backButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 8
spacing: 0
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 8
Layout.bottomMargin: 24
headerText: qsTr("MTProxy settings")
descriptionLinkText: qsTr("Read more about this settings")
descriptionLinkUrl: "https://core.telegram.org/proxy"
}
LabelWithButtonType {
CaptionTextType {
Layout.fillWidth: true
Layout.leftMargin: 0
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Read more about this settings")
textColor: AmneziaStyle.color.goldenApricot
clickedFunction: function () {
Qt.openUrlExternally("https://core.telegram.org/proxy")
Layout.topMargin: 8
visible: root.mtProxyNetworkBlocked
text: qsTr("No internet connection. Connect to the internet to change MTProxy settings.")
color: AmneziaStyle.color.mutedGray
wrapMode: Text.WordWrap
font.pixelSize: 14
}
}
TabBar {
id: mainTabBar
anchors.top: pageHeader.bottom
anchors.left: parent.left
anchors.right: parent.right
width: parent.width
background: Rectangle {
color: AmneziaStyle.color.transparent
Rectangle {
width: parent.width
height: 1
anchors.bottom: parent.bottom
color: AmneziaStyle.color.slateGray
}
}
TabBar {
id: mainTabBar
Layout.fillWidth: true
Layout.topMargin: 4
background: Rectangle {
color: AmneziaStyle.color.transparent
Rectangle {
width: parent.width
height: 1
anchors.bottom: parent.bottom
color: AmneziaStyle.color.slateGray
}
}
TabButtonType {
text: qsTr("Connection")
isSelected: mainTabBar.currentIndex === 0
}
TabButtonType {
text: qsTr("Settings")
isSelected: mainTabBar.currentIndex === 1
}
TabButtonType {
text: qsTr("Connection")
isSelected: mainTabBar.currentIndex === 0
}
TabButtonType {
text: qsTr("Settings")
isSelected: mainTabBar.currentIndex === 1
}
}
StackLayout {
id: tabContent
anchors.top: pageHeader.bottom
anchors.top: mainTabBar.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
@@ -382,35 +494,11 @@ PageType {
width: connectionListView.width
spacing: 0
function domainToHex(domain) {
var hex = ""
for (var i = 0; i < domain.length; i++) {
var code = domain.charCodeAt(i).toString(16)
hex += (code.length < 2 ? "0" : "") + code
}
return hex
}
function secretForMode(mode) {
if (mode === "faketls") {
var domain = root.savedTlsDomain !== "" ? root.savedTlsDomain : MtProxyConfigModel.defaultTlsDomain()
return "ee" + secret + domainToHex(domain)
} else if (mode === "padded") {
return "dd" + secret
}
return secret
}
property int secretTabIndex: root.syncedSecretTabIndex
function activeSecret() {
if (root.syncedSecretTabIndex === 0) {
return secretForMode("standard")
}
if (root.syncedSecretTabIndex === 1) {
return secretForMode("padded")
}
return secretForMode("faketls")
return root.mtProxyClientSecretForTabIndex(secret, root.syncedSecretTabIndex,
root.savedTlsDomain, MtProxyConfigModel.defaultTlsDomain())
}
function effectiveSecret() {
@@ -418,7 +506,7 @@ PageType {
}
function effectiveHost() {
return root.savedPublicHost !== "" ? root.savedPublicHost : ServersModel.getProcessedServerData("hostName")
return root.savedPublicHost !== "" ? root.savedPublicHost : ServersUiController.serverHostName(ServersUiController.processedServerId)
}
function tmeLink() {
@@ -720,7 +808,7 @@ PageType {
Layout.bottomMargin: 24
Layout.leftMargin: 0
Layout.rightMargin: 16
visible: ServersModel.isProcessedServerHasWriteAccess()
visible: ServersUiController.isProcessedServerHasWriteAccess()
text: qsTr("Delete MTProxy")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function () {
@@ -730,7 +818,7 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function () {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
InstallController.removeContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex)
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, function () {
})
@@ -754,37 +842,13 @@ PageType {
width: settingsListView.width
spacing: 0
function mtProxyDomainToHex(domain) {
var hex = ""
for (var i = 0; i < domain.length; i++) {
var code = domain.charCodeAt(i).toString(16)
hex += (code.length < 2 ? "0" : "") + code
}
return hex
}
function mtProxySecretForBaseHex(baseHex, mode) {
if (mode === "faketls") {
var domain = root.savedTlsDomain !== "" ? root.savedTlsDomain : MtProxyConfigModel.defaultTlsDomain()
return "ee" + baseHex + mtProxyDomainToHex(domain)
} else if (mode === "padded") {
return "dd" + baseHex
}
return baseHex
}
function mtProxyActiveSecretForBaseHex(baseHex) {
if (root.syncedSecretTabIndex === 0) {
return mtProxySecretForBaseHex(baseHex, "standard")
}
if (root.syncedSecretTabIndex === 1) {
return mtProxySecretForBaseHex(baseHex, "padded")
}
return mtProxySecretForBaseHex(baseHex, "faketls")
return root.mtProxyClientSecretForTabIndex(baseHex, root.syncedSecretTabIndex,
root.savedTlsDomain, MtProxyConfigModel.defaultTlsDomain())
}
function mtProxyEffectiveHostForLinks() {
return root.savedPublicHost !== "" ? root.savedPublicHost : ServersModel.getProcessedServerData("hostName")
return root.savedPublicHost !== "" ? root.savedPublicHost : ServersUiController.serverHostName(ServersUiController.processedServerId)
}
function mtProxyTmeLinkForAdditional(baseHex) {
@@ -804,7 +868,7 @@ PageType {
Layout.bottomMargin: 16
text: qsTr("Enable MTProxy")
checked: isEnabled
enabled: !isCheckingStatus && containerStatus !== 0 && containerStatus !== 3 && !isUpdating
enabled: containerStatus !== 0 && containerStatus !== 3 && !root.pageBusy
&& !root.mtProxyNetworkBlocked
onToggled: function () {
if (checked !== isEnabled) {
@@ -815,9 +879,9 @@ PageType {
isUpdating = true
if (checked) {
root.pendingUpdateAfterEnable = true
InstallController.setContainerEnabled(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, true)
InstallController.setContainerEnabled(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, true)
} else {
InstallController.setContainerEnabled(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, false)
InstallController.setContainerEnabled(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, false)
}
}
}
@@ -843,19 +907,20 @@ PageType {
CaptionTextType {
Layout.fillWidth: true
text: secret !== "" ? secret : qsTr("Not generated")
text: secret !== "" ? mtProxyActiveSecretForBaseHex(secret) : qsTr("Not generated")
color: secret !== "" ? AmneziaStyle.color.paleGray : AmneziaStyle.color.mutedGray
elide: Text.ElideMiddle
wrapMode: Text.WrapAnywhere
font.pixelSize: 14
}
ImageButtonType {
Layout.alignment: Qt.AlignTop
implicitWidth: 36
implicitHeight: 36
hoverEnabled: true
image: "qrc:/images/controls/refresh-cw.svg"
imageColor: AmneziaStyle.color.paleGray
visible: ServersModel.isProcessedServerHasWriteAccess()
visible: ServersUiController.isProcessedServerHasWriteAccess()
onClicked: {
var secretSnapshot = secret
showQuestionDrawer(
@@ -889,7 +954,7 @@ PageType {
Layout.rightMargin: 16
Layout.bottomMargin: 4
headerText: qsTr("Public host / IP")
textField.placeholderText: ServersModel.getProcessedServerData("hostName")
textField.placeholderText: ServersUiController.serverHostName(ServersUiController.processedServerId)
textField.text: publicHost
textField.maximumLength: 253
textField.validator: PublicHostInputValidator {
@@ -936,7 +1001,7 @@ PageType {
Layout.rightMargin: 16
Layout.bottomMargin: 12
visible: publicHostTextField.textField.text !== "" &&
publicHostTextField.textField.text !== ServersModel.getProcessedServerData("hostName")
publicHostTextField.textField.text !== ServersUiController.serverHostName(ServersUiController.processedServerId)
text: qsTr("⚠ This overrides the server IP in connection links. Make sure this host/domain points to your server.")
color: AmneziaStyle.color.goldenApricot
font.pixelSize: 12
@@ -1098,6 +1163,7 @@ PageType {
clickedFunction: function () {
transportMode = (index === 0) ? "standard" : "faketls"
MtProxyConfigModel.setTransportMode(transportMode)
root.syncedSecretTabIndex = transportMode === "faketls" ? 1 : 0
transportModeDropDown.closeTriggered()
}
}
@@ -1281,11 +1347,14 @@ PageType {
implicitWidth: 32
implicitHeight: 32
hoverEnabled: true
visible: ServersModel.isProcessedServerHasWriteAccess()
visible: ServersUiController.isProcessedServerHasWriteAccess()
image: "qrc:/images/controls/trash.svg"
imageColor: AmneziaStyle.color.vibrantRed
onClicked: {
MtProxyConfigModel.removeAdditionalSecret(index)
if (containerStatus === 1) {
root.mtProxyScheduleUpdate(false)
}
}
}
}
@@ -1628,7 +1697,7 @@ PageType {
enabled: !diagLoading
onClicked: {
diagLoading = true
InstallController.refreshContainerDiagnostics(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, parseInt(port))
InstallController.refreshContainerDiagnostics(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, parseInt(port))
}
}
}
@@ -1754,7 +1823,7 @@ PageType {
Layout.bottomMargin: 32
Layout.rightMargin: 16
Layout.leftMargin: 16
visible: ServersModel.isProcessedServerHasWriteAccess()
visible: ServersUiController.isProcessedServerHasWriteAccess()
enabled: !root.mtProxyNetworkBlocked
text: qsTr("Save")
clickedFunc: function () {
@@ -1852,34 +1921,5 @@ PageType {
}
}
Rectangle {
anchors.fill: parent
visible: isCheckingStatus || isUpdating || root.mtProxyNetworkBlocked
color: AmneziaStyle.color.midnightBlack
opacity: 0.6
z: 1
MouseArea {
anchors.fill: parent
}
BusyIndicator {
anchors.centerIn: parent
visible: isCheckingStatus || isUpdating
running: isCheckingStatus || isUpdating
width: 48
height: 48
}
CaptionTextType {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 24
anchors.rightMargin: 24
visible: root.mtProxyNetworkBlocked && !isCheckingStatus && !isUpdating
horizontalAlignment: Text.AlignHCenter
text: qsTr("No internet connection. Connect to the internet to change MTProxy settings.")
color: AmneziaStyle.color.paleGray
wrapMode: Text.WordWrap
font.pixelSize: 14
}
}
}
@@ -73,7 +73,7 @@ PageType {
Layout.rightMargin: 16
text: qsTr("Host")
descriptionText: ServersModel.getProcessedServerData("hostName")
descriptionText: ServersUiController.serverHostName(ServersUiController.processedServerId)
descriptionOnTop: true
@@ -173,7 +173,7 @@ PageType {
clickedFunc: function() {
PageController.showBusyIndicator(true)
InstallController.mountSftpDrive(ServersUiController.getServerId(ServersUiController.processedServerIndex), port, password, username)
InstallController.mountSftpDrive(ServersUiController.processedServerId, port, password, username)
PageController.showBusyIndicator(false)
}
}
@@ -71,7 +71,7 @@ PageType {
Layout.bottomMargin: 16
text: qsTr("Host")
descriptionText: ServersModel.getProcessedServerData("hostName")
descriptionText: ServersUiController.serverHostName(ServersUiController.processedServerId)
descriptionOnTop: true
@@ -285,7 +285,7 @@ PageType {
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, ProtocolEnum.Socks5Proxy)
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Socks5Proxy)
tempPort = portTextField.textField.text
tempUsername = usernameTextField.textField.text
tempPassword = passwordTextField.textField.text
+206 -126
View File
@@ -18,15 +18,10 @@ import "../Components"
PageType {
id: root
Rectangle {
anchors.fill: parent
z: -1
color: AmneziaStyle.color.onyxBlack
}
property int containerStatus: 1
property bool isUpdating: false
property bool isCheckingStatus: false
property bool isFetchingSecret: false
property bool previousEnabled: true
property int previousContainerStatus: 1
@@ -40,6 +35,7 @@ PageType {
property bool previousNatEnabled: false
property string previousNatInternalIp: ""
property string previousNatExternalIp: ""
property string previousSecret: ""
property string savedTransportMode: ""
property string savedTlsDomain: ""
@@ -47,7 +43,7 @@ PageType {
onSavedTransportModeChanged: {
if (savedTransportMode === "faketls") {
root.syncedSecretTabIndex = 2
root.syncedSecretTabIndex = 1
} else if (savedTransportMode !== "") {
root.syncedSecretTabIndex = 0
}
@@ -64,13 +60,101 @@ PageType {
property string diagStatsEndpoint: ""
readonly property bool telemtNetworkBlocked: !NetworkReachabilityController.hasInternetAccess
readonly property bool navigationBlockedWhileBusy: isUpdating || diagLoading
// Defer SSH/updateContainer so QML control handlers return before nested event loops run.
property bool remoteOperationBusy: false
readonly property bool operationInProgress: isCheckingStatus || isFetchingSecret || isUpdating || diagLoading
readonly property bool pageBusy: operationInProgress || remoteOperationBusy
readonly property bool navigationBlockedWhileBusy: pageBusy
property bool pageOpenHandled: false
property bool busyIndicatorShown: false
function syncPageBusyIndicator() {
if (!root.pageOpenHandled) {
return
}
var wantBusy = root.pageBusy
if (wantBusy === root.busyIndicatorShown) {
return
}
root.busyIndicatorShown = wantBusy
PageController.showBusyIndicator(wantBusy)
}
onPageBusyChanged: syncPageBusyIndicator()
function telemtDomainToHex(domain) {
var hex = ""
for (var i = 0; i < domain.length; i++) {
var code = domain.charCodeAt(i).toString(16)
hex += (code.length < 2 ? "0" : "") + code
}
return hex
}
function telemtClientSecret(baseHex32, mode, tlsDomain) {
if (baseHex32 === "") {
return ""
}
if (mode === "faketls") {
return "ee" + baseHex32 + telemtDomainToHex(tlsDomain)
}
return "dd" + baseHex32
}
function telemtClientSecretForTabIndex(baseHex32, tabIndex, tlsDomain, defaultTlsDomain) {
var domain = tlsDomain !== "" ? tlsDomain : defaultTlsDomain
if (tabIndex === 1) {
return telemtClientSecret(baseHex32, "faketls", domain)
}
return telemtClientSecret(baseHex32, "standard", domain)
}
property bool containerStatusRefreshCallPending: false
function telemtRequestContainerStatusRefresh() {
if (!NetworkReachabilityController.hasInternetAccess) {
isCheckingStatus = false
syncPageBusyIndicator()
return
}
isCheckingStatus = true
syncPageBusyIndicator()
InstallController.refreshContainerStatus(ServersUiController.processedServerId, ServersUiController.processedContainerIndex)
}
function telemtScheduleContainerStatusRefresh() {
if (containerStatusRefreshCallPending) {
return
}
containerStatusRefreshCallPending = true
Qt.callLater(function () {
containerStatusRefreshCallPending = false
root.telemtRequestContainerStatusRefresh()
})
}
function telemtOnPageShown() {
if (root.pageOpenHandled) {
return
}
root.pageOpenHandled = true
PageController.disableControls(navigationBlockedWhileBusy)
if (!NetworkReachabilityController.hasInternetAccess) {
isCheckingStatus = false
} else {
isCheckingStatus = true
}
syncPageBusyIndicator()
root.telemtScheduleContainerStatusRefresh()
}
function telemtScheduleUpdate(closePage) {
var cp = closePage === undefined ? false : closePage
Qt.callLater(function () {
InstallController.updateContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, ProtocolEnum.Telemt, cp)
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Telemt, cp)
})
}
@@ -105,12 +189,7 @@ PageType {
root.savedTlsDomain = TelemtConfigModel.getTlsDomain()
root.savedPublicHost = TelemtConfigModel.getPublicHost()
if (!NetworkReachabilityController.hasInternetAccess) {
isCheckingStatus = false
return
}
isCheckingStatus = true
InstallController.refreshContainerStatus(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
Qt.callLater(root.telemtOnPageShown)
}
onNavigationBlockedWhileBusyChanged: {
@@ -121,10 +200,16 @@ PageType {
onVisibleChanged: {
if (!visible) {
root.pageOpenHandled = false
containerStatusRefreshCallPending = false
isCheckingStatus = false
isFetchingSecret = false
busyIndicatorShown = false
PageController.disableControls(false)
PageController.showBusyIndicator(false)
diagLoading = false
} else {
PageController.disableControls(navigationBlockedWhileBusy)
root.telemtOnPageShown()
}
}
@@ -136,8 +221,7 @@ PageType {
return
}
if (NetworkReachabilityController.hasInternetAccess) {
isCheckingStatus = true
InstallController.refreshContainerStatus(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
root.telemtScheduleContainerStatusRefresh()
}
}
}
@@ -145,10 +229,15 @@ PageType {
Connections {
target: InstallController
function onServerIsBusy(busy) {
remoteOperationBusy = busy
}
function onUpdateContainerFinished(message, closePage) {
if (!root.visible) {
isUpdating = false
isCheckingStatus = false
isFetchingSecret = false
return
}
isUpdating = false
@@ -166,9 +255,11 @@ PageType {
if (!root.visible) {
isUpdating = false
isCheckingStatus = false
isFetchingSecret = false
return
}
isUpdating = false
isFetchingSecret = false
containerStatus = previousContainerStatus
TelemtConfigModel.setEnabled(previousEnabled)
TelemtConfigModel.setPort(previousPort)
@@ -181,6 +272,9 @@ PageType {
TelemtConfigModel.setNatEnabled(previousNatEnabled)
TelemtConfigModel.setNatInternalIp(previousNatInternalIp)
TelemtConfigModel.setNatExternalIp(previousNatExternalIp)
if (previousSecret !== "") {
TelemtConfigModel.setSecret(previousSecret)
}
}
function onSetContainerEnabledFinished(enabled) {
@@ -190,6 +284,7 @@ PageType {
}
if (enabled && pendingUpdateAfterEnable) {
pendingUpdateAfterEnable = false
isUpdating = true
root.telemtScheduleUpdate(false)
return
}
@@ -202,9 +297,9 @@ PageType {
function onContainerStatusRefreshed(status) {
if (!root.visible) {
isCheckingStatus = false
isFetchingSecret = false
return
}
isCheckingStatus = false
containerStatus = status
root.savedTransportMode = TelemtConfigModel.getTransportMode()
@@ -212,10 +307,17 @@ PageType {
root.savedPublicHost = TelemtConfigModel.getPublicHost()
if (status === 1) {
TelemtConfigModel.setEnabled(true)
InstallController.fetchContainerSecret(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
} else if (status === 2) {
TelemtConfigModel.setEnabled(false)
isFetchingSecret = true
isCheckingStatus = false
InstallController.fetchContainerSecret(ServersUiController.processedServerId, ServersUiController.processedContainerIndex)
} else {
isFetchingSecret = false
isCheckingStatus = false
if (status === 2) {
TelemtConfigModel.setEnabled(false)
}
}
syncPageBusyIndicator()
}
function onContainerDiagnosticsRefreshed(portReachable, upstreamReachable, clientsConnected, lastConfigRefresh, statsEndpoint) {
@@ -232,20 +334,35 @@ PageType {
function onContainerSecretFetched(secret) {
if (!root.visible) {
isFetchingSecret = false
return
}
isFetchingSecret = false
syncPageBusyIndicator()
TelemtConfigModel.validateAndSetSecret(secret)
}
}
Item {
id: contentLayer
anchors.fill: parent
enabled: !root.pageBusy
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
anchors.topMargin: 20 + PageController.safeAreaTopMargin
onFocusChanged: {
if (this.activeFocus) connectionListView.positionViewAtBeginning()
if (this.activeFocus) {
if (mainTabBar.currentIndex === 0) {
connectionListView.positionViewAtBeginning()
} else {
settingsListView.positionViewAtBeginning()
}
}
}
}
@@ -254,57 +371,62 @@ PageType {
anchors.top: backButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 8
spacing: 0
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 8
Layout.bottomMargin: 24
headerText: qsTr("Telemt settings")
descriptionLinkText: qsTr("Read more about this settings")
descriptionLinkUrl: "https://github.com/telemt/telemt"
}
LabelWithButtonType {
CaptionTextType {
Layout.fillWidth: true
Layout.leftMargin: 0
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Read more about this settings")
textColor: AmneziaStyle.color.goldenApricot
clickedFunction: function () {
Qt.openUrlExternally("https://github.com/telemt/telemt")
Layout.topMargin: 8
visible: root.telemtNetworkBlocked
text: qsTr("No internet connection. Connect to the internet to change Telemt settings.")
color: AmneziaStyle.color.mutedGray
wrapMode: Text.WordWrap
font.pixelSize: 14
}
}
TabBar {
id: mainTabBar
anchors.top: pageHeader.bottom
anchors.left: parent.left
anchors.right: parent.right
width: parent.width
background: Rectangle {
color: AmneziaStyle.color.transparent
Rectangle {
width: parent.width
height: 1
anchors.bottom: parent.bottom
color: AmneziaStyle.color.slateGray
}
}
TabBar {
id: mainTabBar
Layout.fillWidth: true
Layout.topMargin: 4
background: Rectangle {
color: AmneziaStyle.color.transparent
Rectangle {
width: parent.width
height: 1
anchors.bottom: parent.bottom
color: AmneziaStyle.color.slateGray
}
}
TabButtonType {
text: qsTr("Connection")
isSelected: mainTabBar.currentIndex === 0
}
TabButtonType {
text: qsTr("Settings")
isSelected: mainTabBar.currentIndex === 1
}
TabButtonType {
text: qsTr("Connection")
isSelected: mainTabBar.currentIndex === 0
}
TabButtonType {
text: qsTr("Settings")
isSelected: mainTabBar.currentIndex === 1
}
}
StackLayout {
id: tabContent
anchors.top: pageHeader.bottom
anchors.top: mainTabBar.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
@@ -318,36 +440,11 @@ PageType {
width: connectionListView.width
spacing: 0
function domainToHex(domain) {
var hex = ""
for (var i = 0; i < domain.length; i++) {
var code = domain.charCodeAt(i).toString(16)
hex += (code.length < 2 ? "0" : "") + code
}
return hex
}
function secretForMode(mode) {
if (mode === "faketls") {
var domain = root.savedTlsDomain !== "" ? root.savedTlsDomain : TelemtConfigModel.defaultTlsDomain()
return "ee" + secret + domainToHex(domain)
} else if (mode === "padded") {
return "dd" + secret
}
// Telemt default (secure MTProto, not FakeTLS): Telegram proxy links require dd + hex secret
return "dd" + secret
}
property int secretTabIndex: root.syncedSecretTabIndex
function activeSecret() {
if (root.syncedSecretTabIndex === 0) {
return secretForMode("standard")
}
if (root.syncedSecretTabIndex === 1) {
return secretForMode("padded")
}
return secretForMode("faketls")
return root.telemtClientSecretForTabIndex(secret, root.syncedSecretTabIndex,
root.savedTlsDomain, TelemtConfigModel.defaultTlsDomain())
}
function effectiveSecret() {
@@ -355,7 +452,7 @@ PageType {
}
function effectiveHost() {
return root.savedPublicHost !== "" ? root.savedPublicHost : ServersModel.getProcessedServerData("hostName")
return root.savedPublicHost !== "" ? root.savedPublicHost : ServersUiController.serverHostName(ServersUiController.processedServerId)
}
function tmeLink() {
@@ -657,7 +754,7 @@ PageType {
Layout.bottomMargin: 24
Layout.leftMargin: 0
Layout.rightMargin: 16
visible: ServersModel.isProcessedServerHasWriteAccess()
visible: ServersUiController.isProcessedServerHasWriteAccess()
text: qsTr("Delete Telemt")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function () {
@@ -667,7 +764,7 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function () {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
InstallController.removeContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex)
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, function () {
})
@@ -690,6 +787,11 @@ PageType {
width: settingsListView.width
spacing: 0
function telemtActiveSecretForBaseHex(baseHex) {
return root.telemtClientSecretForTabIndex(baseHex, root.syncedSecretTabIndex,
root.savedTlsDomain, TelemtConfigModel.defaultTlsDomain())
}
SwitcherType {
id: enableTelemtSwitch
Layout.fillWidth: true
@@ -699,18 +801,20 @@ PageType {
Layout.bottomMargin: 16
text: qsTr("Enable Telemt")
checked: isEnabled
enabled: !isCheckingStatus && containerStatus !== 0 && containerStatus !== 3 && !isUpdating
enabled: containerStatus !== 0 && containerStatus !== 3 && !root.pageBusy
&& !root.telemtNetworkBlocked
onToggled: function () {
if (checked !== isEnabled) {
previousEnabled = isEnabled
previousContainerStatus = containerStatus
root.previousSecret = secret
isEnabled = checked
isUpdating = true
if (checked) {
root.pendingUpdateAfterEnable = true
InstallController.setContainerEnabled(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, true)
InstallController.setContainerEnabled(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, true)
} else {
InstallController.setContainerEnabled(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, false)
InstallController.setContainerEnabled(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, false)
}
}
}
@@ -736,26 +840,29 @@ PageType {
CaptionTextType {
Layout.fillWidth: true
text: secret !== "" ? secret : qsTr("Not generated")
text: secret !== "" ? telemtActiveSecretForBaseHex(secret) : qsTr("Not generated")
color: secret !== "" ? AmneziaStyle.color.paleGray : AmneziaStyle.color.mutedGray
elide: Text.ElideMiddle
wrapMode: Text.WrapAnywhere
font.pixelSize: 14
}
ImageButtonType {
Layout.alignment: Qt.AlignTop
implicitWidth: 36
implicitHeight: 36
hoverEnabled: true
image: "qrc:/images/controls/refresh-cw.svg"
imageColor: AmneziaStyle.color.paleGray
visible: ServersModel.isProcessedServerHasWriteAccess()
visible: ServersUiController.isProcessedServerHasWriteAccess()
onClicked: {
var secretSnapshot = secret
showQuestionDrawer(
qsTr("Generate new secret?"),
qsTr("All existing connection links will stop working. Users will need new links."),
qsTr("Generate"),
qsTr("Cancel"),
function () {
root.previousSecret = secretSnapshot
if (containerStatus === 1) {
isUpdating = true
TelemtConfigModel.generateSecret()
@@ -780,7 +887,7 @@ PageType {
Layout.rightMargin: 16
Layout.bottomMargin: 4
headerText: qsTr("Public host / IP")
textField.placeholderText: ServersModel.getProcessedServerData("hostName")
textField.placeholderText: ServersUiController.serverHostName(ServersUiController.processedServerId)
textField.text: publicHost
textField.onEditingFinished: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
@@ -809,7 +916,7 @@ PageType {
Layout.rightMargin: 16
Layout.bottomMargin: 12
visible: publicHostTextField.textField.text !== "" &&
publicHostTextField.textField.text !== ServersModel.getProcessedServerData("hostName")
publicHostTextField.textField.text !== ServersUiController.serverHostName(ServersUiController.processedServerId)
text: qsTr("⚠ This overrides the server IP in connection links. Make sure this host/domain points to your server.")
color: AmneziaStyle.color.goldenApricot
font.pixelSize: 12
@@ -926,6 +1033,7 @@ PageType {
clickedFunction: function () {
transportMode = (index === 0) ? "standard" : "faketls"
TelemtConfigModel.setTransportMode(transportMode)
root.syncedSecretTabIndex = transportMode === "faketls" ? 1 : 0
transportModeDropDown.closeTriggered()
}
}
@@ -1240,7 +1348,7 @@ PageType {
enabled: !diagLoading
onClicked: {
diagLoading = true
InstallController.refreshContainerDiagnostics(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, parseInt(port))
InstallController.refreshContainerDiagnostics(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, parseInt(port))
}
}
}
@@ -1366,7 +1474,7 @@ PageType {
Layout.bottomMargin: 32
Layout.rightMargin: 16
Layout.leftMargin: 16
visible: ServersModel.isProcessedServerHasWriteAccess()
visible: ServersUiController.isProcessedServerHasWriteAccess()
text: qsTr("Save")
clickedFunc: function () {
var portValue = portTextField.textField.text === ""
@@ -1406,6 +1514,7 @@ PageType {
previousNatEnabled = natEnabled
previousNatInternalIp = natInternalIp
previousNatExternalIp = natExternalIp
root.previousSecret = secret
isUpdating = true
root.telemtScheduleUpdate(false)
}
@@ -1414,34 +1523,5 @@ PageType {
}
}
Rectangle {
anchors.fill: parent
visible: isCheckingStatus || isUpdating || root.telemtNetworkBlocked
color: AmneziaStyle.color.midnightBlack
opacity: 0.6
z: 1
MouseArea {
anchors.fill: parent
}
BusyIndicator {
anchors.centerIn: parent
visible: isCheckingStatus || isUpdating
running: isCheckingStatus || isUpdating
width: 48
height: 48
}
CaptionTextType {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 24
anchors.rightMargin: 24
visible: root.telemtNetworkBlocked && !isCheckingStatus && !isUpdating
horizontalAlignment: Text.AlignHCenter
text: qsTr("No internet connection. Connect to the internet to change Telemt settings.")
color: AmneziaStyle.color.paleGray
wrapMode: Text.WordWrap
font.pixelSize: 14
}
}
}
@@ -24,8 +24,8 @@ PageType {
property bool isInAppPurchase: false
function updateSubscriptionState() {
root.subscriptionExpired = ServersModel.getProcessedServerData("isSubscriptionExpired")
root.subscriptionExpiringSoon = ServersModel.getProcessedServerData("isSubscriptionExpiringSoon")
root.subscriptionExpired = ServersUiController.isServerSubscriptionExpired(ServersUiController.processedServerId)
root.subscriptionExpiringSoon = ServersUiController.isServerSubscriptionExpiringSoon(ServersUiController.processedServerId)
root.isSubscriptionRenewalAvailable = ApiAccountInfoModel.data("isSubscriptionRenewalAvailable")
root.isInAppPurchase = ApiAccountInfoModel.data("isInAppPurchase")
}
@@ -35,14 +35,22 @@ PageType {
}
Connections {
target: ServersModel
target: ServersUiController
function onProcessedServerChanged() {
function onProcessedServerIdChanged() {
root.processedServer = proxyServersModel.get(0)
root.updateSubscriptionState()
}
}
Connections {
target: ServersModel
function onModelReset() {
root.processedServer = proxyServersModel.get(0)
}
}
Connections {
target: ApiAccountInfoModel
@@ -58,8 +66,8 @@ PageType {
sourceModel: ServersModel
filters: [
ValueFilter {
roleName: "isCurrentlyProcessed"
value: true
roleName: "serverId"
value: ServersUiController.processedServerId
}
]
@@ -108,7 +116,7 @@ PageType {
actionButtonFunction: function() {
PageController.showBusyIndicator(true)
let result = SubscriptionUiController.getAccountInfo(ServersUiController.getServerId(ServersUiController.processedServerIndex), false)
let result = SubscriptionUiController.getAccountInfo(ServersUiController.processedServerId, false)
PageController.showBusyIndicator(false)
if (!result) {
return
@@ -148,7 +156,7 @@ PageType {
text: qsTr("Renew subscription")
clickedFunc: function() {
SubscriptionUiController.getRenewalLink(ServersUiController.getServerId(ServersUiController.processedServerIndex))
SubscriptionUiController.getRenewalLink(ServersUiController.processedServerId)
}
}
@@ -200,7 +208,7 @@ PageType {
PageController.showBusyIndicator(true)
var prevIndex = ApiCountryModel.currentIndex
ApiCountryModel.currentIndex = index
if (!SubscriptionUiController.updateServiceFromGateway(ServersUiController.getServerId(ServersUiController.processedServerIndex), countryCode, countryName)) {
if (!SubscriptionUiController.updateServiceFromGateway(ServersUiController.processedServerId, countryCode, countryName)) {
ApiCountryModel.currentIndex = prevIndex
}
PageController.showBusyIndicator(false)
@@ -82,7 +82,7 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
var serverId = ServersUiController.getServerId(ServersUiController.processedServerIndex)
var serverId = ServersUiController.processedServerId
Qt.callLater(deactivateExternalDevice, serverId, supportTag, countryCode)
}
var noButtonFunction = function() {
@@ -191,7 +191,7 @@ PageType {
}
if (fileName !== "") {
PageController.showBusyIndicator(true)
let result = SubscriptionUiController.exportNativeConfig(ServersUiController.getServerId(ServersUiController.processedServerIndex), countryCode, fileName)
let result = SubscriptionUiController.exportNativeConfig(ServersUiController.processedServerId, countryCode, fileName)
PageController.showBusyIndicator(false)
if (result) {
@@ -202,9 +202,9 @@ PageType {
function revokeConfig(countryCode) {
PageController.showBusyIndicator(true)
let result = SubscriptionUiController.revokeNativeConfig(ServersUiController.getServerId(ServersUiController.processedServerIndex), countryCode)
let result = SubscriptionUiController.revokeNativeConfig(ServersUiController.processedServerId, countryCode)
if (result) {
SubscriptionUiController.getAccountInfo(ServersUiController.getServerId(ServersUiController.processedServerIndex), true)
SubscriptionUiController.getAccountInfo(ServersUiController.processedServerId, true)
}
PageController.showBusyIndicator(false)
@@ -76,10 +76,18 @@ PageType {
}
}
Connections {
target: ServersUiController
function onProcessedServerIdChanged() {
root.processedServer = proxyServersModel.get(0)
}
}
Connections {
target: ServersModel
function onProcessedServerChanged() {
function onModelReset() {
root.processedServer = proxyServersModel.get(0)
}
}
@@ -91,8 +99,8 @@ PageType {
sourceModel: ServersModel
filters: [
ValueFilter {
roleName: "isCurrentlyProcessed"
value: true
roleName: "serverId"
value: ServersUiController.processedServerId
}
]
@@ -131,7 +139,7 @@ PageType {
actionButtonImage: "qrc:/images/controls/edit-3.svg"
headerText: root.processedServer.name
headerText: root.processedServer != null ? root.processedServer.name : ""
actionButtonFunction: function() {
serverNameEditDrawer.openTriggered()
@@ -186,7 +194,7 @@ PageType {
textColor: AmneziaStyle.color.midnightBlack
clickedFunc: function() {
SubscriptionUiController.getRenewalLink(ServersUiController.getServerId(ServersUiController.processedServerIndex))
SubscriptionUiController.getRenewalLink(ServersUiController.processedServerId)
}
}
}
@@ -246,7 +254,7 @@ PageType {
text: qsTr("Renew subscription")
clickedFunc: function() {
SubscriptionUiController.getRenewalLink(ServersUiController.getServerId(ServersUiController.processedServerIndex))
SubscriptionUiController.getRenewalLink(ServersUiController.processedServerId)
}
}
@@ -258,8 +266,8 @@ PageType {
SwitcherType {
id: switcher
readonly property bool isVlessProtocol: SubscriptionUiController.isVlessProtocol(ServersUiController.getServerId(ServersUiController.processedServerIndex))
readonly property bool isProtocolSwitchBlocked: ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
readonly property bool isVlessProtocol: SubscriptionUiController.isVlessProtocol(ServersUiController.processedServerId)
readonly property bool isProtocolSwitchBlocked: ServersUiController.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
Layout.fillWidth: true
Layout.topMargin: 24
@@ -277,8 +285,8 @@ PageType {
PageController.showNotificationMessage(qsTr("Cannot change protocol during active connection"))
} else {
PageController.showBusyIndicator(true)
SubscriptionUiController.setCurrentProtocol(ServersUiController.getServerId(ServersUiController.processedServerIndex), switcher.isVlessProtocol ? "awg" : "vless")
SubscriptionUiController.updateServiceFromGateway(ServersUiController.getServerId(ServersUiController.processedServerIndex), "", "", true)
SubscriptionUiController.setCurrentProtocol(ServersUiController.processedServerId, switcher.isVlessProtocol ? "awg" : "vless")
SubscriptionUiController.updateServiceFromGateway(ServersUiController.processedServerId, "", "", true)
PageController.showBusyIndicator(false)
}
}
@@ -326,7 +334,7 @@ PageType {
PageController.goToPage(PageEnum.PageSettingsApiSubscriptionKey)
PageController.showBusyIndicator(true)
SubscriptionUiController.prepareVpnKeyExport(ServersUiController.getServerId(ServersUiController.processedServerIndex))
SubscriptionUiController.prepareVpnKeyExport(ServersUiController.processedServerId)
PageController.showBusyIndicator(false)
}
@@ -432,7 +440,7 @@ PageType {
PageController.showNotificationMessage(qsTr("Cannot reload API config during active connection"))
} else {
PageController.showBusyIndicator(true)
SubscriptionUiController.updateServiceFromGateway(ServersUiController.getServerId(ServersUiController.processedServerIndex), "", "", true)
SubscriptionUiController.updateServiceFromGateway(ServersUiController.processedServerId, "", "", true)
PageController.showBusyIndicator(false)
}
}
@@ -470,8 +478,8 @@ PageType {
PageController.showNotificationMessage(qsTr("Cannot unlink device during active connection"))
} else {
PageController.showBusyIndicator(true)
if (SubscriptionUiController.deactivateDevice(ServersUiController.getServerId(ServersUiController.processedServerIndex))) {
SubscriptionUiController.getAccountInfo(ServersUiController.getServerId(ServersUiController.processedServerIndex), true)
if (SubscriptionUiController.deactivateDevice(ServersUiController.processedServerId)) {
SubscriptionUiController.getAccountInfo(ServersUiController.processedServerId, true)
}
PageController.showBusyIndicator(false)
}
@@ -507,7 +515,7 @@ PageType {
PageController.showNotificationMessage(qsTr("Cannot remove server during active connection"))
} else {
PageController.showBusyIndicator(true)
SubscriptionUiController.removeServer(ServersUiController.getServerId(ServersUiController.processedServerIndex))
SubscriptionUiController.removeServer(ServersUiController.processedServerId)
PageController.showBusyIndicator(false)
}
}
@@ -526,6 +534,6 @@ PageType {
anchors.fill: parent
expandedHeight: parent.height * 0.35
serverNameText: root.processedServer.name
serverNameText: root.processedServer != null ? root.processedServer.name : ""
}
}
@@ -21,10 +21,18 @@ PageType {
property var processedServer
Connections {
target: ServersUiController
function onProcessedServerIdChanged() {
root.processedServer = proxyServersModel.get(0)
}
}
Connections {
target: ServersModel
function onProcessedServerChanged() {
function onModelReset() {
root.processedServer = proxyServersModel.get(0)
}
}
@@ -36,8 +44,8 @@ PageType {
sourceModel: ServersModel
filters: [
ValueFilter {
roleName: "isCurrentlyProcessed"
value: true
roleName: "serverId"
value: ServersUiController.processedServerId
}
]
@@ -48,7 +56,7 @@ PageType {
Component.onCompleted: {
PageController.showBusyIndicator(true)
SubscriptionUiController.prepareVpnKeyExport(ServersUiController.getServerId(ServersUiController.processedServerIndex))
SubscriptionUiController.prepareVpnKeyExport(ServersUiController.processedServerId)
PageController.showBusyIndicator(false)
}
@@ -119,7 +127,7 @@ PageType {
if (fileName !== "") {
PageController.showBusyIndicator(true)
let ok = SubscriptionUiController.exportVpnKey(ServersUiController.getServerId(ServersUiController.processedServerIndex), fileName)
let ok = SubscriptionUiController.exportVpnKey(ServersUiController.processedServerId, fileName)
PageController.showBusyIndicator(false)
if (ok) {
PageController.showNotificationMessage(qsTr("Config file saved"))
@@ -144,7 +152,7 @@ PageType {
clickedFunc: function() {
PageController.showBusyIndicator(true)
SubscriptionUiController.prepareVpnKeyExport(ServersUiController.getServerId(ServersUiController.processedServerIndex))
SubscriptionUiController.prepareVpnKeyExport(ServersUiController.processedServerId)
PageController.showBusyIndicator(false)
vpnKeyDrawer.openTriggered()
}
@@ -154,10 +154,10 @@ PageType {
text: qsTr("Start minimized")
descriptionText: qsTr("Launch application minimized (works with autostart option turned on)")
enabled: switcherAutoStart.checked
enabled: SettingsController.isAutoStartEnabled()
opacity: enabled ? 1.0 : 0.5
checked: SettingsController.startMinimized
checked: SettingsController.isAutoStartEnabled() && SettingsController.startMinimized
onToggled: function() {
if (checked !== SettingsController.startMinimized) {
SettingsController.toggleStartMinimized(checked)
+1 -1
View File
@@ -37,7 +37,7 @@ PageType {
anchors.right: parent.right
anchors.left: parent.left
property var isServerFromApi: ServersModel.isServerFromApi(ServersUiController.defaultServerIndex)
property var isServerFromApi: ServersUiController.isDefaultServerFromApi
enabled: !isServerFromApi
@@ -59,7 +59,7 @@ PageType {
Connections {
target: ServersUiController
function onProcessedServerIndexChanged() {
function onProcessedServerIdChanged() {
root.isServerWithWriteAccess = ServersUiController.isProcessedServerHasWriteAccess()
}
}
@@ -111,7 +111,7 @@ PageType {
readonly property var tColor: AmneziaStyle.color.paleGray
readonly property var clickedHandler: function() {
PageController.showBusyIndicator(true)
InstallController.scanServerForInstalledContainers(ServersUiController.getServerId(ServersUiController.processedServerIndex))
InstallController.scanServerForInstalledContainers(ServersUiController.processedServerId)
PageController.showBusyIndicator(false)
}
}
@@ -134,7 +134,7 @@ PageType {
PageController.showNotificationMessage(qsTr("Cannot reboot server during active connection"))
} else {
PageController.showBusyIndicator(true)
InstallController.rebootServer(ServersUiController.getServerId(ServersUiController.processedServerIndex))
InstallController.rebootServer(ServersUiController.processedServerId)
PageController.showBusyIndicator(false)
}
}
@@ -164,7 +164,7 @@ PageType {
PageController.showNotificationMessage(qsTr("Cannot remove server during active connection"))
} else {
PageController.showBusyIndicator(true)
InstallController.removeServer(ServersUiController.getServerId(ServersUiController.processedServerIndex))
InstallController.removeServer(ServersUiController.processedServerId)
PageController.showBusyIndicator(false)
}
}
@@ -194,7 +194,7 @@ PageType {
PageController.showNotificationMessage(qsTr("Cannot clear server from Amnezia software during active connection"))
} else {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeAllContainers(ServersUiController.getServerId(ServersUiController.processedServerIndex))
InstallController.removeAllContainers(ServersUiController.processedServerId)
}
}
var noButtonFunction = function() {
@@ -208,7 +208,7 @@ PageType {
QtObject {
id: reset
property bool isVisible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
property bool isVisible: ServersUiController.isServerFromApi(ServersUiController.processedServerId)
readonly property string title: qsTr("Reset API config")
readonly property string description: ""
readonly property var tColor: AmneziaStyle.color.vibrantRed
@@ -223,7 +223,7 @@ PageType {
PageController.showNotificationMessage(qsTr("Cannot reset API config during active connection"))
} else {
PageController.showBusyIndicator(true)
SubscriptionUiController.removeApiConfig(ServersUiController.getServerId(ServersUiController.processedServerIndex))
SubscriptionUiController.removeApiConfig(ServersUiController.processedServerId)
PageController.showBusyIndicator(false)
}
}
@@ -31,10 +31,18 @@ PageType {
}
}
Connections {
target: ServersUiController
function onProcessedServerIdChanged() {
root.processedServer = proxyServersModel.get(0)
}
}
Connections {
target: ServersModel
function onProcessedServerChanged() {
function onModelReset() {
root.processedServer = proxyServersModel.get(0)
}
}
@@ -46,8 +54,8 @@ PageType {
sourceModel: ServersModel
filters: [
ValueFilter {
roleName: "isCurrentlyProcessed"
value: true
roleName: "serverId"
value: ServersUiController.processedServerId
}
]
@@ -80,11 +88,14 @@ PageType {
actionButtonImage: "qrc:/images/controls/edit-3.svg"
headerText: root.processedServer.name
headerText: root.processedServer != null ? root.processedServer.name : ""
descriptionText: {
if (root.processedServer.isServerFromTelegramApi) {
if (root.processedServer == null) {
return ""
}
if (ServersUiController.isServerFromApi(ServersUiController.processedServerId)) {
return root.processedServer.serverDescription
} else if (root.processedServer.hasWriteAccess) {
} else if (ServersUiController.isProcessedServerHasWriteAccess()) {
return root.processedServer.credentialsLogin + " · " + root.processedServer.hostName
} else {
return root.processedServer.hostName
@@ -104,7 +115,7 @@ PageType {
anchors.fill: parent
expandedHeight: root.height * 0.35
serverNameText: root.processedServer.name
serverNameText: root.processedServer != null ? root.processedServer.name : ""
}
TabBar {
@@ -112,8 +123,8 @@ PageType {
Layout.fillWidth: true
currentIndex: (ServersModel.getProcessedServerData("isServerFromTelegramApi")
&& !ServersModel.getProcessedServerData("hasInstalledContainers")) ?
currentIndex: (ServersUiController.isServerFromApi(ServersUiController.processedServerId)
&& !ServersUiController.serverHasInstalledContainers(ServersUiController.processedServerId)) ?
root.pageSettingsServerData : root.pageSettingsServerProtocols
background: Rectangle {
@@ -76,7 +76,7 @@ PageType {
clickedFunction: function() {
if (isClientProtocolExists) {
InstallController.openClientSettings(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, protocolIndex)
InstallController.openClientSettings(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, protocolIndex)
PageController.goToPage(clientProtocolPage);
} else {
PageController.showNotificationMessage(qsTr("Click the \"connect\" button to create a connection configuration"))
@@ -104,7 +104,7 @@ PageType {
visible: delegateContent.isServerSettingsVisible
clickedFunction: function() {
InstallController.openServerSettings(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex, protocolIndex)
InstallController.openServerSettings(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, protocolIndex)
PageController.goToPage(serverProtocolPage);
}
@@ -140,14 +140,14 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ServersUiController.processedContainerIndex) {
if (ConnectionController.isConnected && ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId) === ServersUiController.processedContainerIndex) {
var message = qsTr("Unable to clear %1 profile while there is an active connection").arg(ContainersModel.getProcessedContainerName())
PageController.showNotificationMessage(message)
return
}
PageController.showBusyIndicator(true)
InstallController.clearCachedProfile(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
InstallController.clearCachedProfile(ServersUiController.processedServerId, ServersUiController.processedContainerIndex)
PageController.showBusyIndicator(false)
}
@@ -186,12 +186,12 @@ PageType {
var yesButtonFunction = function() {
if (ServersUiController.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
&& ServersModel.getDefaultServerData("defaultContainer") === ServersUiController.processedContainerIndex) {
&& ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId) === ServersUiController.processedContainerIndex) {
PageController.showNotificationMessage(qsTr("Cannot remove active container"))
} else
{
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), ServersUiController.processedContainerIndex)
InstallController.removeContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex)
}
}
var noButtonFunction = function() {
@@ -32,7 +32,7 @@ PageType {
Connections {
target: ServersUiController
function onProcessedServerIndexChanged() {
function onProcessedServerIdChanged() {
settingsContainersListView.updateContainersModelFilters()
}
}
@@ -28,7 +28,7 @@ PageType {
Connections {
target: ServersUiController
function onProcessedServerIndexChanged() {
function onProcessedServerIdChanged() {
settingsContainersListView.updateContainersModelFilters()
}
}
@@ -77,7 +77,7 @@ PageType {
servicesNameString += servicesName[i] + " · "
}
if (ServersModel.isServerFromApi(index)) {
if (ServersUiController.isServerFromApi(serverId)) {
return servicesNameString + serverDescription
} else {
return servicesNameString + hostName
@@ -86,11 +86,11 @@ PageType {
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
ServersUiController.setProcessedServerIndex(index)
ServersUiController.setProcessedServerId(serverId)
if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) {
if (ServersUiController.isServerFromApi(ServersUiController.processedServerId)) {
PageController.showBusyIndicator(true)
let result = SubscriptionUiController.getAccountInfo(ServersUiController.getServerId(ServersUiController.processedServerIndex), false)
let result = SubscriptionUiController.getAccountInfo(ServersUiController.processedServerId, false)
PageController.showBusyIndicator(false)
if (!result) {
return
@@ -20,7 +20,7 @@ import "../Components"
PageType {
id: root
property var isServerFromTelegramApi: ServersModel.getDefaultServerData("isServerFromTelegramApi")
property var isServerFromTelegramApi: ServersUiController.isServerFromApi(ServersUiController.defaultServerId)
property bool pageEnabled
@@ -121,7 +121,7 @@ PageType {
var _secretData = listView.itemAtIndex(vars.secretDataIndex).children[0].textField.text
InstallController.setProcessedServerCredentials(_hostname, _username, _secretData)
ServersUiController.setProcessedServerIndex(-1)
ServersUiController.setProcessedServerId("")
PageController.showBusyIndicator(true)
var isConnectionOpened = InstallController.checkSshConnection()
+1 -1
View File
@@ -165,7 +165,7 @@ PageType {
InstallController.install(listView.dockerContainer,
listView.containerDefaultPort,
listView.containerDefaultTransportProto,
ServersUiController.getServerId(ServersUiController.processedServerIndex))
ServersUiController.processedServerId)
} else {
PageController.goToPage(PageEnum.PageSetupWizardProtocols)
}
@@ -15,22 +15,21 @@ import "../Config"
PageType {
id: root
Component.onCompleted: PageController.disableTabBar(true)
Component.onCompleted: {
root.installingContainerIndex = ServersUiController.processedContainerIndex
PageController.disableTabBar(true)
}
Component.onDestruction: PageController.disableTabBar(false)
property bool isTimerRunning: true
property string progressBarText: qsTr("Usually it takes no more than 5 minutes")
property bool isCancelButtonVisible: false
property int installingContainerIndex: -1
Connections {
target: InstallController
function onInstallContainerFinished(finishedMessage, isServiceInstall) {
var containerIndex = ServersUiController.processedContainerIndex
if (!ConnectionController.isConnected && !ContainersModel.isServiceContainer(containerIndex)) {
ServersUiController.setDefaultContainer(ServersUiController.getServerId(ServersUiController.processedServerIndex), containerIndex)
}
PageController.closePage() // close installing page
PageController.closePage() // close protocol settings page
@@ -46,18 +45,13 @@ PageType {
}
function onInstallServerFinished(finishedMessage) {
if (!ConnectionController.isConnected) {
ServersUiController.setDefaultServerAtIndex(ServersModel.getServersCount() - 1);
ServersUiController.setProcessedServerIndex(ServersUiController.defaultServerIndex)
}
PageController.goToPageHome()
PageController.showNotificationMessage(finishedMessage)
}
function onServerAlreadyExists(serverIndex) {
PageController.goToStartPage()
ServersUiController.setProcessedServerIndex(serverIndex)
ServersUiController.setProcessedServerId(ServersUiController.getServerId(serverIndex))
PageController.goToPage(PageEnum.PageSettingsServerInfo, false)
PageController.showErrorMessage(qsTr("The server has already been added to the application"))
@@ -83,8 +77,8 @@ PageType {
sourceModel: ContainersModel
filters: [
ValueFilter {
roleName: "isCurrentlyProcessed"
value: true
roleName: "dockerContainer"
value: root.installingContainerIndex
}
]
}
@@ -243,7 +243,7 @@ PageType {
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.install(dockerContainer, port.textField.text, transportProtoSelector.currentIndex, ServersUiController.getServerId(ServersUiController.processedServerIndex))
InstallController.install(dockerContainer, port.textField.text, transportProtoSelector.currentIndex, ServersUiController.processedServerId)
}
}
+13 -11
View File
@@ -43,7 +43,7 @@ PageType {
var configFileName
var containerIndex = ServersUiController.processedContainerIndex
var serverId = ServersUiController.getServerId(ServersUiController.processedServerIndex)
var serverId = ServersUiController.processedServerId
switch (type) {
case PageShare.ConfigType.AmneziaConnection: {
@@ -249,7 +249,7 @@ PageType {
onClicked: {
accessTypeSelector.currentIndex = 1
PageController.showBusyIndicator(true)
ExportController.updateClientManagementModel(ServersUiController.getServerId(ServersUiController.processedServerIndex),
ExportController.updateClientManagementModel(ServersUiController.processedServerId,
ServersUiController.processedContainerIndex)
PageController.showBusyIndicator(false)
}
@@ -332,8 +332,10 @@ PageType {
}
Component.onCompleted: {
if (ServersModel.isDefaultServerHasWriteAccess() && ServersModel.getDefaultServerData("hasInstalledContainers")) {
serverSelectorListView.selectedIndex = proxyServersModel.mapFromSource(ServersUiController.defaultServerIndex)
if (ServersUiController.isServerHasWriteAccess(ServersUiController.defaultServerId)
&& ServersUiController.serverHasInstalledContainers(ServersUiController.defaultServerId)) {
serverSelectorListView.selectedIndex =
proxyServersModel.mapFromSource(ServersUiController.getServerIndexById(ServersUiController.defaultServerId))
} else {
serverSelectorListView.selectedIndex = 0
}
@@ -344,7 +346,7 @@ PageType {
function handler() {
serverSelector.text = selectedText
ServersUiController.setProcessedServerIndex(proxyServersModel.mapToSource(selectedIndex))
ServersUiController.setProcessedServerId(ServersUiController.getServerId(proxyServersModel.mapToSource(selectedIndex)))
}
}
}
@@ -394,7 +396,7 @@ PageType {
target: serverSelector
function onServerSelectorIndexChanged() {
var defaultContainer = proxyContainersModel.mapFromSource(ServersModel.getProcessedServerData("defaultContainer"))
var defaultContainer = proxyContainersModel.mapFromSource(ServersUiController.serverDefaultContainer(ServersUiController.processedServerId))
containerSelectorListView.selectedIndex = defaultContainer
containerSelectorListView.positionViewAtIndex(selectedIndex, ListView.Beginning)
containerSelectorListView.triggerCurrentItem()
@@ -417,7 +419,7 @@ PageType {
if (accessTypeSelector.currentIndex === 1) {
PageController.showBusyIndicator(true)
ExportController.updateClientManagementModel(ServersUiController.getServerId(ServersUiController.processedServerIndex),
ExportController.updateClientManagementModel(ServersUiController.processedServerId,
ServersUiController.processedContainerIndex)
PageController.showBusyIndicator(false)
}
@@ -793,7 +795,7 @@ PageType {
PageController.showBusyIndicator(true)
ExportController.renameClient(proxyClientManagementModel.mapToSource(index),
clientNameEditor.textField.text,
ServersUiController.getServerId(ServersUiController.processedServerIndex),
ServersUiController.processedServerId,
ServersUiController.processedContainerIndex)
PageController.showBusyIndicator(false)
Qt.callLater(function(){ clientsListView.freezeFilter = false })
@@ -829,14 +831,14 @@ PageType {
clientInfoDrawer.closeTriggered()
PageController.showBusyIndicator(true)
ExportController.revokeConfig(proxyClientManagementModel.mapToSource(index),
ServersUiController.getServerId(ServersUiController.processedServerIndex),
ServersUiController.processedServerId,
ServersUiController.processedContainerIndex)
}
var noButtonFunction = function() {
}
var isActiveConfigForCurrentClient = ServersModel.isDefaultServerCurrentlyProcessed()
&& ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()
var isActiveConfigForCurrentClient = ServersUiController.isDefaultServerCurrentlyProcessed()
&& ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId) === ServersUiController.processedContainerIndex
if ((ConnectionController.isConnectionInProgress || ConnectionController.isConnected)
&& isActiveConfigForCurrentClient) {
+4 -4
View File
@@ -118,14 +118,14 @@ PageType {
}
Component.onCompleted: {
serverSelectorListView.currentIndex = ServersModel.isDefaultServerHasWriteAccess() ?
proxyServersModel.mapFromSource(ServersUiController.defaultServerIndex) : 0
serverSelectorListView.currentIndex = ServersUiController.isServerHasWriteAccess(ServersUiController.defaultServerId) ?
proxyServersModel.mapFromSource(ServersUiController.getServerIndexById(ServersUiController.defaultServerId)) : 0
serverSelectorListView.triggerCurrentItem()
}
function handler() {
serverSelector.text = selectedText
ServersUiController.setProcessedServerIndex(proxyServersModel.mapToSource(selectedIndex))
ServersUiController.setProcessedServerId(ServersUiController.getServerId(proxyServersModel.mapToSource(selectedIndex)))
}
}
}
@@ -155,7 +155,7 @@ PageType {
ExportController.exportErrorOccurred(qsTr("Access error!"))
return
} else {
ExportController.generateFullAccessConfig(ServersUiController.getServerId(ServersUiController.processedServerIndex))
ExportController.generateFullAccessConfig(ServersUiController.processedServerId)
}
PageController.showBusyIndicator(false)
+8 -28
View File
@@ -144,7 +144,7 @@ PageType {
}
function onRemoveServerFinished(finishedMessage) {
if (!ServersModel.getServersCount()) {
if (!ServersUiController.getServersCount()) {
PageController.goToPageHome()
} else {
PageController.goToStartPage()
@@ -156,22 +156,11 @@ PageType {
function onNoInstalledContainers() {
PageController.setTriggeredByConnectButton(true)
ServersUiController.setProcessedServerIndex(ServersUiController.defaultServerIndex)
ServersUiController.setProcessedServerId(ServersUiController.defaultServerId)
PageController.goToPage(PageEnum.PageSetupWizardEasy)
}
}
Connections {
objectName: "connectionControllerConnections"
target: ConnectionController
function onReconnectWithUpdatedContainer(message) {
PageController.showNotificationMessage(message)
PageController.closePage()
}
}
Connections {
objectName: "importControllerConnections"
@@ -227,7 +216,7 @@ PageType {
}
function onApiServerRemoved(message) {
if (!ServersModel.getServersCount()) {
if (!ServersUiController.getServersCount()) {
PageController.goToPageHome()
} else {
PageController.goToStartPage()
@@ -237,15 +226,6 @@ PageType {
}
function onInstallServerFromApiFinished(message, preferredDefaultIndex) {
if (!ConnectionController.isConnected) {
if (preferredDefaultIndex !== undefined && preferredDefaultIndex >= 0) {
ServersUiController.setDefaultServerAtIndex(preferredDefaultIndex)
} else {
ServersUiController.setDefaultServerAtIndex(ServersModel.getServersCount() - 1);
}
ServersUiController.setProcessedServerIndex(ServersUiController.defaultServerIndex)
}
PageController.goToPageHome()
PageController.showNotificationMessage(message)
}
@@ -286,7 +266,7 @@ PageType {
} else {
tabBar.visible = true
pagePath = PageController.getPagePath(PageEnum.PageHome)
ServersUiController.setProcessedServerIndex(ServersUiController.defaultServerIndex)
ServersUiController.setProcessedServerId(ServersUiController.defaultServerId)
}
tabBarStackView.push(pagePath, { "objectName" : pagePath })
@@ -360,7 +340,7 @@ PageType {
image: "qrc:/images/controls/home.svg"
clickedFunc: function () {
tabBarStackView.goToTabBarPage(PageEnum.PageHome)
ServersUiController.setProcessedServerIndex(ServersUiController.defaultServerIndex)
ServersUiController.setProcessedServerId(ServersUiController.defaultServerId)
tabBar.currentIndex = 0
}
}
@@ -374,15 +354,15 @@ PageType {
function onModelReset() {
if (!SettingsController.isOnTv()) {
var hasServerWithWriteAccess = ServersModel.hasServerWithWriteAccess()
var hasServerWithWriteAccess = ServersUiController.hasServerWithWriteAccess()
shareTabButton.visible = hasServerWithWriteAccess
shareTabButton.width = hasServerWithWriteAccess ? undefined : 0
}
}
}
visible: !SettingsController.isOnTv() && ServersModel.hasServerWithWriteAccess()
width: !SettingsController.isOnTv() && ServersModel.hasServerWithWriteAccess() ? undefined : 0
visible: !SettingsController.isOnTv() && ServersUiController.hasServerWithWriteAccess()
width: !SettingsController.isOnTv() && ServersUiController.hasServerWithWriteAccess() ? undefined : 0
isSelected: tabBar.currentIndex === 1
image: "qrc:/images/controls/share-2.svg"
+1 -1
View File
@@ -124,7 +124,7 @@ void Autostart::setAutostart(bool autostart) {
if (file.open(QIODevice::ReadWrite)) {
QTextStream stream(&file);
stream << "[Desktop Entry]" << Qt::endl;
stream << "Exec=AmneziaVPN" << Qt::endl;
stream << "Exec=" << appPath() << Qt::endl;
stream << "Type=Application" << Qt::endl;
stream << "Name=AmneziaVPN" << Qt::endl;
stream << "Comment=Client of your self-hosted VPN" << Qt::endl;
+10 -7
View File
@@ -7,7 +7,7 @@ if(APPLE)
set(CMAKE_OSX_DEPLOYMENT_TARGET "14.0" CACHE STRING "" FORCE)
set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "" FORCE)
elseif(MACOS_NE)
set(CONAN_INSTALL_ARGS "--build=missing;-o=&:macos_ne=True" CACHE STRING "" FORCE)
set(_CONAN_INSTALL_ARGS "-o=&:macos_ne=True")
set(CMAKE_OSX_DEPLOYMENT_TARGET "13.0" CACHE STRING "" FORCE)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)
else()
@@ -17,15 +17,18 @@ if(APPLE)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Android")
set(CONAN_INSTALL_ARGS
"--build=missing"
"-c=tools.android:cmake_legacy_toolchain=false"
"-c=tools.build:sharedlinkflags=['-Wl,-z,max-page-size=16384']"
"-c=tools.build:exelinkflags=['-Wl,-z,max-page-size=16384']"
CACHE STRING "" FORCE)
set(_CONAN_INSTALL_ARGS
"-c=tools.android:cmake_legacy_toolchain=false"
"-c=tools.build:sharedlinkflags=['-Wl,-z,max-page-size=16384']"
"-c=tools.build:exelinkflags=['-Wl,-z,max-page-size=16384']"
"-o=openssl/*:shared=True")
set(CMAKE_ANDROID_STL_TYPE "c++_shared" CACHE STRING "")
endif()
if (WIN32 OR APPLE)
set(CMAKE_INSTALL_BINDIR ".")
endif()
list(PREPEND _CONAN_INSTALL_ARGS "--build=missing")
list(JOIN _CONAN_INSTALL_ARGS ";" _CONAN_INSTALL_ARGS_JOINED)
set(CONAN_INSTALL_ARGS ${_CONAN_INSTALL_ARGS_JOINED} CACHE STRING "" FORCE)
+1 -1
View File
@@ -43,5 +43,5 @@ class AmneziaVPN(ConanFile):
# expicitly use libssh@amnezia to prevent it from being downloaded from conan-center
self.requires("libssh/0.11.3@amnezia")
self.requires("openssl/3.6.1")
self.requires("openssl/3.6.2")
self.requires("zlib/1.3.2")
+682
View File
@@ -0,0 +1,682 @@
from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.apple import fix_apple_shared_install_name, is_apple_os, XCRun
from conan.tools.build import build_jobs
from conan.tools.files import chdir, copy, get, replace_in_file, rm, rmdir, save
from conan.tools.gnu import AutotoolsToolchain
from conan.tools.layout import basic_layout
from conan.tools.microsoft import is_msvc, msvc_runtime_flag, unix_path
from conan.tools.scm import Version
import fnmatch
import os
import textwrap
required_conan_version = ">=1.57.0"
class OpenSSLConan(ConanFile):
name = "openssl"
version = "3.6.2"
url = "https://github.com/conan-io/conan-center-index"
homepage = "https://github.com/openssl/openssl"
license = "Apache-2.0"
topics = ("ssl", "tls", "encryption", "security")
description = "A toolkit for the Transport Layer Security (TLS) and Secure Sockets Layer (SSL) protocols"
package_type = "library"
settings = "os", "arch", "compiler", "build_type"
options = {
"shared": [True, False],
"fPIC": [True, False],
"enable_weak_ssl_ciphers": [True, False],
"386": [True, False],
"capieng_dialog": [True, False],
"enable_capieng": [True, False],
"enable_trace": [True, False],
"no_aria": [True, False],
"no_apps": [True, False],
"no_autoload_config": [True, False],
"no_asm": [True, False],
"no_async": [True, False],
"no_blake2": [True, False],
"no_bf": [True, False],
"no_camellia": [True, False],
"no_chacha": [True, False],
"no_cms": [True, False],
"no_comp": [True, False],
"no_ct": [True, False],
"no_cast": [True, False],
"no_deprecated": [True, False],
"no_des": [True, False],
"no_dgram": [True, False],
"no_dh": [True, False],
"no_dsa": [True, False],
"no_dso": [True, False],
"no_ec": [True, False],
"no_ecdh": [True, False],
"no_ecdsa": [True, False],
"no_engine": [True, False],
"no_filenames": [True, False],
"no_fips": [True, False],
"no_gost": [True, False],
"no_idea": [True, False],
"no_legacy": [True, False],
"no_md2": [True, False],
"no_md4": [True, False],
"no_mdc2": [True, False],
"no_module": [True, False],
"no_ocsp": [True, False],
"no_pinshared": [True, False],
"no_rc2": [True, False],
"no_rc4": [True, False],
"no_rc5": [True, False],
"no_rfc3779": [True, False],
"no_rmd160": [True, False],
"no_sm2": [True, False],
"no_sm3": [True, False],
"no_sm4": [True, False],
"no_srp": [True, False],
"no_srtp": [True, False],
"no_sse2": [True, False],
"no_ssl": [True, False],
"no_stdio": [True, False],
"no_seed": [True, False],
"no_sock": [True, False],
"no_ssl3": [True, False],
"no_threads": [True, False],
"no_tls1": [True, False],
"no_ts": [True, False],
"no_whirlpool": [True, False],
"no_zlib": [True, False],
"openssldir": [None, "ANY"],
"tls_security_level": [None, 0, 1, 2, 3, 4, 5],
}
default_options = {key: False for key in options.keys()}
default_options["fPIC"] = True
default_options["no_md2"] = True
default_options["openssldir"] = None
default_options["tls_security_level"] = None
@property
def _is_clang_cl(self):
return self.settings.os == "Windows" and self.settings.compiler == "clang" and \
self.settings.compiler.get_safe("runtime")
@property
def _is_mingw(self):
return self.settings.os == "Windows" and self.settings.compiler == "gcc"
@property
def _use_nmake(self):
return self._is_clang_cl or is_msvc(self)
def config_options(self):
if self.settings.os != "Windows":
self.options.rm_safe("capieng_dialog")
self.options.rm_safe("enable_capieng")
else:
self.options.rm_safe("fPIC")
def configure(self):
if self.options.shared:
self.options.rm_safe("fPIC")
self.settings.rm_safe("compiler.libcxx")
self.settings.rm_safe("compiler.cppstd")
def layout(self):
basic_layout(self, src_folder="src")
def requirements(self):
if not self.options.no_zlib:
self.requires("zlib/[>=1.2.11 <2]")
def validate(self):
if self.settings.os == "iOS" and self.options.shared:
raise ConanInvalidConfiguration("OpenSSL 3 does not support building shared libraries for iOS")
def build_requirements(self):
if self.settings_build.os == "Windows":
if self.conf.get("user.openssl:windows_use_jom", False):
self.tool_requires("jom/[*]")
if not self.options.no_asm and self.settings.arch in ["x86", "x86_64"]:
self.tool_requires("nasm/2.16.01")
if self._use_nmake:
self.tool_requires("strawberryperl/5.32.1.1")
else:
self.win_bash = True
if not self.conf.get("tools.microsoft.bash:path", check_type=str):
self.tool_requires("msys2/cci.latest")
def source(self):
get(self, url="https://github.com/openssl/openssl/releases/download/openssl-3.6.2/openssl-3.6.2.tar.gz",
sha256="aaf51a1fe064384f811daeaeb4ec4dce7340ec8bd893027eee676af31e83a04f", strip_root=True)
@property
def _target(self):
target = f"conan-{self.settings.build_type}-{self.settings.os}-{self.settings.arch}-{self.settings.compiler}-{self.settings.compiler.version}"
if self._use_nmake:
target = f"VC-{target}" # VC- prefix is important as it's checked by Configure
if self._is_mingw:
target = f"mingw-{target}"
return target
@property
def _perlasm_scheme(self):
# right now, we need to tweak this for iOS & Android only, as they inherit from generic targets
if self.settings.os in ("iOS", "watchOS", "tvOS"):
return {
"armv7": "ios32",
"armv7s": "ios32",
"armv8": "ios64",
"armv8_32": "ios64",
"armv8.3": "ios64",
"armv7k": "ios32",
}.get(str(self.settings.arch), None)
elif self.settings.os == "Android":
return {
"armv7": "void",
"armv8": "linux64",
"mips": "o32",
"mips64": "64",
"x86": "android",
"x86_64": "elf",
}.get(str(self.settings.arch), None)
return None
@property
def _asm_target(self):
if self.settings.os in ("Android", "iOS", "watchOS", "tvOS"):
return {
"x86": "x86_asm" if self.settings.os == "Android" else None,
"x86_64": "x86_64_asm" if self.settings.os == "Android" else None,
"armv5el": "armv4_asm",
"armv5hf": "armv4_asm",
"armv6": "armv4_asm",
"armv7": "armv4_asm",
"armv7hf": "armv4_asm",
"armv7s": "armv4_asm",
"armv7k": "armv4_asm",
"armv8": "aarch64_asm",
"armv8_32": "aarch64_asm",
"armv8.3": "aarch64_asm",
"mips": "mips32_asm",
"mips64": "mips64_asm",
"sparc": "sparcv8_asm",
"sparcv9": "sparcv9_asm",
"ia64": "ia64_asm",
"ppc32be": "ppc32_asm",
"ppc32": "ppc32_asm",
"ppc64le": "ppc64_asm",
"ppc64": "ppc64_asm",
"s390": "s390x_asm",
"s390x": "s390x_asm"
}.get(str(self.settings.os), None)
@property
def _targets(self):
is_cygwin = self.settings.get_safe("os.subsystem") == "cygwin"
return {
"Linux-x86-clang": "linux-x86-clang",
"Linux-x86_64-clang": "linux-x86_64-clang",
"Linux-x86-*": "linux-x86",
"Linux-x86_64-*": "linux-x86_64",
"Linux-armv4-*": "linux-armv4",
"Linux-armv4i-*": "linux-armv4",
"Linux-armv5el-*": "linux-armv4",
"Linux-armv5hf-*": "linux-armv4",
"Linux-armv6-*": "linux-armv4",
"Linux-armv7-*": "linux-armv4",
"Linux-armv7hf-*": "linux-armv4",
"Linux-armv7s-*": "linux-armv4",
"Linux-armv7k-*": "linux-armv4",
"Linux-armv8-*": "linux-aarch64",
"Linux-armv8.3-*": "linux-aarch64",
"Linux-armv8-32-*": "linux-arm64ilp32",
"Linux-mips-*": "linux-mips32",
"Linux-mips64-*": "linux-mips64",
"Linux-ppc32-*": "linux-ppc32",
"Linux-ppc32le-*": "linux-pcc32",
"Linux-ppc32be-*": "linux-ppc32",
"Linux-ppc64-*": "linux-ppc64",
"Linux-ppc64le-*": "linux-ppc64le",
"Linux-pcc64be-*": "linux-pcc64",
"Linux-s390x-*": "linux64-s390x",
"Linux-e2k-*": "linux-generic64",
"Linux-sparc-*": "linux-sparcv8",
"Linux-sparcv9-*": "linux64-sparcv9",
"Linux-*-*": "linux-generic32",
"Macos-x86-*": "darwin-i386-cc",
"Macos-x86_64-*": "darwin64-x86_64-cc",
"Macos-ppc32-*": "darwin-ppc-cc",
"Macos-ppc32be-*": "darwin-ppc-cc",
"Macos-ppc64-*": "darwin64-ppc-cc",
"Macos-ppc64be-*": "darwin64-ppc-cc",
"Macos-armv8-*": "darwin64-arm64-cc",
"Macos-*-*": "darwin-common",
"iOS-x86_64-*": "darwin64-x86_64-cc",
"iOS-*-*": "iphoneos-cross",
"watchOS-*-*": "iphoneos-cross",
"tvOS-*-*": "iphoneos-cross",
# Android targets are very broken, see https://github.com/openssl/openssl/issues/7398
"Android-armv7-*": "linux-generic32",
"Android-armv7hf-*": "linux-generic32",
"Android-armv8-*": "linux-generic64",
"Android-x86-*": "linux-x86-clang",
"Android-x86_64-*": "linux-x86_64-clang",
"Android-mips-*": "linux-generic32",
"Android-mips64-*": "linux-generic64",
"Android-*-*": "linux-generic32",
"Windows-x86-gcc": "Cygwin-x86" if is_cygwin else "mingw",
"Windows-x86_64-gcc": "Cygwin-x86_64" if is_cygwin else "mingw64",
"Windows-*-gcc": "Cygwin-common" if is_cygwin else "mingw-common",
"Windows-ia64-Visual Studio": "VC-WIN64I", # Itanium
"Windows-x86-Visual Studio": "VC-WIN32",
"Windows-x86_64-Visual Studio": "VC-WIN64A",
"Windows-armv7-Visual Studio": "VC-WIN32-ARM",
"Windows-armv8-Visual Studio": "VC-WIN64-CLANGASM-ARM",
"Windows-*-Visual Studio": "VC-noCE-common",
"Windows-ia64-clang": "VC-WIN64I", # Itanium
"Windows-x86-clang": "VC-WIN32",
"Windows-x86_64-clang": "VC-WIN64A",
"Windows-armv7-clang": "VC-WIN32-ARM",
"Windows-armv8-clang": "VC-WIN64-ARM",
"Windows-*-clang": "VC-noCE-common",
"WindowsStore-x86-*": "VC-WIN32-UWP",
"WindowsStore-x86_64-*": "VC-WIN64A-UWP",
"WindowsStore-armv7-*": "VC-WIN32-ARM-UWP",
"WindowsStore-armv8-*": "VC-WIN64-ARM-UWP",
"WindowsStore-*-*": "VC-WIN32-ONECORE",
"WindowsCE-*-*": "VC-CE",
"SunOS-x86-gcc": "solaris-x86-gcc",
"SunOS-x86_64-gcc": "solaris64-x86_64-gcc",
"SunOS-sparc-gcc": "solaris-sparcv8-gcc",
"SunOS-sparcv9-gcc": "solaris64-sparcv9-gcc",
"SunOS-x86-suncc": "solaris-x86-cc",
"SunOS-x86_64-suncc": "solaris64-x86_64-cc",
"SunOS-sparc-suncc": "solaris-sparcv8-cc",
"SunOS-sparcv9-suncc": "solaris64-sparcv9-cc",
"SunOS-*-*": "solaris-common",
"*BSD-x86-*": "BSD-x86",
"*BSD-x86_64-*": "BSD-x86_64",
"*BSD-ia64-*": "BSD-ia64",
"*BSD-sparc-*": "BSD-sparcv8",
"*BSD-sparcv9-*": "BSD-sparcv9",
"*BSD-armv8-*": "BSD-generic64",
"*BSD-mips64-*": "BSD-generic64",
"*BSD-ppc64-*": "BSD-generic64",
"*BSD-ppc64le-*": "BSD-generic64",
"*BSD-ppc64be-*": "BSD-generic64",
"AIX-ppc32-gcc": "aix-gcc",
"AIX-ppc64-gcc": "aix64-gcc",
"AIX-pcc32-*": "aix-cc",
"AIX-ppc64-*": "aix64-cc",
"AIX-*-*": "aix-common",
"*BSD-*-*": "BSD-generic32",
"Emscripten-*-*": "cc",
"Neutrino-*-*": "BASE_unix",
}
@property
def _ancestor_target(self):
if "CONAN_OPENSSL_CONFIGURATION" in os.environ:
return os.environ["CONAN_OPENSSL_CONFIGURATION"]
compiler = "Visual Studio" if self.settings.compiler == "msvc" else self.settings.compiler
query = f"{self.settings.os}-{self.settings.arch}-{compiler}"
ancestor = next((self._targets[i] for i in self._targets if fnmatch.fnmatch(query, i)), None)
if not ancestor:
raise ConanInvalidConfiguration(
f"Unsupported configuration ({self.settings.os}/{self.settings.arch}/{self.settings.compiler}).\n"
f"Please open an issue at {self.url}.\n"
f"Alternatively, set the CONAN_OPENSSL_CONFIGURATION environment variable into your conan profile."
)
return ancestor
def _get_default_openssl_dir(self):
if self.settings.os == "Linux":
return "/etc/ssl"
return os.path.join(self.package_folder, "res")
def _adjust_path(self, path):
if self._use_nmake:
return path.replace("\\", "/")
return unix_path(self, path)
@property
def _configure_args(self):
openssldir = self.options.openssldir or self._get_default_openssl_dir()
openssldir = unix_path(self, openssldir) if self.win_bash else openssldir
args = [
f'"{self._target}"',
"shared" if self.options.shared else "no-shared",
"--debug" if self.settings.build_type == "Debug" else "--release",
"--prefix=/",
"--libdir=lib",
f"--openssldir=\"{openssldir}\"",
"no-threads" if self.options.no_threads else "threads",
f"PERL={self._perl}",
"no-unit-test",
"no-tests",
]
if self.settings.os == "Android":
args.append(f" -D__ANDROID_API__={str(self.settings.os.api_level)}") # see NOTES.ANDROID
if self.settings.os == "Windows":
if self.options.enable_capieng:
args.append("enable-capieng")
if self.options.capieng_dialog:
args.append("-DOPENSSL_CAPIENG_DIALOG=1")
else:
args.append("-fPIC" if self.options.get_safe("fPIC", True) else "no-pic")
args.append("no-fips" if self.options.get_safe("no_fips", True) else "enable-fips")
args.append("no-md2" if self.options.get_safe("no_md2", True) else "enable-md2")
if str(self.options.tls_security_level) != "None":
args.append(f"-DOPENSSL_TLS_SECURITY_LEVEL={self.options.tls_security_level}")
if self.options.get_safe("enable_trace"):
args.append("enable-trace")
if self.settings.os == "Neutrino":
args.append("no-asm -lsocket -latomic")
if not self.options.no_zlib:
zlib_cpp_info = self.dependencies["zlib"].cpp_info.aggregated_components()
include_path = self._adjust_path(zlib_cpp_info.includedirs[0])
is_shared_zlib = self.dependencies["zlib"].options.shared
# the --with-zlib-lib flag takes a different value depending on platform and if ZLIB is shared
# From https://github.com/openssl/openssl/blob/openssl-3.4.1/INSTALL.md#with-zlib-lib
# On Unix: the directory where the zlib library is (for -L flag)
# On Windows with static zlib: the path to the static library to link (assumed)
# On Windows with shared zlib: the leaf name of the dll (its loaded with LoadLibrary)
if self._use_nmake:
# notes: consider where this should be "if on windows"
# zlib1 is assumed to be the name of the zlib1.dll for all windows configurations
lib_path = self._adjust_path(os.path.join(zlib_cpp_info.libdirs[0], f"{zlib_cpp_info.libs[0]}.lib"))
zlib_lib_flag = "zlib1" if is_shared_zlib else lib_path
else:
# Just path, GNU like compilers will find the right file
zlib_lib_flag = self._adjust_path(zlib_cpp_info.libdirs[0])
zlib_configure_arg = "zlib-dynamic" if is_shared_zlib else "zlib"
args.append(zlib_configure_arg)
args.extend([
f'--with-zlib-include="{include_path}"',
f'--with-zlib-lib="{zlib_lib_flag}"',
])
for option_name in self.default_options.keys():
if self.options.get_safe(option_name, False) and option_name not in ("shared", "fPIC", "openssldir", "tls_security_level", "capieng_dialog", "enable_capieng", "zlib", "no_fips", "no_md2"):
self.output.info(f"Activated option: {option_name}")
args.append(option_name.replace("_", "-"))
return args
def generate(self):
tc = AutotoolsToolchain(self)
env = tc.environment()
env.define_path("PERL", self._perl)
if self.settings.compiler == "apple-clang":
xcrun = XCRun(self)
env.define_path("CROSS_SDK", os.path.basename(xcrun.sdk_path))
env.define_path("CROSS_TOP", os.path.dirname(os.path.dirname(xcrun.sdk_path)))
if is_apple_os(self) and self.options.shared:
# Inject -headerpad_max_install_names for shared library, otherwise fix_apple_shared_install_name() may fail.
# See https://github.com/conan-io/conan-center-index/issues/27424
tc.extra_ldflags.append("-headerpad_max_install_names")
self._create_targets(tc.cflags, tc.cxxflags, tc.defines, tc.ldflags)
tc.generate(env)
def _create_targets(self, cflags, cxxflags, defines, ldflags):
config_template = textwrap.dedent("""\
{targets} = (
"{target}" => {{
inherit_from => {ancestor},
cflags => add("{cflags}"),
cxxflags => add("{cxxflags}"),
{defines}
lflags => add("{lflags}"),
{shared_target}
{shared_cflag}
{shared_extension}
{perlasm_scheme}
}},
);
""")
perlasm_scheme = ""
if self._perlasm_scheme:
perlasm_scheme = f'perlasm_scheme => "{self._perlasm_scheme}",'
defines = '", "'.join(defines)
defines = 'defines => add("%s"),' % defines if defines else ""
targets = "my %targets"
if self._asm_target:
ancestor = f'[ "{self._ancestor_target}", asm("{self._asm_target}") ]'
else:
ancestor = f'[ "{self._ancestor_target}" ]'
shared_cflag = ""
shared_extension = ""
shared_target = ""
if self.settings.os == "Neutrino":
if self.options.shared:
shared_extension = r'shared_extension => ".so.\$(SHLIB_VERSION_NUMBER)",'
shared_target = 'shared_target => "gnu-shared",'
if self.options.get_safe("fPIC", True):
shared_cflag = 'shared_cflag => "-fPIC",'
if self.settings.os == "Android":
shared_extension = r'shared_extension => "_3.so",'
if self.settings.os in ["iOS", "tvOS", "watchOS"] and self.conf.get("tools.apple:enable_bitcode", check_type=bool):
cflags.append("-fembed-bitcode")
cxxflags.append("-fembed-bitcode")
config = config_template.format(
targets=targets,
target=self._target,
ancestor=ancestor,
cflags=" ".join(cflags),
cxxflags=" ".join(cxxflags),
defines=defines,
perlasm_scheme=perlasm_scheme,
shared_target=shared_target,
shared_extension=shared_extension,
shared_cflag=shared_cflag,
lflags=" ".join(ldflags)
)
self.output.info(f"using target: {self._target} -> {self._ancestor_target}")
self.output.info(config)
save(self, os.path.join(self.source_folder, "Configurations", "20-conan.conf"), config)
def _run_make(self, targets=None, parallel=True, install=False):
command = [self._make_program]
if install:
command.append(f"DESTDIR={self._adjust_path(self.package_folder)}")
if targets:
command.extend(targets)
if self._make_program in ["make", "jom"]:
command.append(f"-j{build_jobs(self)}" if parallel else "-j1")
self.run(" ".join(command), env="conanbuild")
@property
def _perl(self):
if self._use_nmake:
return self.dependencies.build["strawberryperl"].conf_info.get("user.strawberryperl:perl", check_type=str)
return "perl"
def _make(self):
with chdir(self, self.source_folder):
args = " ".join(self._configure_args)
if self._use_nmake:
self._replace_runtime_in_file(os.path.join("Configurations", "10-main.conf"))
self.run(f"{self._perl} ./Configure {args}", env="conanbuild")
if self._use_nmake:
# When `--prefix=/`, the scripts derive `\` without escaping, which
# causes issues on Windows
replace_in_file(self, "Makefile", "INSTALLTOP_dir=\\", "INSTALLTOP_dir=\\\\")
if Version(self.version) >= "3.3.0":
# replace backslashes in paths with forward slashes
mkinstallvars_pl = os.path.join(self.source_folder, "util", "mkinstallvars.pl")
if Version(self.version) >= "3.3.2":
replace_in_file(self, mkinstallvars_pl, "push @{$values{$k}}, $v;", """$v =~ s|\\\\|/|g; push @{$values{$k}}, $v;""")
replace_in_file(self, mkinstallvars_pl, "$values{$k} = $v;", """$v->[0] =~ s|\\\\|/|g; $values{$k} = $v;""")
else:
replace_in_file(self, mkinstallvars_pl, "$ENV{$k} = $v;", """$v =~ s|\\\\|/|g; $ENV{$k} = $v;""")
self._run_make()
def _make_install(self):
with chdir(self, self.source_folder):
self._run_make(targets=["install_sw"], parallel=False, install=True)
def build(self):
self._make()
configdata_pm = self._adjust_path(os.path.join(self.source_folder, "configdata.pm"))
self.run(f"{self._perl} {configdata_pm} --dump")
@property
def _make_program(self):
use_jom = self._use_nmake and self.conf.get("user.openssl:windows_use_jom", False)
if self._use_nmake:
return "jom" if use_jom else "nmake"
else:
return "make"
def _replace_runtime_in_file(self, filename):
runtime = msvc_runtime_flag(self)
for e in ["MDd", "MD", "MT"]:
replace_in_file(self, filename, f"/{e} ", f"/{runtime} ")
replace_in_file(self, filename, f"/{e}\"", f"/{runtime}\"")
def package(self):
copy(self, "*LICENSE*", src=self.source_folder, dst=os.path.join(self.package_folder, "licenses"))
self._make_install()
if is_apple_os(self):
fix_apple_shared_install_name(self)
rm(self, "*.pdb", self.package_folder, "lib")
if self.options.shared:
libdir = os.path.join(self.package_folder, "lib")
for file in os.listdir(libdir):
if self._is_mingw and file.endswith(".dll.a"):
continue
if file.endswith(".a"):
os.unlink(os.path.join(libdir, file))
if not self.options.no_fips:
provdir = os.path.join(self.source_folder, "providers")
modules_dir = os.path.join(self.package_folder, "lib", "ossl-modules")
if self.settings.os == "Macos":
copy(self, "fips.dylib", src=provdir, dst=modules_dir)
elif self.settings.os == "Windows":
copy(self, "fips.dll", src=provdir, dst=modules_dir)
else:
copy(self, "fips.so", src=provdir, dst=modules_dir)
rmdir(self, os.path.join(self.package_folder, "lib", "pkgconfig"))
rmdir(self, os.path.join(self.package_folder, "lib", "cmake"))
self._create_cmake_module_variables(
os.path.join(self.package_folder, self._module_file_rel_path)
)
def _create_cmake_module_variables(self, module_file):
content = textwrap.dedent("""\
set(OPENSSL_FOUND TRUE)
if(DEFINED OpenSSL_INCLUDE_DIR)
set(OPENSSL_INCLUDE_DIR ${OpenSSL_INCLUDE_DIR})
endif()
if(DEFINED OpenSSL_Crypto_LIBS)
set(OPENSSL_CRYPTO_LIBRARY ${OpenSSL_Crypto_LIBS})
set(OPENSSL_CRYPTO_LIBRARIES ${OpenSSL_Crypto_LIBS}
${OpenSSL_Crypto_DEPENDENCIES}
${OpenSSL_Crypto_FRAMEWORKS}
${OpenSSL_Crypto_SYSTEM_LIBS})
elseif(DEFINED openssl_OpenSSL_Crypto_LIBS_%(config)s)
set(OPENSSL_CRYPTO_LIBRARY ${openssl_OpenSSL_Crypto_LIBS_%(config)s})
set(OPENSSL_CRYPTO_LIBRARIES ${openssl_OpenSSL_Crypto_LIBS_%(config)s}
${openssl_OpenSSL_Crypto_DEPENDENCIES_%(config)s}
${openssl_OpenSSL_Crypto_FRAMEWORKS_%(config)s}
${openssl_OpenSSL_Crypto_SYSTEM_LIBS_%(config)s})
endif()
if(DEFINED OpenSSL_SSL_LIBS)
set(OPENSSL_SSL_LIBRARY ${OpenSSL_SSL_LIBS})
set(OPENSSL_SSL_LIBRARIES ${OpenSSL_SSL_LIBS}
${OpenSSL_SSL_DEPENDENCIES}
${OpenSSL_SSL_FRAMEWORKS}
${OpenSSL_SSL_SYSTEM_LIBS})
elseif(DEFINED openssl_OpenSSL_SSL_LIBS_%(config)s)
set(OPENSSL_SSL_LIBRARY ${openssl_OpenSSL_SSL_LIBS_%(config)s})
set(OPENSSL_SSL_LIBRARIES ${openssl_OpenSSL_SSL_LIBS_%(config)s}
${openssl_OpenSSL_SSL_DEPENDENCIES_%(config)s}
${openssl_OpenSSL_SSL_FRAMEWORKS_%(config)s}
${openssl_OpenSSL_SSL_SYSTEM_LIBS_%(config)s})
endif()
if(DEFINED OpenSSL_LIBRARIES)
set(OPENSSL_LIBRARIES ${OpenSSL_LIBRARIES})
endif()
if(DEFINED OpenSSL_VERSION)
set(OPENSSL_VERSION ${OpenSSL_VERSION})
endif()
"""% {"config":str(self.settings.build_type).upper()})
save(self, module_file, content)
@property
def _module_subfolder(self):
return os.path.join("lib", "cmake")
@property
def _module_file_rel_path(self):
return os.path.join(self._module_subfolder,
f"conan-official-{self.name}-variables.cmake")
def package_info(self):
self.cpp_info.set_property("cmake_file_name", "OpenSSL")
self.cpp_info.set_property("cmake_find_mode", "both")
self.cpp_info.set_property("pkg_config_name", "openssl")
self.cpp_info.set_property("cmake_build_modules", [self._module_file_rel_path])
self.cpp_info.components["ssl"].builddirs.append(self._module_subfolder)
self.cpp_info.components["ssl"].set_property("cmake_build_modules", [self._module_file_rel_path])
self.cpp_info.components["crypto"].builddirs.append(self._module_subfolder)
self.cpp_info.components["crypto"].set_property("cmake_build_modules", [self._module_file_rel_path])
if self._use_nmake:
self.cpp_info.components["ssl"].libs = ["libssl"]
self.cpp_info.components["crypto"].libs = ["libcrypto"]
else:
self.cpp_info.components["ssl"].libs = ["ssl"]
self.cpp_info.components["crypto"].libs = ["crypto"]
self.cpp_info.components["ssl"].requires = ["crypto"]
if not self.options.no_zlib:
self.cpp_info.components["crypto"].requires.append("zlib::zlib")
if self.settings.os == "Windows":
self.cpp_info.components["crypto"].system_libs.extend(["crypt32", "ws2_32", "advapi32", "user32", "bcrypt"])
elif self.settings.os == "Linux":
self.cpp_info.components["crypto"].system_libs.extend(["dl", "rt"])
self.cpp_info.components["ssl"].system_libs.append("dl")
if not self.options.no_threads:
self.cpp_info.components["crypto"].system_libs.append("pthread")
self.cpp_info.components["ssl"].system_libs.append("pthread")
elif self.settings.os == "Neutrino":
self.cpp_info.components["crypto"].system_libs.append("atomic")
self.cpp_info.components["ssl"].system_libs.append("atomic")
self.cpp_info.components["crypto"].system_libs.append("socket")
self.cpp_info.components["ssl"].system_libs.append("socket")
self.cpp_info.components["crypto"].set_property("cmake_target_name", "OpenSSL::Crypto")
self.cpp_info.components["crypto"].set_property("pkg_config_name", "libcrypto")
self.cpp_info.components["ssl"].set_property("cmake_target_name", "OpenSSL::SSL")
self.cpp_info.components["ssl"].set_property("pkg_config_name", "libssl")
openssl_modules_dir = os.path.join(self.package_folder, "lib", "ossl-modules")
self.runenv_info.define_path("OPENSSL_MODULES", openssl_modules_dir)