Compare commits

..

6 Commits

Author SHA1 Message Date
yp
6b7d0fdb20 remove check version xray in start connect 2026-08-21 14:56:50 +03:00
yp
844a78c2a9 fixed xhttp/mkcp + tls 2026-08-21 11:20:00 +03:00
yp
aff0ab7e05 in-place save and TLS pin 2026-08-21 10:05:38 +03:00
yp
c24e2f68bc remove test logs 2026-08-18 19:26:03 +03:00
yp
2bf076874d merge dev 2026-08-18 17:21:52 +03:00
yp
025ad6fe1f Feat: split xray client and server settings 2026-08-18 17:20:27 +03:00
51 changed files with 3285 additions and 1504 deletions

View File

@@ -5,7 +5,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(PROJECT AmneziaVPN)
if(NOT AMNEZIAVPN_VERSION)
set(AMNEZIAVPN_VERSION 5.0.1.5 CACHE STRING "Client app version")
set(AMNEZIAVPN_VERSION 5.0.1.1 CACHE STRING "Client app version")
endif()
set(QT_CREATOR_SKIP_PACKAGE_MANAGER_SETUP ON CACHE BOOL "" FORCE)
@@ -32,7 +32,7 @@ set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
# bump by 2 on every release, because we're releasing two versions on the play store
set(APP_ANDROID_VERSION_CODE 2152)
set(APP_ANDROID_VERSION_CODE 2148)
if(DEFINED APP_ANDROID_VERSION_CODE_OFFSET)
math(EXPR APP_ANDROID_VERSION_CODE "${APP_ANDROID_VERSION_CODE} + ${APP_ANDROID_VERSION_CODE_OFFSET}")
endif()

View File

@@ -190,6 +190,18 @@ if(APPLE)
cmake_policy(SET CMP0099 NEW)
cmake_policy(SET CMP0114 NEW)
if(NOT BUILD_OSX_APP_IDENTIFIER)
set(BUILD_OSX_APP_IDENTIFIER org.amnezia.AmneziaVPN CACHE STRING "OSX Application identifier")
endif()
if(NOT BUILD_IOS_APP_IDENTIFIER)
set(BUILD_IOS_APP_IDENTIFIER org.amnezia.AmneziaVPN CACHE STRING "iOS Application identifier")
endif()
if(NOT BUILD_IOS_GROUP_IDENTIFIER)
set(BUILD_IOS_GROUP_IDENTIFIER group.org.amnezia.AmneziaVPN CACHE STRING "iOS Group identifier")
endif()
if(NOT BUILD_VPN_DEVELOPMENT_TEAM)
set(BUILD_VPN_DEVELOPMENT_TEAM X7UJ388FXK CACHE STRING "Amnezia VPN Development Team")
endif()
set(CMAKE_XCODE_GENERATE_SCHEME FALSE)
set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${BUILD_VPN_DEVELOPMENT_TEAM})

View File

@@ -7,16 +7,3 @@ endif()
if(NOT CLIENT_NETWORK_EXTENSION_NAME)
set(CLIENT_NETWORK_EXTENSION_NAME "${CLIENT_APPLICATION_NAME}NetworkExtension" CACHE STRING "Display name for Apple network extension targets")
endif()
if(NOT BUILD_OSX_APP_IDENTIFIER)
set(BUILD_OSX_APP_IDENTIFIER org.amnezia.AmneziaVPN CACHE STRING "OSX Application identifier")
endif()
if(NOT BUILD_IOS_APP_IDENTIFIER)
set(BUILD_IOS_APP_IDENTIFIER org.amnezia.AmneziaVPN CACHE STRING "iOS Application identifier")
endif()
if(NOT BUILD_IOS_GROUP_IDENTIFIER)
set(BUILD_IOS_GROUP_IDENTIFIER group.org.amnezia.AmneziaVPN CACHE STRING "iOS Group identifier")
endif()
if(NOT BUILD_VPN_DEVELOPMENT_TEAM)
set(BUILD_VPN_DEVELOPMENT_TEAM X7UJ388FXK CACHE STRING "Amnezia VPN Development Team")
endif()

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@
#define XRAY_CONFIGURATOR_H
#include <QObject>
#include <QJsonArray>
#include <QJsonObject>
#include "configuratorBase.h"
@@ -35,7 +36,23 @@ public:
amnezia::ContainerConfig &containerConfig,
const amnezia::DnsSettings &dnsSettings);
bool uploadClientTemplate(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container,
const amnezia::XrayClientTemplate &clientTemplate) const;
amnezia::XrayClientTemplate readClientTemplate(const amnezia::ServerCredentials &credentials,
amnezia::DockerContainer container, bool &outFound) const;
static bool isSecuritySupportedOnSelfHosted(const amnezia::XrayServerConfig &srv);
private:
QJsonArray collectServerClients(const amnezia::ServerCredentials &credentials,
amnezia::DockerContainer container, const QString &flowValue,
const QString &fallbackClientId, amnezia::ErrorCode &outError) const;
amnezia::ErrorCode uploadServerConfigAtomically(const amnezia::ServerCredentials &credentials,
amnezia::DockerContainer container, const QString &listenPort,
const QJsonObject &serverConfig) const;
amnezia::ErrorCode readContainerKeyFile(amnezia::DockerContainer container,
const amnezia::ServerCredentials &credentials,
const QString &path, QString &out) const;
@@ -50,18 +67,21 @@ private:
amnezia::XrayProtocolConfig buildClientProtocolConfig(const amnezia::ServerCredentials &credentials,
amnezia::DockerContainer container,
const amnezia::XrayServerConfig &srv,
const amnezia::XrayClientTemplate &tpl,
const QString &clientId,
amnezia::ErrorCode &errorCode,
const QString &prefetchedRealityPublicKey = {},
const QString &prefetchedRealityShortId = {}) const;
const QString &prefetchedRealityShortId = {},
const QString &prefetchedTlsPin = {}) const;
amnezia::ErrorCode readRealityKeyFiles(amnezia::DockerContainer container,
const amnezia::ServerCredentials &credentials,
QString &outPublicKey,
QString &outShortId) const;
QJsonObject buildStreamSettings(const amnezia::XrayServerConfig &srv,
const QString &clientId) const;
amnezia::ErrorCode ensureTlsCertificate(const amnezia::ServerCredentials &credentials,
amnezia::DockerContainer container,
QString &outFingerprint) const;
};
#endif // XRAY_CONFIGURATOR_H

View File

@@ -200,6 +200,10 @@ GatewayController::DecryptionResult GatewayController::tryDecryptResponseBody(co
result.isDecryptionSuccessful = false;
}
qDebug() << "tryDecryptResponseBody: inputBytes=" << encryptedResponseBody.size() << "keyBytes=" << key.size()
<< "ivBytes=" << iv.size() << "saltBytes=" << salt.size() << "replyError=" << replyError
<< "decrypted=" << result.isDecryptionSuccessful << "outputBytes=" << decrypted.size();
return result;
}
@@ -482,7 +486,9 @@ bool GatewayController::shouldBypassProxy(const QNetworkReply::NetworkError &rep
apiErrorMessage = jsonObj.value(QStringLiteral("message")).toString().trimmed();
}
} else {
qDebug() << "failed to decrypt the data";
qDebug() << "failed to decrypt the data, bypassing the proxy: replyError=" << replyError
<< "bodyBytes=" << responseBody.size()
<< "(an empty body looks the same here as a wrong key, compare with tryDecryptResponseBody above)";
return true;
}
@@ -496,6 +502,7 @@ bool GatewayController::shouldBypassProxy(const QNetworkReply::NetworkError &rep
return true;
}
if (apiHttpStatus == httpStatusCodeRequestTimeout) {
qDebug() << "keeping the proxy: apiHttpStatus=" << apiHttpStatus;
return false;
}
if (apiHttpStatus == httpStatusCodeNotFound) {
@@ -517,18 +524,24 @@ bool GatewayController::shouldBypassProxy(const QNetworkReply::NetworkError &rep
}
}
if (apiHttpStatus == httpStatusCodeConflict) {
qDebug() << "keeping the proxy: apiHttpStatus=" << apiHttpStatus;
return false;
}
if (apiHttpStatus == httpStatusCodePaymentRequired) {
qDebug() << "keeping the proxy: apiHttpStatus=" << apiHttpStatus;
return false;
}
if (apiHttpStatus == httpStatusCodeUnprocessableEntity) {
return apiErrorMessage != unprocessableSubscriptionMessage;
const bool bypass = apiErrorMessage != unprocessableSubscriptionMessage;
qDebug() << "apiHttpStatus=" << apiHttpStatus << "bypass=" << bypass
<< "(decided by the api message, which is not printed)";
return bypass;
}
if (replyError != QNetworkReply::NetworkError::NoError) {
qDebug() << replyError;
qDebug() << "bypassing the proxy on a reply error:" << replyError << "apiHttpStatus=" << apiHttpStatus;
return true;
}
qDebug() << "keeping the proxy: nothing matched, apiHttpStatus=" << apiHttpStatus << "replyError=" << replyError;
return false;
}

View File

@@ -12,6 +12,7 @@
#include "core/configurators/configuratorBase.h"
#include "core/configurators/xrayConfigurator.h"
#include "core/models/protocols/xrayProtocolConfig.h"
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
#include "core/utils/protocolEnum.h"
@@ -59,6 +60,70 @@ namespace
{
Logger logger("InstallController");
QString effectiveXrayPort(const XrayProtocolConfig *cfg)
{
if (!cfg || cfg->serverConfig.port.isEmpty()) {
return QString::fromLatin1(protocols::xray::defaultPort);
}
return cfg->serverConfig.port;
}
QString normalizeXrayRelease(QString raw)
{
raw = raw.trimmed();
if (raw.startsWith(QLatin1String("v"), Qt::CaseInsensitive)) {
raw.remove(0, 1);
}
return raw.trimmed();
}
QString parseXrayVersionOutput(const QString &output)
{
static const QRegularExpression re(QStringLiteral(R"(Xray\s+v?(\d+\.\d+\.\d+))"),
QRegularExpression::CaseInsensitiveOption);
const auto match = re.match(output);
if (!match.hasMatch()) {
return {};
}
return match.captured(1);
}
enum class XrayBinaryProbeResult {
Match,
Mismatch,
Failed
};
XrayBinaryProbeResult probeXrayServerBinary(const ServerCredentials &credentials, SshSession &sshSession)
{
const QString expected =
normalizeXrayRelease(QString::fromLatin1(protocols::xray::expectedServerXrayRelease));
QString stdOut;
auto collect = [&stdOut](const QString &data, libssh::Client &) {
stdOut += data;
return ErrorCode::NoError;
};
const QString script = SshSession::replaceVars(
QStringLiteral("sudo docker exec $CONTAINER_NAME xray version 2>/dev/null | head -1"),
amnezia::genBaseVars(credentials, DockerContainer::Xray, QString(), QString()));
const ErrorCode errorCode = sshSession.runScript(credentials, script, collect, collect);
const QString live = parseXrayVersionOutput(stdOut);
if (errorCode != ErrorCode::NoError || live.isEmpty()) {
return XrayBinaryProbeResult::Failed;
}
if (live != expected) {
return XrayBinaryProbeResult::Mismatch;
}
return XrayBinaryProbeResult::Match;
}
bool xrayServerBinaryMismatchesExpected(const ServerCredentials &credentials, SshSession &sshSession)
{
return probeXrayServerBinary(credentials, sshSession) != XrayBinaryProbeResult::Match;
}
bool dockerDaemonContainerMissing(const QString &out, const QString &containerDockerName)
{
if (!out.contains(QLatin1String("Error response from daemon"), Qt::CaseInsensitive)) {
@@ -74,15 +139,33 @@ namespace
return false;
}
bool containerKeepsIdentityInDataVolume(DockerContainer container)
{
return container == DockerContainer::MtProxy
|| container == DockerContainer::Telemt
|| container == DockerContainer::Xray;
}
QString buildRemoveContainerScript(const amnezia::ScriptVars &vars, bool removeDataVolume)
{
QString script = SshSession::replaceVars(amnezia::scriptData(SharedScriptType::remove_container), vars);
if (removeDataVolume) {
script += QLatin1String("\nsudo docker volume rm -f $CONTAINER_NAME-data 2>/dev/null || true");
script += QLatin1String(
"\nfor attempt in 1 2 3 4 5; do"
"\n sudo docker volume rm -f $CONTAINER_NAME-data && break"
"\n sleep 1"
"\ndone"
"\necho \"amnezia_volume_left=$(sudo docker volume ls -q -f name=^$CONTAINER_NAME-data$ | head -1)\"");
script = SshSession::replaceVars(script, vars);
}
return script;
}
bool dataVolumeSurvivedRemoval(const QString &out)
{
static const QRegularExpression reLeft(QStringLiteral("amnezia_volume_left=(\\S+)"));
return reLeft.match(out).hasMatch();
}
}
InstallController::InstallController(SecureServersRepository *serversRepository,
@@ -131,12 +214,30 @@ ErrorCode InstallController::setupContainer(const ServerCredentials &credentials
return e;
qDebug().noquote() << "InstallController::setupContainer prepareHostWorker finished";
QMap<QString, QString> xrayStateToMigrate;
if (isUpdate) {
e = readXrayStateBeforeVolumeMigration(credentials, container, sshSession, xrayStateToMigrate);
if (e)
return e;
}
const amnezia::ScriptVars removeContainerVars =
amnezia::genBaseVars(credentials, container, QString(), QString());
const bool removeDataVolume = !isUpdate && (container == DockerContainer::MtProxy || container == DockerContainer::Telemt);
sshSession.runScript(credentials, buildRemoveContainerScript(removeContainerVars, removeDataVolume));
const bool removeDataVolume = !isUpdate && containerKeepsIdentityInDataVolume(container);
QString removeOut;
auto collectRemoveOut = [&removeOut](const QString &data, libssh::Client &) {
removeOut += data + "\n";
return ErrorCode::NoError;
};
sshSession.runScript(credentials, buildRemoveContainerScript(removeContainerVars, removeDataVolume),
collectRemoveOut, collectRemoveOut);
qDebug().noquote() << "InstallController::setupContainer removeContainer finished";
if (removeDataVolume && dataVolumeSurvivedRemoval(removeOut)) {
logger.error() << "Data volume survived removal, refusing to install on top of it, output=" << removeOut;
return ErrorCode::ServerDataVolumeNotRemoved;
}
qDebug().noquote() << "buildContainerWorker start";
e = buildContainerWorker(credentials, container, config, sshSession);
if (e)
@@ -148,6 +249,10 @@ ErrorCode InstallController::setupContainer(const ServerCredentials &credentials
return e;
qDebug().noquote() << "InstallController::setupContainer runContainerWorker finished";
e = restoreXrayStateIntoDataVolume(credentials, container, sshSession, xrayStateToMigrate);
if (e)
return e;
e = configureContainerWorker(credentials, container, config, sshSession);
if (e)
return e;
@@ -171,18 +276,46 @@ ErrorCode InstallController::setupContainer(const ServerCredentials &credentials
ErrorCode InstallController::updateServerConfig(const QString &serverId, DockerContainer container, const ContainerConfig &oldConfig,
ContainerConfig &newConfig)
{
if (!isUpdateDockerContainerRequired(container, oldConfig, newConfig)) {
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
const bool serverSettingsChanged = isUpdateDockerContainerRequired(container, oldConfig, newConfig);
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
SshSession sshSession;
bool reinstallRequired = false;
bool portReinstall = false;
bool binaryMismatch = false;
if (serverSettingsChanged) {
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
portReinstall = isReinstallContainerRequired(container, oldConfig, newConfig);
reinstallRequired = portReinstall;
}
if (container == DockerContainer::Xray) {
if (credentials.isValid()) {
binaryMismatch = xrayServerBinaryMismatchesExpected(credentials, sshSession);
if (binaryMismatch) {
reinstallRequired = true;
}
}
}
if (!serverSettingsChanged && !reinstallRequired) {
if (container == DockerContainer::Xray || container == DockerContainer::SSXray) {
if (const auto *xray = newConfig.getXrayProtocolConfig()) {
if (credentials.isValid()) {
XrayConfigurator xrayConfigurator(&sshSession);
xrayConfigurator.uploadClientTemplate(credentials, container, xray->clientTemplate);
}
}
}
if (container == DockerContainer::MtProxy) {
ServerCredentials credentials = adminConfig->credentials();
SshSession sshSession;
MtProxyInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
} else if (container == DockerContainer::Telemt) {
ServerCredentials credentials = adminConfig->credentials();
SshSession sshSession;
TelemtInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
}
adminConfig->updateContainerConfig(container, newConfig);
@@ -190,22 +323,37 @@ ErrorCode InstallController::updateServerConfig(const QString &serverId, DockerC
return ErrorCode::NoError;
}
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
return ErrorCode::InternalError;
}
SshSession sshSession;
bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig);
qDebug() << "InstallController::updateServerConfig for container" << container << "reinstall required is" << reinstallRequired;
ErrorCode errorCode = ErrorCode::NoError;
if (reinstallRequired) {
if (container == DockerContainer::Xray) {
const QString oldPort = effectiveXrayPort(oldConfig.getXrayProtocolConfig());
const QString newPort = effectiveXrayPort(newConfig.getXrayProtocolConfig());
if (oldPort != newPort) {
errorCode = isServerPortBusy(credentials, container, newConfig, sshSession);
if (errorCode != ErrorCode::NoError) {
if (errorCode == ErrorCode::ServerPortAlreadyAllocatedError) {
logger.error() << "Xray reinstall refused, port busy, error=201";
}
return errorCode;
}
}
}
errorCode = setupContainer(credentials, container, newConfig, true);
if (container == DockerContainer::Xray) {
if (errorCode == ErrorCode::NoError) {
if (xrayServerBinaryMismatchesExpected(credentials, sshSession)) {
logger.error() << "Xray version probe: still mismatched after recreate";
}
} else {
logger.error() << "Xray version probe: recreate failed, error="
<< static_cast<int>(errorCode);
}
}
// Reinstall pulls the latest container image, so the server runs the latest protocol version
if (errorCode == ErrorCode::NoError && container == DockerContainer::Awg2) {
@@ -213,7 +361,17 @@ ErrorCode InstallController::updateServerConfig(const QString &serverId, DockerC
awgConfig->serverConfig.protocolVersion = protocols::awg::awgV3;
}
}
} else if (container != DockerContainer::Xray && container != DockerContainer::SSXray) {
} else if (container == DockerContainer::Xray) {
DnsSettings dnsSettings = { m_appSettingsRepository->primaryDns(), m_appSettingsRepository->secondaryDns() };
XrayConfigurator xrayConfigurator(&sshSession);
errorCode = xrayConfigurator.writeServerConfigForSetup(credentials, container, newConfig, dnsSettings);
if (errorCode == ErrorCode::NoError) {
errorCode = sshSession.runScript(
credentials,
sshSession.replaceVars(QStringLiteral("sudo docker restart $CONTAINER_NAME"),
amnezia::genBaseVars(credentials, container, QString(), QString())));
}
} else if (container != DockerContainer::SSXray) {
errorCode = configureContainerWorker(credentials, container, newConfig, sshSession);
if (errorCode == ErrorCode::NoError) {
errorCode = startupContainerWorker(credentials, container, newConfig, sshSession);
@@ -233,7 +391,12 @@ ErrorCode InstallController::updateServerConfig(const QString &serverId, DockerC
TelemtInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
}
if (reinstallRequired) {
clearCachedProfile(serverId, container);
// OpenVPN/WireGuard reinstall mints new client keys, so the old profile is revoked.
// Xray identity lives in the data volume; the uuid is unchanged. Revoking it here
// drops the admin from clients[] (seen after a port change with a shared account).
if (container != DockerContainer::Xray) {
clearCachedProfile(serverId, container);
}
}
adminConfig->updateContainerConfig(container, newConfig);
m_serversRepository->editServer(serverId, adminConfig->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
@@ -442,6 +605,11 @@ ErrorCode InstallController::prepareContainerConfig(DockerContainer container, c
void InstallController::adminAppendRequested(const QString &serverId, DockerContainer container,
const ContainerConfig &containerConfig, const QString &clientName)
{
// Xray admin identity is the volume uuid; it must stay in server.json and must not
// appear in the Share Users list (revoking it emptied the inbound).
if (container == DockerContainer::Xray) {
return;
}
if (ContainerUtils::containerService(container) == ServiceType::Other
|| !containerConfig.protocolConfig.hasClientConfig()) {
return;
@@ -605,6 +773,142 @@ ErrorCode InstallController::startupContainerWorker(const ServerCredentials &cre
baseVars));
}
ErrorCode InstallController::readXrayStateBeforeVolumeMigration(const ServerCredentials &credentials,
DockerContainer container, SshSession &sshSession,
QMap<QString, QString> &outFiles)
{
outFiles.clear();
if (container != DockerContainer::Xray) {
return ErrorCode::NoError;
}
namespace px = amnezia::protocols::xray;
const amnezia::ScriptVars vars = amnezia::genBaseVars(credentials, container, QString(), QString());
QString stdOut;
auto collect = [&stdOut](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
const QString probe = QStringLiteral(
"echo \"amnezia_volume=$(sudo docker volume ls -q -f name=^$CONTAINER_NAME-data$ | head -1)\"\n"
"echo \"amnezia_container=$(sudo docker ps -a -q -f name=^$CONTAINER_NAME$ | head -1)\"\n"
"if [ -n \"$(sudo docker ps -a -q -f name=^$CONTAINER_NAME$ | head -1)\" ] && "
"[ -z \"$(sudo docker ps -q -f name=^$CONTAINER_NAME$ | head -1)\" ]; then\n"
" sudo docker start $CONTAINER_NAME > /dev/null 2>&1\n"
" sleep 2\n"
"fi\n"
"echo \"amnezia_running=$(sudo docker ps -q -f name=^$CONTAINER_NAME$ | head -1)\"\n"
"echo \"amnezia_mounted=$(sudo docker inspect -f "
"'{{range .Mounts}}{{if and (eq .Destination \"%1\") "
"(eq .Name \"$CONTAINER_NAME-data\")}}yes{{end}}{{end}}' "
"$CONTAINER_NAME 2>/dev/null | head -1)\"")
.arg(QString::fromLatin1(px::dataDir));
ErrorCode errorCode = sshSession.runScript(credentials, SshSession::replaceVars(probe, vars), collect, collect);
if (errorCode != ErrorCode::NoError) {
logger.error() << "Xray key migration: could not probe server state";
return ErrorCode::XrayKeyMigrationFailed;
}
static const QRegularExpression reContainer(QStringLiteral("amnezia_container=(\\S+)"));
static const QRegularExpression reRunning(QStringLiteral("amnezia_running=(\\S+)"));
static const QRegularExpression reMounted(QStringLiteral("amnezia_mounted=(\\S+)"));
const bool containerExists = reContainer.match(stdOut).hasMatch();
const bool containerRunning = reRunning.match(stdOut).hasMatch();
const bool dataDirIsVolume = reMounted.match(stdOut).hasMatch();
if (!containerExists) {
return ErrorCode::NoError;
}
if (dataDirIsVolume) {
return ErrorCode::NoError;
}
if (!containerRunning) {
logger.error() << "Xray key migration: container will not start, cannot read the keys out of it";
return ErrorCode::XrayKeyMigrationFailed;
}
const QStringList requiredPaths = {
QString::fromLatin1(px::PrivateKeyPath),
QString::fromLatin1(px::PublicKeyPath),
QString::fromLatin1(px::uuidPath),
QString::fromLatin1(px::shortidPath),
};
for (const QString &path : requiredPaths) {
QString content;
bool read = false;
for (int attempt = 0; attempt < 3 && !read; ++attempt) {
ErrorCode fileError = ErrorCode::NoError;
content = QString::fromUtf8(sshSession.getTextFileFromContainer(container, credentials, path, fileError));
if (fileError == ErrorCode::NoError && !content.trimmed().isEmpty()) {
read = true;
break;
}
if (attempt < 2) {
QThread::msleep(500);
}
}
if (!read) {
logger.error() << "Xray key migration: failed to read" << path << ", aborting before container removal";
return ErrorCode::XrayKeyMigrationFailed;
}
outFiles.insert(path, content);
}
QString serverConfig;
bool configRead = false;
for (int attempt = 0; attempt < 3 && !configRead; ++attempt) {
ErrorCode configError = ErrorCode::NoError;
serverConfig = QString::fromUtf8(sshSession.getTextFileFromContainer(
container, credentials, QString::fromLatin1(px::serverConfigPath), configError));
if (configError == ErrorCode::NoError && !serverConfig.trimmed().isEmpty()) {
configRead = true;
break;
}
if (attempt < 2) {
QThread::msleep(500);
}
}
if (!configRead) {
logger.error() << "Xray key migration: server config unreadable, aborting before container removal";
return ErrorCode::XrayKeyMigrationFailed;
}
outFiles.insert(QString::fromLatin1(px::serverConfigPath), serverConfig);
ErrorCode templateError = ErrorCode::NoError;
const QString clientTemplate = QString::fromUtf8(sshSession.getTextFileFromContainer(
container, credentials, QString::fromLatin1(px::clientTemplatePath), templateError));
if (templateError == ErrorCode::NoError && !clientTemplate.trimmed().isEmpty()) {
outFiles.insert(QString::fromLatin1(px::clientTemplatePath), clientTemplate);
}
return ErrorCode::NoError;
}
ErrorCode InstallController::restoreXrayStateIntoDataVolume(const ServerCredentials &credentials,
DockerContainer container, SshSession &sshSession,
const QMap<QString, QString> &files)
{
if (files.isEmpty()) {
return ErrorCode::NoError;
}
for (auto it = files.constBegin(); it != files.constEnd(); ++it) {
const ErrorCode errorCode = sshSession.uploadTextFileToContainer(
container, credentials, it.value(), it.key(), libssh::ScpOverwriteMode::ScpOverwriteExisting);
if (errorCode != ErrorCode::NoError) {
logger.error() << "Xray key migration: failed to write back" << it.key();
return ErrorCode::XrayKeyMigrationFailed;
}
}
return ErrorCode::NoError;
}
ErrorCode InstallController::isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config, SshSession &sshSession)
{
if (container == DockerContainer::Dns) {
@@ -722,7 +1026,20 @@ bool InstallController::isReinstallContainerRequired(DockerContainer container,
}
}
if (container == DockerContainer::Xray || container == DockerContainer::SSXray) {
if (container == DockerContainer::Xray) {
const auto *oldXrayConfig = oldConfig.getXrayProtocolConfig();
const auto *newXrayConfig = newConfig.getXrayProtocolConfig();
if (oldXrayConfig && newXrayConfig) {
const QString oldPort = effectiveXrayPort(oldXrayConfig);
const QString newPort = effectiveXrayPort(newXrayConfig);
if (oldPort != newPort) {
return true;
}
}
}
if (container == DockerContainer::SSXray) {
const auto *oldXrayConfig = oldConfig.getXrayProtocolConfig();
const auto *newXrayConfig = newConfig.getXrayProtocolConfig();
@@ -1010,9 +1327,20 @@ ErrorCode InstallController::removeContainer(const QString &serverId, DockerCont
SshSession sshSession;
const amnezia::ScriptVars removeContainerVars =
amnezia::genBaseVars(credentials, container, QString(), QString());
const bool removeDataVolume = (container == DockerContainer::MtProxy || container == DockerContainer::Telemt);
ErrorCode errorCode =
sshSession.runScript(credentials, buildRemoveContainerScript(removeContainerVars, removeDataVolume));
const bool removeDataVolume = containerKeepsIdentityInDataVolume(container);
QString removeOut;
auto collectRemoveOut = [&removeOut](const QString &data, libssh::Client &) {
removeOut += data + "\n";
return ErrorCode::NoError;
};
ErrorCode errorCode = sshSession.runScript(
credentials, buildRemoveContainerScript(removeContainerVars, removeDataVolume), collectRemoveOut,
collectRemoveOut);
if (errorCode == ErrorCode::NoError && removeDataVolume && dataVolumeSurvivedRemoval(removeOut)) {
logger.error() << "Data volume survived protocol removal, output=" << removeOut;
errorCode = ErrorCode::ServerDataVolumeNotRemoved;
}
if (errorCode == ErrorCode::NoError) {
QMap<DockerContainer, ContainerConfig> containers = adminConfig->containers;
@@ -1087,6 +1415,13 @@ bool InstallController::isUpdateDockerContainerRequired(DockerContainer containe
return false;
}
}
} else if (container == DockerContainer::Xray) {
const auto *oldXray = oldConfig.getXrayProtocolConfig();
const auto *newXray = newConfig.getXrayProtocolConfig();
if (oldXray && newXray && oldXray->serverConfig.hasEqualServerSettings(newXray->serverConfig)) {
return false;
}
return true;
} else if (container == DockerContainer::MtProxy) {
const auto *oldMt = oldConfig.getMtProxyProtocolConfig();
const auto *newMt = newConfig.getMtProxyProtocolConfig();

View File

@@ -104,6 +104,11 @@ private:
ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, SshSession &sshSession);
ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config, SshSession &sshSession);
ErrorCode readXrayStateBeforeVolumeMigration(const ServerCredentials &credentials, DockerContainer container,
SshSession &sshSession, QMap<QString, QString> &outFiles);
ErrorCode restoreXrayStateIntoDataVolume(const ServerCredentials &credentials, DockerContainer container,
SshSession &sshSession, const QMap<QString, QString> &files);
ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const ContainerConfig &config, SshSession &sshSession);
ErrorCode isUserInSudo(const ServerCredentials &credentials, SshSession &sshSession);
ErrorCode isServerDpkgBusy(const ServerCredentials &credentials, SshSession &sshSession);
@@ -126,7 +131,7 @@ private:
SecureServersRepository* m_serversRepository;
SecureAppSettingsRepository* m_appSettingsRepository;
bool m_cancelInstallation = false;
#ifndef Q_OS_IOS
QList<QSharedPointer<QProcess>> m_sftpMountProcesses;
#endif

View File

@@ -2,6 +2,7 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDateTime>
#include "core/utils/containerEnum.h"
@@ -55,6 +56,34 @@ int UsersController::clientIndexById(const QString &clientId, const QJsonArray &
return -1;
}
QString UsersController::readXrayVolumeUuid(const DockerContainer container, const ServerCredentials &credentials,
SshSession *sshSession) const
{
ErrorCode error = ErrorCode::NoError;
const QString uuid = QString::fromUtf8(sshSession->getTextFileFromContainer(
container, credentials, QString::fromLatin1(amnezia::protocols::xray::uuidPath), error));
if (error != ErrorCode::NoError) {
logger.warning() << "readXrayVolumeUuid: failed, error=" << static_cast<int>(error);
return {};
}
return uuid.trimmed();
}
int UsersController::stripXrayVolumeUuidFromTable(QJsonArray &clientsTable, const QString &volumeUuid)
{
if (volumeUuid.isEmpty()) {
return 0;
}
int removed = 0;
for (int i = clientsTable.size() - 1; i >= 0; --i) {
if (clientsTable.at(i).toObject().value(configKey::clientId).toString() == volumeUuid) {
clientsTable.removeAt(i);
++removed;
}
}
return removed;
}
void UsersController::migration(const QByteArray &clientsTableString, QJsonArray &clientsTable)
{
QJsonObject clientsTableObj = QJsonDocument::fromJson(clientsTableString).object();
@@ -264,6 +293,7 @@ ErrorCode UsersController::getXrayClients(const DockerContainer container, const
}
const QJsonArray clients = settings[protocols::xray::clients].toArray();
const QString xrayDefaultUuid = readXrayVolumeUuid(container, credentials, sshSession);
for (const auto &clientValue : clients) {
const QJsonObject clientObj = clientValue.toObject();
if (!clientObj.contains(protocols::xray::id)) {
@@ -271,9 +301,6 @@ ErrorCode UsersController::getXrayClients(const DockerContainer container, const
continue;
}
QString clientId = clientObj[protocols::xray::id].toString();
QString xrayDefaultUuid = sshSession->getTextFileFromContainer(container, credentials, amnezia::protocols::xray::uuidPath, error);
xrayDefaultUuid.replace("\n", "");
if (!isClientExists(clientId, clientsTable) && clientId != xrayDefaultUuid) {
QJsonObject client;
@@ -297,10 +324,14 @@ ErrorCode UsersController::updateClients(const QString &serverId, const DockerCo
SshSession sshSession;
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
logger.error() << "updateClients: no admin config for this server, container="
<< ContainerUtils::containerTypeToString(container);
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
logger.error() << "updateClients: credentials are not valid, container="
<< ContainerUtils::containerTypeToString(container);
return ErrorCode::InternalError;
}
@@ -313,7 +344,8 @@ ErrorCode UsersController::updateClients(const QString &serverId, const DockerCo
const QByteArray clientsTableString = sshSession.getTextFileFromContainer(container, credentials, clientsTableFile, error);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to get the clientsTable file from the server";
logger.error() << "updateClients: failed to read the clientsTable file, error=" << static_cast<int>(error)
<< "container=" << ContainerUtils::containerTypeToString(container);
emit clientsUpdated(QJsonArray());
return error;
}
@@ -321,6 +353,10 @@ ErrorCode UsersController::updateClients(const QString &serverId, const DockerCo
m_clientsTable = QJsonDocument::fromJson(clientsTableString).array();
if (m_clientsTable.isEmpty()) {
logger.info() << "updateClients: the clientsTable is empty, rebuilding it from the server, readBytes="
<< clientsTableString.size()
<< (clientsTableString.trimmed().isEmpty() ? ", the file was empty"
: ", the file had content but did not parse as an array");
migration(clientsTableString, m_clientsTable);
int count = 0;
@@ -333,6 +369,8 @@ ErrorCode UsersController::updateClients(const QString &serverId, const DockerCo
error = getXrayClients(container, credentials, &sshSession, count, m_clientsTable);
}
if (error != ErrorCode::NoError) {
logger.error() << "updateClients: failed to rebuild the client list from the server, error="
<< static_cast<int>(error) << "container=" << ContainerUtils::containerTypeToString(container);
emit clientsUpdated(QJsonArray());
return error;
}
@@ -341,7 +379,27 @@ ErrorCode UsersController::updateClients(const QString &serverId, const DockerCo
if (clientsTableString != newClientsTableString) {
error = sshSession.uploadTextFileToContainer(container, credentials, newClientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
logger.error() << "updateClients: failed to write back the rebuilt clientsTable, error="
<< static_cast<int>(error) << "container=" << ContainerUtils::containerTypeToString(container)
<< "rows=" << m_clientsTable.size();
logger.warning() << "updateClients: the app now holds a client list the server does not have;"
<< "this error is returned but every caller discards it";
}
}
}
if (container == DockerContainer::Xray) {
const QString volumeUuid = readXrayVolumeUuid(container, credentials, &sshSession);
const int stripped = stripXrayVolumeUuidFromTable(m_clientsTable, volumeUuid);
if (stripped > 0) {
logger.info() << "updateClients: removed the volume uuid from the user list, rows="
<< stripped;
const QByteArray strippedTable = QJsonDocument(m_clientsTable).toJson();
const ErrorCode writeError =
sshSession.uploadTextFileToContainer(container, credentials, strippedTable, clientsTableFile);
if (writeError != ErrorCode::NoError) {
logger.error() << "updateClients: failed to write the user list without the volume uuid, error="
<< static_cast<int>(writeError);
}
}
}
@@ -393,18 +451,32 @@ ErrorCode UsersController::appendClient(const QString &serverId, const QString &
SshSession sshSession;
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
logger.error() << "appendClient: no admin config for this server, container="
<< ContainerUtils::containerTypeToString(container);
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
logger.error() << "appendClient: credentials are not valid, container="
<< ContainerUtils::containerTypeToString(container);
return ErrorCode::InternalError;
}
error = updateClients(serverId, container);
if (error != ErrorCode::NoError) {
logger.error() << "appendClient: could not read the current client list, error=" << static_cast<int>(error)
<< ", the new account will not appear in the user list";
return error;
}
if (container == DockerContainer::Xray) {
const QString volumeUuid = readXrayVolumeUuid(container, credentials, &sshSession);
if (!volumeUuid.isEmpty() && clientId == volumeUuid) {
logger.info() << "appendClient: skipped the volume uuid, it is not listed in Share Users";
return ErrorCode::NoError;
}
}
int existingIndex = clientIndexById(clientId, m_clientsTable);
if (existingIndex >= 0) {
return renameClient(serverId, existingIndex, clientName, container, true);
@@ -430,7 +502,11 @@ ErrorCode UsersController::appendClient(const QString &serverId, const QString &
error = sshSession.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
logger.error() << "appendClient: failed to upload the clientsTable, error=" << static_cast<int>(error)
<< "container=" << ContainerUtils::containerTypeToString(container)
<< "rows=" << m_clientsTable.size();
logger.warning() << "appendClient: the account exists on the server but is missing from the user list;"
<< "the caller discards this error, so nothing is reported to the user";
return error;
}
@@ -477,7 +553,9 @@ ErrorCode UsersController::renameClient(const QString &serverId, const int row,
ErrorCode error = sshSession.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
logger.error() << "renameClient: failed to upload the clientsTable, error=" << static_cast<int>(error)
<< "container=" << ContainerUtils::containerTypeToString(container)
<< "rows=" << m_clientsTable.size();
return error;
}
@@ -522,7 +600,9 @@ ErrorCode UsersController::revokeOpenVpn(const int row, const DockerContainer co
clientsTableFile = clientsTableFile.arg(ContainerUtils::containerTypeToString(DockerContainer::OpenVpn));
error = sshSession->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
logger.error() << "revokeOpenVpn: failed to upload the clientsTable, error=" << static_cast<int>(error)
<< "container=" << ContainerUtils::containerTypeToString(container)
<< "rows=" << clientsTable.size();
return error;
}
@@ -582,7 +662,9 @@ ErrorCode UsersController::revokeWireGuard(const int row, const DockerContainer
}
error = sshSession->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
logger.error() << "revokeWireGuard: failed to upload the clientsTable, error=" << static_cast<int>(error)
<< "container=" << ContainerUtils::containerTypeToString(container)
<< "rows=" << clientsTable.size();
return error;
}
@@ -615,6 +697,24 @@ ErrorCode UsersController::revokeXray(const int row,
ErrorCode error = ErrorCode::NoError;
auto client = clientsTable.at(row).toObject();
QString clientId = client.value(configKey::clientId).toString();
const QString volumeUuid = readXrayVolumeUuid(container, credentials, sshSession);
if (!volumeUuid.isEmpty() && clientId == volumeUuid) {
logger.info() << "revokeXray: refused, this is the volume uuid";
clientsTable.removeAt(row);
const QByteArray clientsTableString = QJsonDocument(clientsTable).toJson();
const QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable")
.arg(ContainerUtils::containerTypeToString(container));
error = sshSession->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "revokeXray: refused the volume uuid but failed to drop it from the user list, error="
<< static_cast<int>(error);
}
return ErrorCode::NoError;
}
const QString serverConfigPath = amnezia::protocols::xray::serverConfigPath;
const QString configString = sshSession->getTextFileFromContainer(container, credentials, serverConfigPath, error);
if (error != ErrorCode::NoError) {
@@ -628,9 +728,6 @@ ErrorCode UsersController::revokeXray(const int row,
return ErrorCode::InternalError;
}
auto client = clientsTable.at(row).toObject();
QString clientId = client.value(configKey::clientId).toString();
QJsonObject configObj = serverConfig.object();
if (!configObj.contains(protocols::xray::inbounds)) {
logger.error() << "Missing inbounds in xray config";
@@ -669,6 +766,10 @@ ErrorCode UsersController::revokeXray(const int row,
}
}
if (clients.isEmpty()) {
logger.warning() << "revokeXray: inbound left with zero clients";
}
settings[protocols::xray::clients] = clients;
inbound[protocols::xray::settings] = settings;
inbounds[0] = inbound;
@@ -693,7 +794,11 @@ ErrorCode UsersController::revokeXray(const int row,
error = sshSession->uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file";
logger.error() << "revokeXray: failed to upload the clientsTable, error=" << static_cast<int>(error)
<< "container=" << ContainerUtils::containerTypeToString(container)
<< "rows=" << clientsTable.size();
logger.warning() << "revokeXray: the account is gone from the server config but still listed in the user table;"
<< "this error is then overwritten by the container restart below and never reaches the caller";
}
QString restartScript = QString("sudo docker restart $CONTAINER_NAME");
@@ -712,16 +817,22 @@ ErrorCode UsersController::revokeXray(const int row,
ErrorCode UsersController::revokeClient(const QString &serverId, const int index, const DockerContainer container)
{
if (index < 0 || index >= m_clientsTable.size()) {
logger.error() << "revokeClient: row" << index << "is outside the client table of" << m_clientsTable.size()
<< "rows, container=" << ContainerUtils::containerTypeToString(container);
return ErrorCode::InternalError;
}
SshSession sshSession;
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
logger.error() << "revokeClient: no admin config for this server, container="
<< ContainerUtils::containerTypeToString(container);
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
logger.error() << "revokeClient: credentials are not valid, container="
<< ContainerUtils::containerTypeToString(container);
return ErrorCode::InternalError;
}
@@ -777,16 +888,22 @@ ErrorCode UsersController::revokeClient(const QString &serverId, const Container
SshSession sshSession;
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
logger.error() << "revokeClient by config: no admin config for this server, container="
<< ContainerUtils::containerTypeToString(container);
return ErrorCode::InternalError;
}
ServerCredentials credentials = adminConfig->credentials();
if (!credentials.isValid()) {
logger.error() << "revokeClient by config: credentials are not valid, container="
<< ContainerUtils::containerTypeToString(container);
return ErrorCode::InternalError;
}
ErrorCode errorCode = ErrorCode::NoError;
errorCode = updateClients(serverId, container);
if (errorCode != ErrorCode::NoError) {
logger.error() << "revokeClient by config: could not read the current client list, error="
<< static_cast<int>(errorCode);
return errorCode;
}
@@ -812,6 +929,9 @@ ErrorCode UsersController::revokeClient(const QString &serverId, const Container
int row = clientIndexById(clientId, m_clientsTable);
if (row < 0) {
logger.warning() << "revokeClient by config: this account is not in the client table of"
<< m_clientsTable.size() << "rows, nothing was revoked and success is reported,"
<< "container=" << ContainerUtils::containerTypeToString(container);
return errorCode;
}

View File

@@ -66,6 +66,10 @@ private:
ErrorCode getXrayClients(const DockerContainer container, const ServerCredentials& credentials,
SshSession* sshSession, int &count, QJsonArray &clientsTable);
QString readXrayVolumeUuid(const DockerContainer container, const ServerCredentials &credentials,
SshSession *sshSession) const;
int stripXrayVolumeUuidFromTable(QJsonArray &clientsTable, const QString &volumeUuid);
ErrorCode wgShow(const DockerContainer container, const ServerCredentials &credentials,
SshSession* sshSession, std::vector<WgShowData> &data);

View File

@@ -86,8 +86,10 @@ ContainerConfig InstallerBase::createBaseConfig(DockerContainer container, int p
srv.flow = protocols::xray::defaultFlow;
srv.site = protocols::xray::defaultSite;
srv.sni = protocols::xray::defaultSni;
srv.fingerprint = protocols::xray::defaultFingerprint;
srv.alpn = protocols::xray::defaultAlpn;
xrayConfig.clientTemplate.fingerprint = protocols::xray::defaultFingerprint;
xrayConfig.clientTemplate.uplinkMethod = protocols::xray::defaultXhttpUplinkMethod;
xrayConfig.clientTemplate.formatVersion = 1;
config.protocolConfig = xrayConfig;
break;
}

View File

@@ -1,7 +1,6 @@
#include "xrayInstaller.h"
#include <QJsonDocument>
#include <QJsonArray>
#include "core/utils/containerEnum.h"
#include "core/utils/containers/containerUtils.h"
@@ -11,6 +10,7 @@
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/configurators/xrayConfigurator.h"
#include "core/models/protocols/xrayProtocolConfig.h"
#include "logger.h"
@@ -18,38 +18,21 @@ namespace
{
Logger logger("XrayInstaller");
// Xray expects uTLS preset names (chrome, firefox, …). Old Amnezia/server templates used "Mozilla/5.0".
QString normalizeXrayFingerprint(const QString &fp)
QString describeServerJsonStatus(amnezia::XrayServerJsonStatus status)
{
if (fp.isEmpty() || fp.contains(QLatin1String("Mozilla/5.0"), Qt::CaseInsensitive)) {
return QString::fromLatin1(protocols::xray::defaultFingerprint);
}
return fp;
}
// Parse an xray int range: "from-to" string, plain int, or legacy {from,to} object.
void parseIntRange(const QJsonValue &v, QString &minOut, QString &maxOut)
{
if (v.isString()) {
const QString s = v.toString().trimmed();
const int dash = s.indexOf(QLatin1Char('-'), 1);
if (dash > 0) {
minOut = s.left(dash).trimmed();
maxOut = s.mid(dash + 1).trimmed();
} else if (!s.isEmpty()) {
minOut = s;
maxOut = s;
}
} else if (v.isDouble()) {
minOut = QString::number(v.toInt());
maxOut = minOut;
} else if (v.isObject()) {
const QJsonObject o = v.toObject();
if (o.contains(QLatin1String("from")) || o.contains(QLatin1String("to"))) {
minOut = QString::number(o.value(QLatin1String("from")).toInt());
maxOut = QString::number(o.value(QLatin1String("to")).toInt());
}
switch (status) {
case amnezia::XrayServerJsonStatus::MissingInbounds:
return QStringLiteral("server config missing 'inbounds' field");
case amnezia::XrayServerJsonStatus::EmptyInbounds:
return QStringLiteral("server config has empty 'inbounds' array");
case amnezia::XrayServerJsonStatus::MissingStreamSettings:
return QStringLiteral("inbound missing 'streamSettings' field");
case amnezia::XrayServerJsonStatus::MissingSettings:
return QStringLiteral("inbound missing 'settings' field");
case amnezia::XrayServerJsonStatus::Ok:
break;
}
return QStringLiteral("ok");
}
}
@@ -64,10 +47,12 @@ XrayInstaller::XrayInstaller(QObject *parent)
ErrorCode XrayInstaller::extractConfigFromContainer(DockerContainer container, const ServerCredentials &credentials,
SshSession* sshSession, ContainerConfig &config)
{
namespace px = amnezia::protocols::xray;
ErrorCode errorCode = ErrorCode::NoError;
QString currentConfig = sshSession->getTextFileFromContainer(
container, credentials, amnezia::protocols::xray::serverConfigPath, errorCode);
container, credentials, px::serverConfigPath, errorCode);
if (errorCode != ErrorCode::NoError) {
return errorCode;
@@ -78,267 +63,36 @@ ErrorCode XrayInstaller::extractConfigFromContainer(DockerContainer container, c
logger.error() << "Failed to parse server config JSON";
return ErrorCode::InternalError;
}
QJsonObject serverConfig = doc.object();
if (!serverConfig.contains(protocols::xray::inbounds)) {
logger.error() << "Server config missing 'inbounds' field";
return ErrorCode::InternalError;
}
QJsonArray inbounds = serverConfig[protocols::xray::inbounds].toArray();
if (inbounds.isEmpty()) {
logger.error() << "Server config has empty 'inbounds' array";
return ErrorCode::InternalError;
}
QJsonObject inbound = inbounds[0].toObject();
if (!inbound.contains(protocols::xray::streamSettings)) {
logger.error() << "Inbound missing 'streamSettings' field";
return ErrorCode::InternalError;
}
QJsonObject streamSettings = inbound[protocols::xray::streamSettings].toObject();
auto *xrayConfig = config.getXrayProtocolConfig();
if (!xrayConfig) {
logger.error() << "No XrayProtocolConfig in ContainerConfig";
return ErrorCode::InternalError;
}
XrayServerConfig &srv = xrayConfig->serverConfig;
// ── Port ─────────────────────────────────────────────────────────
if (inbound.contains(protocols::xray::port)) {
srv.port = QString::number(inbound[protocols::xray::port].toInt());
XrayClientTemplate &tpl = xrayConfig->clientTemplate;
const amnezia::XrayServerJsonStatus status =
XrayServerConfig::fromServerInboundJson(doc.object(), xrayConfig->serverConfig, tpl);
if (status != amnezia::XrayServerJsonStatus::Ok) {
logger.error() << "Xray extractConfigFromContainer:" << describeServerJsonStatus(status);
return ErrorCode::InternalError;
}
// ── Network (transport) ───────────────────────────────────────────
QString networkVal = streamSettings.value(protocols::xray::network).toString("tcp");
if (networkVal == "xhttp") {
srv.transport = "xhttp";
} else if (networkVal == "kcp") {
srv.transport = "mkcp";
} else {
srv.transport = "raw";
}
logger.info() << "Xray extractConfigFromContainer: extracted server, port=" << xrayConfig->serverConfig.port
<< "transport=" << xrayConfig->serverConfig.transport
<< "security=" << xrayConfig->serverConfig.security << "site=" << xrayConfig->serverConfig.site
<< "sni=" << xrayConfig->serverConfig.sni;
// ── Security ──────────────────────────────────────────────────────
srv.security = streamSettings.value(protocols::xray::security).toString("reality");
// ── Reality settings ──────────────────────────────────────────────
if (srv.security == "reality") {
QJsonObject rs = streamSettings.value(protocols::xray::realitySettings).toObject();
// serverNames array → site + sni
if (rs.contains(protocols::xray::serverNames)) {
QString sniVal = rs[protocols::xray::serverNames].toArray().first().toString();
srv.sni = sniVal;
srv.site = sniVal;
} else if (rs.contains(protocols::xray::serverName)) {
srv.sni = rs[protocols::xray::serverName].toString();
srv.site = srv.sni;
{
XrayConfigurator configurator(sshSession);
bool found = false;
const XrayClientTemplate stored = configurator.readClientTemplate(credentials, container, found);
if (found) {
tpl = stored;
logger.info() << "Xray extractConfigFromContainer: adopted the client template from the server,"
<< "fingerprint=" << tpl.fingerprint << "uplinkMethod=" << tpl.uplinkMethod;
} else {
logger.info() << "Xray extractConfigFromContainer: no client template on the server to adopt";
}
srv.fingerprint = normalizeXrayFingerprint(rs.value(protocols::xray::fingerprint).toString());
}
// ── TLS settings ──────────────────────────────────────────────────
if (srv.security == "tls") {
QJsonObject tls = streamSettings.value("tlsSettings").toObject();
srv.sni = tls.value(protocols::xray::serverName).toString();
srv.fingerprint = normalizeXrayFingerprint(tls.value(protocols::xray::fingerprint).toString());
QJsonArray alpnArr = tls.value("alpn").toArray();
QStringList alpnList;
for (const QJsonValue &v : alpnArr) {
QString t = v.toString().trimmed();
if (t.compare(QLatin1String("HTTP/2"), Qt::CaseInsensitive) == 0)
t = QStringLiteral("h2");
else if (t.compare(QLatin1String("HTTP/1.1"), Qt::CaseInsensitive) == 0)
t = QStringLiteral("http/1.1");
if (!t.isEmpty())
alpnList << t;
}
srv.alpn = alpnList.join(",");
}
// ── Flow (from users array) ───────────────────────────────────────
if (inbound.contains(protocols::xray::settings)) {
QJsonObject s = inbound[protocols::xray::settings].toObject();
QJsonArray clientsArr = s.value(protocols::xray::clients).toArray();
if (!clientsArr.isEmpty()) {
srv.flow = clientsArr[0].toObject().value(protocols::xray::flow).toString();
}
}
// ── XHTTP settings (Xray-core SplitHTTPConfig + legacy Amnezia keys) ──
if (srv.transport == "xhttp") {
QJsonObject xhttpObj = streamSettings.value("xhttpSettings").toObject();
{
const QString m = xhttpObj.value("mode").toString();
if (m.isEmpty() || m == QLatin1String("auto"))
srv.xhttp.mode = QStringLiteral("Auto");
else if (m == QLatin1String("packet-up"))
srv.xhttp.mode = QStringLiteral("Packet-up");
else if (m == QLatin1String("stream-up"))
srv.xhttp.mode = QStringLiteral("Stream-up");
else if (m == QLatin1String("stream-one"))
srv.xhttp.mode = QStringLiteral("Stream-one");
else
srv.xhttp.mode = m;
}
srv.xhttp.host = xhttpObj.value("host").toString();
srv.xhttp.path = xhttpObj.value("path").toString();
if (xhttpObj.contains(QLatin1String("uplinkHTTPMethod")))
srv.xhttp.uplinkMethod = xhttpObj.value("uplinkHTTPMethod").toString();
else
srv.xhttp.uplinkMethod = xhttpObj.value("method").toString();
srv.xhttp.disableGrpc = xhttpObj.value("noGRPCHeader").toBool(true);
srv.xhttp.disableSse = xhttpObj.value("noSSEHeader").toBool(true);
auto sessionSeqUi = [](const QString &core) -> QString {
if (core.isEmpty() || core == QLatin1String("path"))
return QStringLiteral("Path");
if (core == QLatin1String("cookie"))
return QStringLiteral("Cookie");
if (core == QLatin1String("header"))
return QStringLiteral("Header");
if (core == QLatin1String("query"))
return QStringLiteral("Query");
return core;
};
QString sess = xhttpObj.value("sessionIDPlacement").toString();
if (sess.isEmpty())
sess = xhttpObj.value("sessionPlacement").toString();
if (sess.isEmpty())
sess = xhttpObj.value("scSessionPlacement").toString();
srv.xhttp.sessionPlacement = sessionSeqUi(sess);
QString seq = xhttpObj.value("seqPlacement").toString();
if (seq.isEmpty())
seq = xhttpObj.value("scSeqPlacement").toString();
srv.xhttp.seqPlacement = sessionSeqUi(seq);
auto uplinkDataUi = [](const QString &core) -> QString {
if (core.isEmpty() || core == QLatin1String("body"))
return QStringLiteral("Body");
if (core == QLatin1String("auto"))
return QStringLiteral("Auto");
if (core == QLatin1String("header"))
return QStringLiteral("Header");
if (core == QLatin1String("cookie"))
return QStringLiteral("Cookie");
return core;
};
QString udata = xhttpObj.value("uplinkDataPlacement").toString();
if (udata.isEmpty())
udata = xhttpObj.value("scUplinkDataPlacement").toString();
srv.xhttp.uplinkDataPlacement = uplinkDataUi(udata);
srv.xhttp.sessionKey = xhttpObj.value("sessionIDKey").toString();
if (srv.xhttp.sessionKey.isEmpty())
srv.xhttp.sessionKey = xhttpObj.value("sessionKey").toString();
srv.xhttp.seqKey = xhttpObj.value("seqKey").toString();
srv.xhttp.uplinkDataKey = xhttpObj.value("uplinkDataKey").toString();
if (xhttpObj.contains(QLatin1String("uplinkChunkSize"))) {
QString ucMin, ucMax;
parseIntRange(xhttpObj.value("uplinkChunkSize"), ucMin, ucMax);
if (!ucMin.isEmpty())
srv.xhttp.uplinkChunkSize = ucMin;
} else if (xhttpObj.contains(QLatin1String("xhttpUplinkChunkSize"))) {
srv.xhttp.uplinkChunkSize = QString::number(xhttpObj.value("xhttpUplinkChunkSize").toInt());
}
if (xhttpObj.contains(QLatin1String("scMaxBufferedPosts"))) {
srv.xhttp.scMaxBufferedPosts = QString::number(xhttpObj.value("scMaxBufferedPosts").toVariant().toLongLong());
}
auto readRange = [&](const char *key, QString &minOut, QString &maxOut) {
parseIntRange(xhttpObj.value(QLatin1String(key)), minOut, maxOut);
};
readRange("scMaxEachPostBytes", srv.xhttp.scMaxEachPostBytesMin, srv.xhttp.scMaxEachPostBytesMax);
readRange("scMinPostsIntervalMs", srv.xhttp.scMinPostsIntervalMsMin, srv.xhttp.scMinPostsIntervalMsMax);
readRange("scStreamUpServerSecs", srv.xhttp.scStreamUpServerSecsMin, srv.xhttp.scStreamUpServerSecsMax);
auto loadPaddingFromObject = [&](const QJsonObject &pad) {
if (pad.contains(QLatin1String("xPaddingObfsMode")))
srv.xhttp.xPadding.obfsMode = pad.value("xPaddingObfsMode").toBool(true);
srv.xhttp.xPadding.key = pad.value("xPaddingKey").toString();
srv.xhttp.xPadding.header = pad.value("xPaddingHeader").toString();
srv.xhttp.xPadding.placement = pad.value("xPaddingPlacement").toString();
srv.xhttp.xPadding.method = pad.value("xPaddingMethod").toString();
QString bytesMin, bytesMax;
parseIntRange(pad.value("xPaddingBytes"), bytesMin, bytesMax);
if (!bytesMin.isEmpty()) {
srv.xhttp.xPadding.bytesMin = bytesMin;
srv.xhttp.xPadding.bytesMax = bytesMax;
}
QString pl = srv.xhttp.xPadding.placement.toLower();
if (pl == QLatin1String("cookie"))
srv.xhttp.xPadding.placement = QStringLiteral("Cookie");
else if (pl == QLatin1String("header"))
srv.xhttp.xPadding.placement = QStringLiteral("Header");
else if (pl == QLatin1String("query"))
srv.xhttp.xPadding.placement = QStringLiteral("Query");
else if (pl == QLatin1String("queryinheader"))
srv.xhttp.xPadding.placement = QStringLiteral("Query in header");
QString met = srv.xhttp.xPadding.method.toLower();
if (met == QLatin1String("repeat-x"))
srv.xhttp.xPadding.method = QStringLiteral("Repeat-x");
else if (met == QLatin1String("tokenish"))
srv.xhttp.xPadding.method = QStringLiteral("Tokenish");
};
if (xhttpObj.contains(QLatin1String("xPaddingObfsMode")) || xhttpObj.contains(QLatin1String("xPaddingKey"))
|| xhttpObj.contains(QLatin1String("xPaddingBytes"))) {
loadPaddingFromObject(xhttpObj);
} else if (xhttpObj.contains(QLatin1String("xPadding")) && xhttpObj.value("xPadding").isObject()) {
const QJsonObject nested = xhttpObj.value("xPadding").toObject();
if (!nested.isEmpty()) {
loadPaddingFromObject(nested);
if (!nested.contains(QLatin1String("xPaddingObfsMode")))
srv.xhttp.xPadding.obfsMode = true;
}
}
if (xhttpObj.contains(QLatin1String("xmux"))) {
QJsonObject mux = xhttpObj.value("xmux").toObject();
srv.xhttp.xmux.enabled = true;
auto readMuxRange = [&](const char *key, QString &minOut, QString &maxOut) {
parseIntRange(mux.value(QLatin1String(key)), minOut, maxOut);
};
readMuxRange("maxConcurrency", srv.xhttp.xmux.maxConcurrencyMin, srv.xhttp.xmux.maxConcurrencyMax);
readMuxRange("maxConnections", srv.xhttp.xmux.maxConnectionsMin, srv.xhttp.xmux.maxConnectionsMax);
readMuxRange("cMaxReuseTimes", srv.xhttp.xmux.cMaxReuseTimesMin, srv.xhttp.xmux.cMaxReuseTimesMax);
readMuxRange("hMaxRequestTimes", srv.xhttp.xmux.hMaxRequestTimesMin, srv.xhttp.xmux.hMaxRequestTimesMax);
readMuxRange("hMaxReusableSecs", srv.xhttp.xmux.hMaxReusableSecsMin, srv.xhttp.xmux.hMaxReusableSecsMax);
if (mux.contains(QLatin1String("hKeepAlivePeriod")))
srv.xhttp.xmux.hKeepAlivePeriod = QString::number(mux.value("hKeepAlivePeriod").toVariant().toLongLong());
}
}
// ── mKCP settings ─────────────────────────────────────────────────
if (srv.transport == "mkcp") {
QJsonObject kcp = streamSettings.value("kcpSettings").toObject();
if (kcp.contains("tti")) {
srv.mkcp.tti = QString::number(kcp["tti"].toInt());
}
if (kcp.contains("uplinkCapacity")) {
srv.mkcp.uplinkCapacity = QString::number(kcp["uplinkCapacity"].toInt());
}
if (kcp.contains("downlinkCapacity")) {
srv.mkcp.downlinkCapacity = QString::number(kcp["downlinkCapacity"].toInt());
}
if (kcp.contains("readBufferSize")) {
srv.mkcp.readBufferSize = QString::number(kcp["readBufferSize"].toInt());
}
if (kcp.contains("writeBufferSize")) {
srv.mkcp.writeBufferSize = QString::number(kcp["writeBufferSize"].toInt());
}
srv.mkcp.congestion = kcp.value("congestion").toBool(true);
}
return ErrorCode::NoError;

View File

@@ -19,6 +19,14 @@ namespace amnezia
namespace
{
// A toggle set to "off" behaves exactly like a missing one, so it is not an AWG 3 marker
bool isAwgToggleEnabled(const QString &value)
{
const QString trimmedValue = value.trimmed();
return !trimmedValue.isEmpty()
&& trimmedValue.compare(QLatin1String(protocols::awg::awgBoolOff), Qt::CaseInsensitive) != 0;
}
template <typename T>
bool hasAwg3Markers(const T &config)
{
@@ -32,8 +40,7 @@ namespace
return true;
}
return AwgProtocolConfig::isToggleEnabled(config.randomTrailers)
|| AwgProtocolConfig::isToggleEnabled(config.disableCookies);
return isAwgToggleEnabled(config.randomTrailers) || isAwgToggleEnabled(config.disableCookies);
}
template <typename T>
@@ -424,13 +431,6 @@ QString AwgProtocolConfig::clientProtocolVersion() const
return clientConfig.has_value() ? awgVersionOf(clientConfig.value()) : QString();
}
bool AwgProtocolConfig::isToggleEnabled(const QString &value)
{
const QString trimmedValue = value.trimmed();
return !trimmedValue.isEmpty()
&& trimmedValue.compare(QLatin1String(protocols::awg::awgBoolOff), Qt::CaseInsensitive) != 0;
}
QString AwgProtocolConfig::protocolVersionString(const QString &version)
{
if (version == protocols::awg::awgV3) return QObject::tr(" (version 3.1)");

View File

@@ -109,7 +109,6 @@ struct AwgProtocolConfig {
QString serverProtocolVersion() const;
QString clientProtocolVersion() const;
static QString protocolVersionString(const QString &version);
static bool isToggleEnabled(const QString &value);
bool hasClientConfig() const;
void setClientConfig(const AwgClientConfig& config);

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,11 @@
#ifndef XRAYPROTOCOLCONFIG_H
#define XRAYPROTOCOLCONFIG_H
#include <QJsonArray>
#include <QJsonObject>
#include "core/utils/constants/protocolConstants.h"
#include <QString>
#include <QStringList>
#include <optional>
namespace amnezia
@@ -43,12 +45,32 @@ struct XrayXmuxConfig {
static XrayXmuxConfig fromJson(const QJsonObject &json);
};
struct XrayClientTemplate {
int formatVersion = 0;
QString fingerprint = protocols::xray::defaultFingerprint;
QString uplinkMethod = protocols::xray::defaultXhttpUplinkMethod;
QString uplinkChunkSize = protocols::xray::defaultXhttpUplinkChunkSize;
QString scMinPostsIntervalMsMin = protocols::xray::defaultXhttpScMinPostsIntervalMsMin;
QString scMinPostsIntervalMsMax = protocols::xray::defaultXhttpScMinPostsIntervalMsMax;
XrayXmuxConfig xmux;
QString updatedAt;
QJsonObject toJson() const;
static XrayClientTemplate fromJson(const QJsonObject &json);
QString contentFingerprint() const;
void materializeFromLegacy(const QJsonObject &storedServerJson);
};
// ── XHTTP transport ───────────────────────────────────────────────────────────
struct XrayXhttpConfig {
QString mode = protocols::xray::defaultXhttpMode; // Auto|Packet-up|Stream-up|Stream-one
QString host = protocols::xray::defaultXhttpHost;
QString path;
QString uplinkMethod = protocols::xray::defaultXhttpUplinkMethod; // POST|PUT|PATCH
bool disableGrpc = true;
bool disableSse = true;
@@ -61,17 +83,13 @@ struct XrayXhttpConfig {
QString uplinkDataKey;
// Traffic Shaping
QString uplinkChunkSize = protocols::xray::defaultXhttpUplinkChunkSize;
QString scMaxBufferedPosts;
QString scMaxEachPostBytesMin = protocols::xray::defaultXhttpScMaxEachPostBytesMin;
QString scMaxEachPostBytesMax = protocols::xray::defaultXhttpScMaxEachPostBytesMax;
QString scMinPostsIntervalMsMin = protocols::xray::defaultXhttpScMinPostsIntervalMsMin;
QString scMinPostsIntervalMsMax = protocols::xray::defaultXhttpScMinPostsIntervalMsMax;
QString scStreamUpServerSecsMin = protocols::xray::defaultXhttpScStreamUpServerSecsMin;
QString scStreamUpServerSecsMax = protocols::xray::defaultXhttpScStreamUpServerSecsMax;
XrayXPaddingConfig xPadding;
XrayXmuxConfig xmux;
QJsonObject toJson() const;
/// Reads only keys present in JSON (no Amnezia UI defaults). Use XrayConfigModel::applyDefaultsToServerConfig for UI.
@@ -81,16 +99,47 @@ struct XrayXhttpConfig {
// ── mKCP transport ────────────────────────────────────────────────────────────
struct XrayMkcpConfig {
QString tti;
QString mtu;
QString uplinkCapacity;
QString downlinkCapacity;
QString readBufferSize;
QString writeBufferSize;
bool congestion = true;
QString cwndMultiplier;
QString maxSendingWindow;
QJsonObject toJson() const;
static XrayMkcpConfig fromJson(const QJsonObject &json);
};
/// Which end of the connection the emitted xray json is meant for. The client
/// stream settings are the server ones plus the fields the server never reads.
enum class XrayStreamSide {
Server,
Client,
};
/// Runtime values the server document needs and the structure must not keep:
/// the client list of this particular operation and the ssh-read reality secrets.
struct XrayServerInboundInputs {
QJsonArray clients;
QString realityPrivateKey;
QString realityShortId;
};
/// Why a server.json could not be read back into the structure. The caller turns
/// this into an error code and a log line; parsing itself stays silent.
enum class XrayServerJsonStatus {
Ok,
MissingInbounds,
EmptyInbounds,
MissingStreamSettings,
MissingSettings,
};
/// What to do with account entries that carry no id when the list is rewritten.
enum class XrayClientListFilter {
KeepAll,
DropWithoutId,
};
// ── Server config (settings editable by user) ─────────────────────────────────
struct XrayServerConfig {
QString port;
@@ -101,7 +150,6 @@ struct XrayServerConfig {
QString security;
QString flow;
QString fingerprint;
QString sni;
QString alpn;
@@ -113,7 +161,77 @@ struct XrayServerConfig {
static XrayServerConfig fromJson(const QJsonObject &json);
void applyDefaults(bool fillFlowDefault = false);
/// Single emitter for both ends. The template is only read on the client side;
/// pass anything on the server side, its fields are not written there.
QJsonObject streamSettingsJson(XrayStreamSide side, const XrayClientTemplate &clientTemplate) const;
QJsonObject serverStreamSettings() const;
QJsonObject clientStreamSettings(const XrayClientTemplate &clientTemplate) const;
/// The whole server.json this configuration means, ready to be uploaded.
QJsonObject toServerInboundJson(const XrayServerInboundInputs &inputs) const;
/// The way back: a server.json read off the container into the two structures.
/// Both are written, because a server document also carries client-side fields
/// (the uTLS preset, the uplink method, xmux) that belong in the template.
static XrayServerJsonStatus fromServerInboundJson(const QJsonObject &serverJson, XrayServerConfig &outServerConfig,
XrayClientTemplate &outClientTemplate);
/// The account list of a server document. Reading is forgiving: a document with
/// no inbounds simply has no accounts. Writing is strict, because putting a list
/// into a document that has nowhere to hold it would drop accounts in silence.
static QJsonArray clientsFromServerInboundJson(const QJsonObject &serverJson);
static XrayServerJsonStatus setClientsInServerInboundJson(QJsonObject &serverJson, const QJsonArray &clients);
/// One account entry, its position in a list, and the flow rewritten across a list.
/// An empty flow means the key is taken out, which is how the raw transport wants it.
static QJsonObject makeClientEntry(const QString &clientId, const QString &flowValue);
static QJsonObject applyFlowToClient(const QJsonObject &client, const QString &flowValue);
static int indexOfClient(const QJsonArray &clients, const QString &clientId);
static QString firstClientId(const QJsonArray &clients);
static QJsonArray applyFlowToClients(const QJsonArray &clients, const QString &flowValue,
XrayClientListFilter filter = XrayClientListFilter::KeepAll);
QJsonObject serverView() const;
QJsonObject issuedConfigView() const;
bool hasEqualServerSettings(const XrayServerConfig &other) const;
QStringList serverViewDifferences(const XrayServerConfig &other) const;
bool breaksIssuedConfigs(const XrayServerConfig &other) const;
};
namespace xrayEffective
{
QString xhttpMode(const QString &mode);
QString sessionSeqPlacement(const QString &placement);
QString uplinkDataPlacement(const QString &placement);
QString xPaddingPlacement(const QString &placement);
QString xPaddingMethod(const QString &method);
QString range(const QString &minV, const QString &maxV);
void putRangeIfAny(QJsonObject &obj, const char *key, QString minV, QString maxV, const char *fallbackMin,
const char *fallbackMax);
QString security(const XrayServerConfig &srv);
QString clientFlow(const XrayServerConfig &srv);
QString network(const XrayServerConfig &srv);
QString xhttpModeSent(const XrayServerConfig &srv);
}
/// Runtime values the client document needs and the structures must not keep:
/// the host we connect to, the account this device was given, and the ssh-read
/// reality keys / TLS certificate pin.
struct XrayClientOutboundInputs {
QString serverAddress;
QString clientId;
QString realityPublicKey;
QString realityShortId;
QString tlsPinnedPeerCertSha256;
};
// ── Client config (generated, not edited by user) ─────────────────────────────
@@ -121,14 +239,20 @@ struct XrayClientConfig {
QString nativeConfig;
QString localPort;
QString id;
QString templateFingerprint;
QJsonObject toJson() const;
static XrayClientConfig fromJson(const QJsonObject &json);
/// The two runtime values read back out of a native client document.
static QString idFromNativeJson(const QJsonObject &nativeJson);
static QString localPortFromNativeJson(const QJsonObject &nativeJson);
};
// ── Top-level protocol config ──────────────────────────────────────────────────
struct XrayProtocolConfig {
XrayServerConfig serverConfig;
XrayClientTemplate clientTemplate;
std::optional<XrayClientConfig> clientConfig;
QJsonObject toJson() const;
@@ -139,8 +263,20 @@ struct XrayProtocolConfig {
void clearClientConfig();
bool needsClientHydration = false;
bool needsTemplateMaterialization = false;
bool templateWasMaterialized = false;
/// The whole client document this configuration means, ready for the core.
QJsonObject toClientOutboundJson(const XrayClientOutboundInputs &inputs) const;
/// The way back: a client document read into the server config, the template and
/// the runtime fields of the cached client config.
bool fromClientOutboundJson(const QJsonObject &nativeJson);
bool hydrateServerConfigFromClientNative();
bool materializeTemplateFromServerConfig();
};
} // namespace amnezia

View File

@@ -214,11 +214,11 @@ namespace amnezia
// Transport — mKCP
constexpr QLatin1String mkcpTti("mkcp_tti");
constexpr QLatin1String mkcpMtu("mkcp_mtu");
constexpr QLatin1String mkcpUplinkCapacity("mkcp_uplink_capacity");
constexpr QLatin1String mkcpDownlinkCapacity("mkcp_downlink_capacity");
constexpr QLatin1String mkcpReadBufferSize("mkcp_read_buffer_size");
constexpr QLatin1String mkcpWriteBufferSize("mkcp_write_buffer_size");
constexpr QLatin1String mkcpCongestion("mkcp_congestion"); // bool
constexpr QLatin1String mkcpCwndMultiplier("mkcp_cwnd_multiplier");
constexpr QLatin1String mkcpMaxSendingWindow("mkcp_max_sending_window");
// xPadding
constexpr QLatin1String xPaddingBytesMin("xpadding_bytes_min");

View File

@@ -48,11 +48,16 @@ namespace amnezia
namespace xray
{
constexpr char dataDir[] = "/opt/amnezia/xray";
constexpr char serverConfigPath[] = "/opt/amnezia/xray/server.json";
constexpr char expectedServerXrayRelease[] = "v26.7.28";
constexpr char uuidPath[] = "/opt/amnezia/xray/xray_uuid.key";
constexpr char PublicKeyPath[] = "/opt/amnezia/xray/xray_public.key";
constexpr char PrivateKeyPath[] = "/opt/amnezia/xray/xray_private.key";
constexpr char shortidPath[] = "/opt/amnezia/xray/xray_short_id.key";
constexpr char tlsCertPath[] = "/opt/amnezia/xray/tls_cert.pem";
constexpr char tlsKeyPath[] = "/opt/amnezia/xray/tls_key.pem";
constexpr char clientTemplatePath[] = "/opt/amnezia/xray/template.json";
constexpr char defaultSite[] = "www.googletagmanager.com";
constexpr char defaultPort[] = "443";
@@ -67,7 +72,7 @@ namespace amnezia
constexpr char defaultSni[] = "www.googletagmanager.com";
constexpr char defaultAlpn[] = "h2";
constexpr char defaultXhttpMode[] = "Auto";
constexpr char defaultXhttpMode[] = "Stream-one";
constexpr char defaultXhttpUplinkMethod[] = "POST";
constexpr char defaultXhttpSessionPlacement[] = "Path";
constexpr char defaultXhttpSessionKey[] = "";
@@ -75,9 +80,10 @@ namespace amnezia
constexpr char defaultXhttpUplinkDataPlacement[] = "Body";
constexpr char defaultXhttpHost[] = "www.googletagmanager.com";
constexpr char defaultXhttpPath[] = "/";
constexpr char defaultXhttpUplinkChunkSize[] = "0";
constexpr char defaultXhttpScMaxEachPostBytesMin[] = "1";
constexpr char defaultXhttpScMaxEachPostBytesMax[] = "100";
constexpr char defaultXhttpScMaxEachPostBytesMin[] = "1000000";
constexpr char defaultXhttpScMaxEachPostBytesMax[] = "1000000";
constexpr char defaultXhttpScMinPostsIntervalMsMin[] = "100";
constexpr char defaultXhttpScMinPostsIntervalMsMax[] = "800";
constexpr char defaultXhttpScStreamUpServerSecsMin[] = "1";
@@ -93,8 +99,8 @@ namespace amnezia
constexpr char defaultMkcpTti[] = "50";
constexpr char defaultMkcpUplinkCapacity[] = "5";
constexpr char defaultMkcpDownlinkCapacity[] = "20";
constexpr char defaultMkcpReadBufferSize[] = "2";
constexpr char defaultMkcpWriteBufferSize[] = "2";
constexpr char defaultMkcpMtu[] = "1350";
constexpr char defaultMkcpCwndMultiplier[] = "1";
constexpr char outbounds[] = "outbounds";
constexpr char inbounds[] = "inbounds";
@@ -120,6 +126,100 @@ namespace amnezia
constexpr char spiderX[] = "spiderX";
constexpr char user[] = "user";
constexpr char pass[] = "pass";
// Envelope of a server.json / client.json document.
constexpr char logBlock[] = "log";
constexpr char logLevel[] = "loglevel";
constexpr char logLevelError[] = "error";
constexpr char protocol[] = "protocol";
constexpr char protocolVless[] = "vless";
constexpr char protocolFreedom[] = "freedom";
constexpr char protocolSocks[] = "socks";
constexpr char decryption[] = "decryption";
constexpr char decryptionNone[] = "none";
constexpr char listen[] = "listen";
constexpr char udp[] = "udp";
// streamSettings blocks.
constexpr char tlsSettings[] = "tlsSettings";
constexpr char xhttpSettings[] = "xhttpSettings";
constexpr char kcpSettings[] = "kcpSettings";
constexpr char alpn[] = "alpn";
constexpr char certificates[] = "certificates";
constexpr char certificateFile[] = "certificateFile";
constexpr char keyFile[] = "keyFile";
constexpr char allowInsecure[] = "allowInsecure";
constexpr char pinnedPeerCertSha256[] = "pinnedPeerCertSha256";
// realitySettings, server side only.
constexpr char dest[] = "dest";
constexpr char privateKey[] = "privateKey";
constexpr char shortIds[] = "shortIds";
// xhttpSettings fields, Xray-core SplitHTTPConfig.
constexpr char xhttpHost[] = "host";
constexpr char xhttpPath[] = "path";
constexpr char xhttpMode[] = "mode";
constexpr char noGrpcHeader[] = "noGRPCHeader";
constexpr char noSseHeader[] = "noSSEHeader";
constexpr char sessionIdPlacement[] = "sessionIDPlacement";
constexpr char sessionIdKey[] = "sessionIDKey";
constexpr char seqPlacement[] = "seqPlacement";
constexpr char seqKey[] = "seqKey";
constexpr char uplinkDataPlacement[] = "uplinkDataPlacement";
constexpr char uplinkDataKey[] = "uplinkDataKey";
constexpr char uplinkHttpMethod[] = "uplinkHTTPMethod";
constexpr char uplinkChunkSize[] = "uplinkChunkSize";
constexpr char scMaxBufferedPosts[] = "scMaxBufferedPosts";
constexpr char scMaxEachPostBytes[] = "scMaxEachPostBytes";
constexpr char scMinPostsIntervalMs[] = "scMinPostsIntervalMs";
constexpr char scStreamUpServerSecs[] = "scStreamUpServerSecs";
constexpr char xPaddingObfsMode[] = "xPaddingObfsMode";
constexpr char xPaddingBytes[] = "xPaddingBytes";
constexpr char xPaddingKey[] = "xPaddingKey";
constexpr char xPaddingHeader[] = "xPaddingHeader";
constexpr char xPaddingPlacement[] = "xPaddingPlacement";
constexpr char xPaddingMethod[] = "xPaddingMethod";
// xmux, client side only.
constexpr char xmux[] = "xmux";
constexpr char xmuxMaxConcurrency[] = "maxConcurrency";
constexpr char xmuxMaxConnections[] = "maxConnections";
constexpr char xmuxCMaxReuseTimes[] = "cMaxReuseTimes";
constexpr char xmuxHMaxRequestTimes[] = "hMaxRequestTimes";
constexpr char xmuxHMaxReusableSecs[] = "hMaxReusableSecs";
constexpr char xmuxHKeepAlivePeriod[] = "hKeepAlivePeriod";
// kcpSettings fields.
constexpr char kcpTti[] = "tti";
constexpr char kcpMtu[] = "mtu";
constexpr char kcpUplinkCapacity[] = "uplinkCapacity";
constexpr char kcpDownlinkCapacity[] = "downlinkCapacity";
constexpr char kcpCwndMultiplier[] = "cwndMultiplier";
constexpr char kcpMaxSendingWindow[] = "maxSendingWindow";
// Aliases written by older builds, read only.
constexpr char legacyXhttpMethod[] = "method";
constexpr char legacySessionPlacement[] = "sessionPlacement";
constexpr char legacyScSessionPlacement[] = "scSessionPlacement";
constexpr char legacyScSeqPlacement[] = "scSeqPlacement";
constexpr char legacyScUplinkDataPlacement[] = "scUplinkDataPlacement";
constexpr char legacySessionKey[] = "sessionKey";
constexpr char legacyUplinkChunkSize[] = "xhttpUplinkChunkSize";
constexpr char legacyXPaddingBlock[] = "xPadding";
constexpr char legacyRangeFrom[] = "from";
constexpr char legacyRangeTo[] = "to";
// Transport / security values as they appear in xray json.
constexpr char networkTcp[] = "tcp";
constexpr char networkXhttp[] = "xhttp";
constexpr char networkKcp[] = "kcp";
constexpr char transportRaw[] = "raw";
constexpr char transportXhttp[] = "xhttp";
constexpr char transportMkcp[] = "mkcp";
constexpr char securityNone[] = "none";
constexpr char securityTls[] = "tls";
constexpr char securityReality[] = "reality";
}
namespace cloak

View File

@@ -40,6 +40,12 @@ namespace amnezia
XrayRealityKeysReadFailed = 217,
ServerContainerRuntimeNotSupported = 218,
ContainerRuntimeServiceNotRunning = 219,
XrayKeyMigrationFailed = 220,
XrayTlsNotSupported = 221,
XrayServerConfigRejected = 222,
XrayServerConfigRolledBack = 223,
XrayServerNotServing = 224,
ServerDataVolumeNotRemoved = 225,
// Ssh connection errors
SshRequestDeniedError = 300,

View File

@@ -39,6 +39,33 @@ QString errorString(ErrorCode code) {
case(ErrorCode::XrayRealityKeysReadFailed):
errorMessage = QObject::tr("Server error: failed to read XRay Reality keys from the server");
break;
case(ErrorCode::XrayTlsNotSupported):
errorMessage = QObject::tr("Server error: could not create a TLS certificate on the XRay server. "
"The previous settings were left unchanged. Try again, or use Reality.");
break;
case(ErrorCode::XrayServerConfigRejected):
errorMessage = QObject::tr("Server error: XRay rejected the new configuration. "
"Nothing was changed, the server keeps running with the previous settings.");
break;
case(ErrorCode::XrayServerConfigRolledBack):
errorMessage = QObject::tr("Server error: XRay did not start with the new configuration. "
"The previous settings were restored and the server is running again.");
break;
case(ErrorCode::XrayServerNotServing):
errorMessage = QObject::tr("Server error: XRay is not accepting connections and the previous configuration "
"could not be restored. The server needs attention: check it over SSH, "
"or reinstall the protocol.");
break;
case(ErrorCode::ServerDataVolumeNotRemoved):
errorMessage = QObject::tr("Server error: the previous data volume could not be removed. "
"Installing on top of it would reuse the old keys, so the configurations you "
"expected to revoke would keep working. Check the server and try again.");
break;
case(ErrorCode::XrayKeyMigrationFailed):
errorMessage = QObject::tr("Server error: could not preserve the XRay keys stored on the server. "
"The settings were not changed, so the existing configurations keep working. "
"Check that the server is reachable and try again.");
break;
case(ErrorCode::ServerContainerRuntimeNotSupported): errorMessage = QObject::tr("Server error: The default container runtime available for installation on this server is not supported.\n Install Docker Engine on the server manually and try again."); break;
case(ErrorCode::ContainerRuntimeServiceNotRunning): errorMessage = QObject::tr("Container runtime error: The container runtime service is not running.\n Check the container runtime service on the server, or wait about a minute and try again."); break;

View File

@@ -265,10 +265,8 @@ amnezia::ScriptVars amnezia::genAwgVars(const ContainerConfig &containerConfig)
vars.append({ { "$REJECT_AFTER_TIME", config.rejectAfterTime } });
vars.append({ { "$KEEPALIVE_TIMEOUT", config.keepaliveTimeout } });
vars.append({ { "$MAX_HANDSHAKE_ATTEMPTS", config.maxHandshakeAttempts } });
vars.append({ { "$RANDOM_TRAILERS", AwgProtocolConfig::isToggleEnabled(config.randomTrailers)
? config.randomTrailers : QString() } });
vars.append({ { "$DISABLE_COOKIES", AwgProtocolConfig::isToggleEnabled(config.disableCookies)
? config.disableCookies : QString() } });
vars.append({ { "$RANDOM_TRAILERS", config.randomTrailers } });
vars.append({ { "$DISABLE_COOKIES", config.disableCookies } });
}
return vars;

View File

@@ -38,9 +38,9 @@
</dict>
<key>com.wireguard.ios.app_group_id</key>
<string>${BUILD_IOS_GROUP_IDENTIFIER}</string>
<string>group.${BUILD_IOS_APP_IDENTIFIER}</string>
<key>com.wireguard.macos.app_group_id</key>
<string>${BUILD_VPN_DEVELOPMENT_TEAM}.${BUILD_IOS_GROUP_IDENTIFIER}</string>
<string>${BUILD_VPN_DEVELOPMENT_TEAM}.group.${BUILD_OSX_APP_IDENTIFIER}</string>
</dict>
</plist>

View File

@@ -92,7 +92,7 @@ extension PacketTunnelProvider {
}
}
} catch {
wg_log(.error, message: "Can't parse WG config: \(String(describing: error))")
wg_log(.error, message: "Can't parse WG config: \(error.localizedDescription)")
errorNotifier.notify(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
completionHandler(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
return

View File

@@ -1,18 +1,57 @@
cd /opt/amnezia/xray
XRAY_CLIENT_ID=$(xray uuid) && echo $XRAY_CLIENT_ID > /opt/amnezia/xray/xray_uuid.key
XRAY_SHORT_ID=$(openssl rand -hex 8) && echo $XRAY_SHORT_ID > /opt/amnezia/xray/xray_short_id.key
XRAY_DIR=/opt/amnezia/xray
# Parse x25519 keypair by label (v26.7 output has an extra Hash32 line; line-index parsing breaks).
KEYPAIR=$(xray x25519)
XRAY_PRIVATE_KEY=$(printf '%s\n' "$KEYPAIR" | sed -n 's/.*[Pp]rivate[ ]*[Kk]ey:[[:space:]]*//p' | head -1)
XRAY_PUBLIC_KEY=$(printf '%s\n' "$KEYPAIR" | sed -n 's/.*(PublicKey):[[:space:]]*//p' | head -1)
[ -z "$XRAY_PUBLIC_KEY" ] && XRAY_PUBLIC_KEY=$(printf '%s\n' "$KEYPAIR" | sed -n 's/.*[Pp]ublic[ ]*[Kk]ey:[[:space:]]*//p' | head -1)
# /opt/amnezia/xray is a docker volume, so these files outlive the container.
# Regenerating any of them revokes every config already handed out, so each one is
# created only when it is really missing.
#
# "Really missing" is checked by content, not by file size. A failed generation used
# to leave a file holding a single newline, which is one byte, so a size test would
# call it present and the key would never be repaired. With the volume that state is
# permanent: nothing short of manual SSH would clear it.
has_key() {
[ -f "$1" ] && [ -n "$(tr -d '[:space:]' < "$1" 2>/dev/null)" ]
}
XRAY_PRIVATE_KEY=$(echo $XRAY_PRIVATE_KEY | tr -d ' ')
XRAY_PUBLIC_KEY=$(echo $XRAY_PUBLIC_KEY | tr -d ' ')
if ! has_key $XRAY_DIR/xray_uuid.key; then
XRAY_CLIENT_ID=$(xray uuid) && [ -n "$XRAY_CLIENT_ID" ] && printf '%s\n' "$XRAY_CLIENT_ID" > $XRAY_DIR/xray_uuid.key
fi
if ! has_key $XRAY_DIR/xray_short_id.key; then
XRAY_SHORT_ID=$(openssl rand -hex 8) && [ -n "$XRAY_SHORT_ID" ] && printf '%s\n' "$XRAY_SHORT_ID" > $XRAY_DIR/xray_short_id.key
fi
echo $XRAY_PUBLIC_KEY > /opt/amnezia/xray/xray_public.key
echo $XRAY_PRIVATE_KEY > /opt/amnezia/xray/xray_private.key
# The private key is the one that must never change: it is what every issued config
# is bound to. If only the public half is missing it gets derived from the private
# one rather than rotating the pair.
if has_key $XRAY_DIR/xray_private.key && ! has_key $XRAY_DIR/xray_public.key; then
XRAY_PRIVATE_KEY=$(tr -d '[:space:]' < $XRAY_DIR/xray_private.key)
DERIVED=$(xray x25519 -i "$XRAY_PRIVATE_KEY" 2>/dev/null)
XRAY_PUBLIC_KEY=$(printf '%s\n' "$DERIVED" | sed -n 's/.*(PublicKey):[[:space:]]*//p' | head -1)
[ -z "$XRAY_PUBLIC_KEY" ] && XRAY_PUBLIC_KEY=$(printf '%s\n' "$DERIVED" | sed -n 's/.*[Pp]ublic[ ]*[Kk]ey:[[:space:]]*//p' | head -1)
XRAY_PUBLIC_KEY=$(echo $XRAY_PUBLIC_KEY | tr -d ' ')
if [ -n "$XRAY_PUBLIC_KEY" ]; then
printf '%s\n' "$XRAY_PUBLIC_KEY" > $XRAY_DIR/xray_public.key
fi
fi
# server.json is written by the client (writeServerConfigForSetup); this script only makes keys.
if ! has_key $XRAY_DIR/xray_private.key || ! has_key $XRAY_DIR/xray_public.key; then
KEYPAIR=$(xray x25519)
XRAY_PRIVATE_KEY=$(printf '%s\n' "$KEYPAIR" | sed -n 's/.*[Pp]rivate[ ]*[Kk]ey:[[:space:]]*//p' | head -1)
XRAY_PUBLIC_KEY=$(printf '%s\n' "$KEYPAIR" | sed -n 's/.*(PublicKey):[[:space:]]*//p' | head -1)
[ -z "$XRAY_PUBLIC_KEY" ] && XRAY_PUBLIC_KEY=$(printf '%s\n' "$KEYPAIR" | sed -n 's/.*[Pp]ublic[ ]*[Kk]ey:[[:space:]]*//p' | head -1)
XRAY_PRIVATE_KEY=$(echo $XRAY_PRIVATE_KEY | tr -d ' ')
XRAY_PUBLIC_KEY=$(echo $XRAY_PUBLIC_KEY | tr -d ' ')
# Both or neither. A half-written pair hands clients a public key the server is
# not using, and it would look complete enough never to be repaired.
if [ -n "$XRAY_PRIVATE_KEY" ] && [ -n "$XRAY_PUBLIC_KEY" ]; then
printf '%s\n' "$XRAY_PRIVATE_KEY" > $XRAY_DIR/xray_private.key.tmp
printf '%s\n' "$XRAY_PUBLIC_KEY" > $XRAY_DIR/xray_public.key.tmp
mv $XRAY_DIR/xray_private.key.tmp $XRAY_DIR/xray_private.key
mv $XRAY_DIR/xray_public.key.tmp $XRAY_DIR/xray_public.key
else
rm -f $XRAY_DIR/xray_private.key.tmp $XRAY_DIR/xray_public.key.tmp
echo "amnezia_xray_keygen=failed"
fi
fi

View File

@@ -6,6 +6,7 @@ sudo docker run -d \
--cap-add=NET_ADMIN \
-p $XRAY_SERVER_PORT:$XRAY_SERVER_PORT/tcp \
-p $XRAY_SERVER_PORT:$XRAY_SERVER_PORT/udp \
-v $CONTAINER_NAME-data:/opt/amnezia/xray \
--name $CONTAINER_NAME $CONTAINER_NAME
sudo docker network connect amnezia-dns-net $CONTAINER_NAME

View File

@@ -591,27 +591,27 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="132"/>
<source>I1 - First special junk packet</source>
<translation>I1 - First special junk packet</translation>
<translation>I1 - أول حزمة بيانات زائفة خاصة</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="142"/>
<source>I2 - Second special junk packet</source>
<translation>I2 - Second special junk packet</translation>
<translation>I2 - ثاني حزمة بيانات زائفة خاصة</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="152"/>
<source>I3 - Third special junk packet</source>
<translation>I3 - Third special junk packet</translation>
<translation>I3 - ثالث حزمة بيانات زائفة خاصة</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="162"/>
<source>I4 - Fourth special junk packet</source>
<translation>I4 - Fourth special junk packet</translation>
<translation>I4 - رابع حزمة بيانات زائفة خاصة</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="172"/>
<source>I5 - Fifth special junk packet</source>
<translation>I5 - Fifth special junk packet</translation>
<translation>I5 - خامس حزمة بيانات زائفة خاصة</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="189"/>
@@ -621,32 +621,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="199"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - إضافة حشو للمحتوى</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="211"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - إعادة توليد المفتاح بعد مدة</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="223"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - مدة انتظار إعادة توليد المفتاح</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="235"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - الرفض بعد مدة</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="247"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - مدة انتظار إبقاء الاتصال</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="259"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - أقصى عدد لمحاولات المصافحة</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="276"/>
@@ -719,82 +719,82 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="131"/>
<source>Jc - Junk packet count</source>
<translation>Jc - Junk packet count</translation>
<translation>Jc - عدد الحزم غير المرغوب فيها</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="141"/>
<source>Jmin - Junk packet minimum size</source>
<translation>Jmin - Junk packet minimum size</translation>
<translation>Jmin - الحجم الادني للحزم الغير مرغوب فيها</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="151"/>
<source>Jmax - Junk packet maximum size</source>
<translation>Jmax - Junk packet maximum size</translation>
<translation>Jmax - الحجم الاقصي للحزم الغير مرغوب فيها</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="161"/>
<source>S1 - Init packet junk size</source>
<translation>S1 - Init packet junk size</translation>
<translation>S1 - حجم حزمة البيانات العشوائية الأولية</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="171"/>
<source>S2 - Response packet junk size</source>
<translation>S2 - Response packet junk size</translation>
<translation>S2 - حجم حزمة الاستجابة غير المرغوب فيها</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="183"/>
<source>S3 - Cookie reply packet junk size</source>
<translation>S3 - Cookie reply packet junk size</translation>
<translation>S3 - حجم البيانات الزائفة في حزمة رد الكوكيز</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="195"/>
<source>S4 - Transport packet junk size</source>
<translation>S4 - Transport packet junk size</translation>
<translation>S4 - حجم البيانات الزائفة في حزمة النقل</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="207"/>
<source>H1 - Init packet magic header</source>
<translation>H1 - Init packet magic header</translation>
<translation>H1 - حزمة رأس سحرية مبدئية</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="219"/>
<source>H2 - Response packet magic header</source>
<translation>H2 - Response packet magic header</translation>
<translation>H2 - رأس حزمة الاستجابة السحرية</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="231"/>
<source>H3 - Underload packet magic header</source>
<translation>H3 - Underload packet magic header</translation>
<translation>H3 - رأس حزمة السحر غير المحمل</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="243"/>
<source>H4 - Transport packet magic header</source>
<translation>H4 - Transport packet magic header</translation>
<translation>H4 - رأس حزمة النقل السحرية</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="253"/>
<source>I1 - Special junk 1</source>
<translation>I1 - Special junk 1</translation>
<translation>I1 - بيانات زائفة خاصة 1</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="263"/>
<source>I2 - Special junk 2</source>
<translation>I2 - Special junk 2</translation>
<translation>I2 - بيانات زائفة خاصة 2</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="273"/>
<source>I3 - Special junk 3</source>
<translation>I3 - Special junk 3</translation>
<translation>I3 - بيانات زائفة خاصة 3</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="283"/>
<source>I4 - Special junk 4</source>
<translation>I4 - Special junk 4</translation>
<translation>I4 - بيانات زائفة خاصة 4</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="293"/>
<source>I5 - Special junk 5</source>
<translation>I5 - Special junk 5</translation>
<translation>I5 - بيانات زائفة خاصة 5</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="310"/>
@@ -804,32 +804,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="326"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - إضافة حشو للمحتوى</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="339"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - إعادة توليد المفتاح بعد مدة</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="352"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - مدة انتظار إعادة توليد المفتاح</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="365"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - الرفض بعد مدة</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="378"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - مدة انتظار إبقاء الاتصال</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="391"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - أقصى عدد لمحاولات المصافحة</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="408"/>

View File

@@ -590,27 +590,27 @@ Se han encontrado contenedores ya instalados en el servidor. Todos ellos se han
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="132"/>
<source>I1 - First special junk packet</source>
<translation>I1 - First special junk packet</translation>
<translation>I1 - Primer paquete basura especial</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="142"/>
<source>I2 - Second special junk packet</source>
<translation>I2 - Second special junk packet</translation>
<translation>I2 - Segundo paquete basura especial</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="152"/>
<source>I3 - Third special junk packet</source>
<translation>I3 - Third special junk packet</translation>
<translation>I3 - Tercer paquete basura especial</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="162"/>
<source>I4 - Fourth special junk packet</source>
<translation>I4 - Fourth special junk packet</translation>
<translation>I4 - Cuarto paquete basura especial</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="172"/>
<source>I5 - Fifth special junk packet</source>
<translation>I5 - Fifth special junk packet</translation>
<translation>I5 - Quinto paquete basura especial</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="189"/>
@@ -620,32 +620,32 @@ Se han encontrado contenedores ya instalados en el servidor. Todos ellos se han
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="199"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - Relleno adicional de contenido</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="211"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - Renovar la clave tras un tiempo</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="223"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - Tiempo de espera de renovación de clave</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="235"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - Rechazar tras un tiempo</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="247"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - Tiempo de espera de keepalive</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="259"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - Número máximo de intentos de handshake</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="276"/>
@@ -718,82 +718,82 @@ Se han encontrado contenedores ya instalados en el servidor. Todos ellos se han
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="131"/>
<source>Jc - Junk packet count</source>
<translation>Jc - Junk packet count</translation>
<translation>Jc - Número de paquetes basura</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="141"/>
<source>Jmin - Junk packet minimum size</source>
<translation>Jmin - Junk packet minimum size</translation>
<translation>Jmin - Tamaño mínimo del paquete basura</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="151"/>
<source>Jmax - Junk packet maximum size</source>
<translation>Jmax - Junk packet maximum size</translation>
<translation>Jmax - Tamaño máximo del paquete basura</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="161"/>
<source>S1 - Init packet junk size</source>
<translation>S1 - Init packet junk size</translation>
<translation>S1 - Tamaño de basura del paquete de inicio</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="171"/>
<source>S2 - Response packet junk size</source>
<translation>S2 - Response packet junk size</translation>
<translation>S2 - Tamaño de basura del paquete de respuesta</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="183"/>
<source>S3 - Cookie reply packet junk size</source>
<translation>S3 - Cookie reply packet junk size</translation>
<translation>S3 - Tamaño de basura del paquete de respuesta de cookie</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="195"/>
<source>S4 - Transport packet junk size</source>
<translation>S4 - Transport packet junk size</translation>
<translation>S4 - Tamaño de basura del paquete de transporte</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="207"/>
<source>H1 - Init packet magic header</source>
<translation>H1 - Init packet magic header</translation>
<translation>H1 - Cabecera mágica del paquete de inicio</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="219"/>
<source>H2 - Response packet magic header</source>
<translation>H2 - Response packet magic header</translation>
<translation>H2 - Cabecera mágica del paquete de respuesta</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="231"/>
<source>H3 - Underload packet magic header</source>
<translation>H3 - Underload packet magic header</translation>
<translation>H3 - Cabecera mágica del paquete de subcarga</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="243"/>
<source>H4 - Transport packet magic header</source>
<translation>H4 - Transport packet magic header</translation>
<translation>H4 - Cabecera mágica del paquete de transporte</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="253"/>
<source>I1 - Special junk 1</source>
<translation>I1 - Special junk 1</translation>
<translation>I1 - Basura especial 1</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="263"/>
<source>I2 - Special junk 2</source>
<translation>I2 - Special junk 2</translation>
<translation>I2 - Basura especial 2</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="273"/>
<source>I3 - Special junk 3</source>
<translation>I3 - Special junk 3</translation>
<translation>I3 - Basura especial 3</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="283"/>
<source>I4 - Special junk 4</source>
<translation>I4 - Special junk 4</translation>
<translation>I4 - Basura especial 4</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="293"/>
<source>I5 - Special junk 5</source>
<translation>I5 - Special junk 5</translation>
<translation>I5 - Basura especial 5</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="310"/>
@@ -803,32 +803,32 @@ Se han encontrado contenedores ya instalados en el servidor. Todos ellos se han
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="326"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - Relleno adicional de contenido</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="339"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - Renovar la clave tras un tiempo</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="352"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - Tiempo de espera de renovación de clave</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="365"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - Rechazar tras un tiempo</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="378"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - Tiempo de espera de keepalive</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="391"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - Número máximo de intentos de handshake</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="408"/>

View File

@@ -590,27 +590,27 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="132"/>
<source>I1 - First special junk packet</source>
<translation>I1 - First special junk packet</translation>
<translation>I1 - نخستین بسته زائد ویژه</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="142"/>
<source>I2 - Second special junk packet</source>
<translation>I2 - Second special junk packet</translation>
<translation>I2 - دومین بسته زائد ویژه</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="152"/>
<source>I3 - Third special junk packet</source>
<translation>I3 - Third special junk packet</translation>
<translation>I3 - سومین بسته زائد ویژه</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="162"/>
<source>I4 - Fourth special junk packet</source>
<translation>I4 - Fourth special junk packet</translation>
<translation>I4 - چهارمین بسته زائد ویژه</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="172"/>
<source>I5 - Fifth special junk packet</source>
<translation>I5 - Fifth special junk packet</translation>
<translation>I5 - پنجمین بسته زائد ویژه</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="189"/>
@@ -620,32 +620,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="199"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - افزودن لایهگذاری محتوا</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="211"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - تولید دوباره کلید پس از زمان</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="223"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - زمان انتظار تولید دوباره کلید</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="235"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - رد کردن پس از زمان</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="247"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - زمان انتظار keepalive</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="259"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - بیشینه تلاشهای دستدهی</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="276"/>
@@ -718,82 +718,82 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="131"/>
<source>Jc - Junk packet count</source>
<translation>Jc - Junk packet count</translation>
<translation>Jc - تعداد بستههای زائد</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="141"/>
<source>Jmin - Junk packet minimum size</source>
<translation>Jmin - Junk packet minimum size</translation>
<translation>Jmin - کمینه اندازه بسته زائد</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="151"/>
<source>Jmax - Junk packet maximum size</source>
<translation>Jmax - Junk packet maximum size</translation>
<translation>Jmax - بیشینه اندازه بسته زائد</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="161"/>
<source>S1 - Init packet junk size</source>
<translation>S1 - Init packet junk size</translation>
<translation>S1 - اندازه داده زائد بسته آغازین</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="171"/>
<source>S2 - Response packet junk size</source>
<translation>S2 - Response packet junk size</translation>
<translation>S2 - اندازه داده زائد بسته پاسخ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="183"/>
<source>S3 - Cookie reply packet junk size</source>
<translation>S3 - Cookie reply packet junk size</translation>
<translation>S3 - اندازه داده زائد بسته پاسخ کوکی</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="195"/>
<source>S4 - Transport packet junk size</source>
<translation>S4 - Transport packet junk size</translation>
<translation>S4 - اندازه داده زائد بسته انتقال</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="207"/>
<source>H1 - Init packet magic header</source>
<translation>H1 - Init packet magic header</translation>
<translation>H1 - هدر جادویی بسته آغازین</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="219"/>
<source>H2 - Response packet magic header</source>
<translation>H2 - Response packet magic header</translation>
<translation>H2 - هدر جادویی بسته پاسخ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="231"/>
<source>H3 - Underload packet magic header</source>
<translation>H3 - Underload packet magic header</translation>
<translation>H3 - هدر جادویی بسته underload</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="243"/>
<source>H4 - Transport packet magic header</source>
<translation>H4 - Transport packet magic header</translation>
<translation>H4 - هدر جادویی بسته انتقال</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="253"/>
<source>I1 - Special junk 1</source>
<translation>I1 - Special junk 1</translation>
<translation>I1 - داده زائد ویژه 1</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="263"/>
<source>I2 - Special junk 2</source>
<translation>I2 - Special junk 2</translation>
<translation>I2 - داده زائد ویژه 2</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="273"/>
<source>I3 - Special junk 3</source>
<translation>I3 - Special junk 3</translation>
<translation>I3 - داده زائد ویژه 3</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="283"/>
<source>I4 - Special junk 4</source>
<translation>I4 - Special junk 4</translation>
<translation>I4 - داده زائد ویژه 4</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="293"/>
<source>I5 - Special junk 5</source>
<translation>I5 - Special junk 5</translation>
<translation>I5 - داده زائد ویژه 5</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="310"/>
@@ -803,32 +803,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="326"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - افزودن لایهگذاری محتوا</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="339"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - تولید دوباره کلید پس از زمان</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="352"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - زمان انتظار تولید دوباره کلید</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="365"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - رد کردن پس از زمان</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="378"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - زمان انتظار keepalive</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="391"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - بیشینه تلاشهای دستدهی</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="408"/>

View File

@@ -590,27 +590,27 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="132"/>
<source>I1 - First special junk packet</source>
<translation>I1 - First special junk packet</translation>
<translation>I1 - ि </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="142"/>
<source>I2 - Second special junk packet</source>
<translation>I2 - Second special junk packet</translation>
<translation>I2 - ि </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="152"/>
<source>I3 - Third special junk packet</source>
<translation>I3 - Third special junk packet</translation>
<translation>I3 - ि </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="162"/>
<source>I4 - Fourth special junk packet</source>
<translation>I4 - Fourth special junk packet</translation>
<translation>I4 - ि </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="172"/>
<source>I5 - Fifth special junk packet</source>
<translation>I5 - Fifth special junk packet</translation>
<translation>I5 - ि </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="189"/>
@@ -620,32 +620,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="199"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - ि </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="211"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="223"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="235"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="247"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="259"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - ि </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="276"/>
@@ -718,82 +718,82 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="131"/>
<source>Jc - Junk packet count</source>
<translation>Jc - Junk packet count</translation>
<translation>Jc - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="141"/>
<source>Jmin - Junk packet minimum size</source>
<translation>Jmin - Junk packet minimum size</translation>
<translation>Jmin - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="151"/>
<source>Jmax - Junk packet maximum size</source>
<translation>Jmax - Junk packet maximum size</translation>
<translation>Jmax - ि </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="161"/>
<source>S1 - Init packet junk size</source>
<translation>S1 - Init packet junk size</translation>
<translation>S1 - init </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="171"/>
<source>S2 - Response packet junk size</source>
<translation>S2 - Response packet junk size</translation>
<translation>S2 - िि </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="183"/>
<source>S3 - Cookie reply packet junk size</source>
<translation>S3 - Cookie reply packet junk size</translation>
<translation>S3 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="195"/>
<source>S4 - Transport packet junk size</source>
<translation>S4 - Transport packet junk size</translation>
<translation>S4 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="207"/>
<source>H1 - Init packet magic header</source>
<translation>H1 - Init packet magic header</translation>
<translation>H1 - init ि </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="219"/>
<source>H2 - Response packet magic header</source>
<translation>H2 - Response packet magic header</translation>
<translation>H2 - िि ि </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="231"/>
<source>H3 - Underload packet magic header</source>
<translation>H3 - Underload packet magic header</translation>
<translation>H3 - underload ि </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="243"/>
<source>H4 - Transport packet magic header</source>
<translation>H4 - Transport packet magic header</translation>
<translation>H4 - ि </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="253"/>
<source>I1 - Special junk 1</source>
<translation>I1 - Special junk 1</translation>
<translation>I1 - ि 1</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="263"/>
<source>I2 - Special junk 2</source>
<translation>I2 - Special junk 2</translation>
<translation>I2 - ि 2</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="273"/>
<source>I3 - Special junk 3</source>
<translation>I3 - Special junk 3</translation>
<translation>I3 - ि 3</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="283"/>
<source>I4 - Special junk 4</source>
<translation>I4 - Special junk 4</translation>
<translation>I4 - ि 4</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="293"/>
<source>I5 - Special junk 5</source>
<translation>I5 - Special junk 5</translation>
<translation>I5 - ि 5</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="310"/>
@@ -803,32 +803,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="326"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - ि </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="339"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="352"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="365"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="378"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="391"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - ि </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="408"/>

View File

@@ -590,27 +590,27 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="132"/>
<source>I1 - First special junk packet</source>
<translation>I1 - First special junk packet</translation>
<translation>I1 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="142"/>
<source>I2 - Second special junk packet</source>
<translation>I2 - Second special junk packet</translation>
<translation>I2 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="152"/>
<source>I3 - Third special junk packet</source>
<translation>I3 - Third special junk packet</translation>
<translation>I3 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="162"/>
<source>I4 - Fourth special junk packet</source>
<translation>I4 - Fourth special junk packet</translation>
<translation>I4 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="172"/>
<source>I5 - Fifth special junk packet</source>
<translation>I5 - Fifth special junk packet</translation>
<translation>I5 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="189"/>
@@ -620,32 +620,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="199"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="211"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="223"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="235"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="247"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="259"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="276"/>
@@ -718,82 +718,82 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="131"/>
<source>Jc - Junk packet count</source>
<translation>Jc - Junk packet count</translation>
<translation>Jc - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="141"/>
<source>Jmin - Junk packet minimum size</source>
<translation>Jmin - Junk packet minimum size</translation>
<translation>Jmin - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="151"/>
<source>Jmax - Junk packet maximum size</source>
<translation>Jmax - Junk packet maximum size</translation>
<translation>Jmax - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="161"/>
<source>S1 - Init packet junk size</source>
<translation>S1 - Init packet junk size</translation>
<translation>S1 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="171"/>
<source>S2 - Response packet junk size</source>
<translation>S2 - Response packet junk size</translation>
<translation>S2 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="183"/>
<source>S3 - Cookie reply packet junk size</source>
<translation>S3 - Cookie reply packet junk size</translation>
<translation>S3 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="195"/>
<source>S4 - Transport packet junk size</source>
<translation>S4 - Transport packet junk size</translation>
<translation>S4 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="207"/>
<source>H1 - Init packet magic header</source>
<translation>H1 - Init packet magic header</translation>
<translation>H1 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="219"/>
<source>H2 - Response packet magic header</source>
<translation>H2 - Response packet magic header</translation>
<translation>H2 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="231"/>
<source>H3 - Underload packet magic header</source>
<translation>H3 - Underload packet magic header</translation>
<translation>H3 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="243"/>
<source>H4 - Transport packet magic header</source>
<translation>H4 - Transport packet magic header</translation>
<translation>H4 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="253"/>
<source>I1 - Special junk 1</source>
<translation>I1 - Special junk 1</translation>
<translation>I1 - 1</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="263"/>
<source>I2 - Special junk 2</source>
<translation>I2 - Special junk 2</translation>
<translation>I2 - 2</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="273"/>
<source>I3 - Special junk 3</source>
<translation>I3 - Special junk 3</translation>
<translation>I3 - 3</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="283"/>
<source>I4 - Special junk 4</source>
<translation>I4 - Special junk 4</translation>
<translation>I4 - 4</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="293"/>
<source>I5 - Special junk 5</source>
<translation>I5 - Special junk 5</translation>
<translation>I5 - 5</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="310"/>
@@ -803,32 +803,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="326"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="339"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="352"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="365"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="378"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="391"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="408"/>

View File

@@ -590,27 +590,27 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="132"/>
<source>I1 - First special junk packet</source>
<translation>I1 - First special junk packet</translation>
<translation>I1 - junk packet</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="142"/>
<source>I2 - Second special junk packet</source>
<translation>I2 - Second special junk packet</translation>
<translation>I2 - junk packet</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="152"/>
<source>I3 - Third special junk packet</source>
<translation>I3 - Third special junk packet</translation>
<translation>I3 - junk packet</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="162"/>
<source>I4 - Fourth special junk packet</source>
<translation>I4 - Fourth special junk packet</translation>
<translation>I4 - junk packet</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="172"/>
<source>I5 - Fifth special junk packet</source>
<translation>I5 - Fifth special junk packet</translation>
<translation>I5 - junk packet</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="189"/>
@@ -620,32 +620,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="199"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - က padding </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="211"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - က </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="223"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - က</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="235"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - က </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="247"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - keepalive က</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="259"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - handshake က </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="276"/>
@@ -718,82 +718,82 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="131"/>
<source>Jc - Junk packet count</source>
<translation>Jc - Junk packet count</translation>
<translation>Jc - Junk packet က</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="141"/>
<source>Jmin - Junk packet minimum size</source>
<translation>Jmin - Junk packet minimum size</translation>
<translation>Jmin - Junk packet က</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="151"/>
<source>Jmax - Junk packet maximum size</source>
<translation>Jmax - Junk packet maximum size</translation>
<translation>Jmax - Junk packet ကက</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="161"/>
<source>S1 - Init packet junk size</source>
<translation>S1 - Init packet junk size</translation>
<translation>S1 - Init packet junk </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="171"/>
<source>S2 - Response packet junk size</source>
<translation>S2 - Response packet junk size</translation>
<translation>S2 - Response packet junk </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="183"/>
<source>S3 - Cookie reply packet junk size</source>
<translation>S3 - Cookie reply packet junk size</translation>
<translation>S3 - cookie reply packet junk </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="195"/>
<source>S4 - Transport packet junk size</source>
<translation>S4 - Transport packet junk size</translation>
<translation>S4 - transport packet junk </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="207"/>
<source>H1 - Init packet magic header</source>
<translation>H1 - Init packet magic header</translation>
<translation>H1 - init packet magic header</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="219"/>
<source>H2 - Response packet magic header</source>
<translation>H2 - Response packet magic header</translation>
<translation>H2 - က packet magic header</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="231"/>
<source>H3 - Underload packet magic header</source>
<translation>H3 - Underload packet magic header</translation>
<translation>H3 - underload packet magic header</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="243"/>
<source>H4 - Transport packet magic header</source>
<translation>H4 - Transport packet magic header</translation>
<translation>H4 - transport packet magic header</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="253"/>
<source>I1 - Special junk 1</source>
<translation>I1 - Special junk 1</translation>
<translation>I1 - junk 1</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="263"/>
<source>I2 - Special junk 2</source>
<translation>I2 - Special junk 2</translation>
<translation>I2 - junk 2</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="273"/>
<source>I3 - Special junk 3</source>
<translation>I3 - Special junk 3</translation>
<translation>I3 - junk 3</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="283"/>
<source>I4 - Special junk 4</source>
<translation>I4 - Special junk 4</translation>
<translation>I4 - junk 4</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="293"/>
<source>I5 - Special junk 5</source>
<translation>I5 - Special junk 5</translation>
<translation>I5 - junk 5</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="310"/>
@@ -803,32 +803,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="326"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - က padding </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="339"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - က </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="352"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - က</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="365"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - က </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="378"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - keepalive က</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="391"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - handshake က </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="408"/>

View File

@@ -591,27 +591,27 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="132"/>
<source>I1 - First special junk packet</source>
<translation>I1 - First special junk packet</translation>
<translation>I1 - первый специальный мусорный пакет</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="142"/>
<source>I2 - Second special junk packet</source>
<translation>I2 - Second special junk packet</translation>
<translation>I2 - второй специальный мусорный пакет</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="152"/>
<source>I3 - Third special junk packet</source>
<translation>I3 - Third special junk packet</translation>
<translation>I3 - третий специальный мусорный пакет</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="162"/>
<source>I4 - Fourth special junk packet</source>
<translation>I4 - Fourth special junk packet</translation>
<translation>I4 - четвёртый специальный мусорный пакет</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="172"/>
<source>I5 - Fifth special junk packet</source>
<translation>I5 - Fifth special junk packet</translation>
<translation>I5 - пятый специальный мусорный пакет</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="189"/>
@@ -621,32 +621,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="199"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - добавление заполнения содержимого</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="211"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - смена ключа по истечении времени</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="223"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - тайм-аут смены ключа</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="235"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - отклонение по истечении времени</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="247"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - тайм-аут keepalive</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="259"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - максимум попыток рукопожатия</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="276"/>
@@ -719,82 +719,82 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="131"/>
<source>Jc - Junk packet count</source>
<translation>Jc - Junk packet count</translation>
<translation>Jc - количество мусорных пакетов</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="141"/>
<source>Jmin - Junk packet minimum size</source>
<translation>Jmin - Junk packet minimum size</translation>
<translation>Jmin - минимальный размер мусорного пакета</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="151"/>
<source>Jmax - Junk packet maximum size</source>
<translation>Jmax - Junk packet maximum size</translation>
<translation>Jmax - максимальный размер мусорного пакета</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="161"/>
<source>S1 - Init packet junk size</source>
<translation>S1 - Init packet junk size</translation>
<translation>S1 - размер мусора в пакете инициализации</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="171"/>
<source>S2 - Response packet junk size</source>
<translation>S2 - Response packet junk size</translation>
<translation>S2 - размер мусора в пакете ответа</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="183"/>
<source>S3 - Cookie reply packet junk size</source>
<translation>S3 - Cookie reply packet junk size</translation>
<translation>S3 - размер мусора в пакете cookie reply</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="195"/>
<source>S4 - Transport packet junk size</source>
<translation>S4 - Transport packet junk size</translation>
<translation>S4 - размер мусора в транспортном пакете</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="207"/>
<source>H1 - Init packet magic header</source>
<translation>H1 - Init packet magic header</translation>
<translation>H1 - магический заголовок пакета инициализации</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="219"/>
<source>H2 - Response packet magic header</source>
<translation>H2 - Response packet magic header</translation>
<translation>H2 - магический заголовок пакета ответа</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="231"/>
<source>H3 - Underload packet magic header</source>
<translation>H3 - Underload packet magic header</translation>
<translation>H3 - магический заголовок пакета underload</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="243"/>
<source>H4 - Transport packet magic header</source>
<translation>H4 - Transport packet magic header</translation>
<translation>H4 - магический заголовок транспортного пакета</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="253"/>
<source>I1 - Special junk 1</source>
<translation>I1 - Special junk 1</translation>
<translation>I1 - специальный мусорный пакет 1</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="263"/>
<source>I2 - Special junk 2</source>
<translation>I2 - Special junk 2</translation>
<translation>I2 - специальный мусорный пакет 2</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="273"/>
<source>I3 - Special junk 3</source>
<translation>I3 - Special junk 3</translation>
<translation>I3 - специальный мусорный пакет 3</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="283"/>
<source>I4 - Special junk 4</source>
<translation>I4 - Special junk 4</translation>
<translation>I4 - специальный мусорный пакет 4</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="293"/>
<source>I5 - Special junk 5</source>
<translation>I5 - Special junk 5</translation>
<translation>I5 - специальный мусорный пакет 5</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="310"/>
@@ -804,32 +804,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="326"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - добавление заполнения содержимого</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="339"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - смена ключа по истечении времени</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="352"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - тайм-аут смены ключа</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="365"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - отклонение по истечении времени</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="378"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - тайм-аут keepalive</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="391"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - максимум попыток рукопожатия</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="408"/>

View File

@@ -590,27 +590,27 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="132"/>
<source>I1 - First special junk packet</source>
<translation>I1 - First special junk packet</translation>
<translation>I1 - перший спеціальний сміттєвий пакет</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="142"/>
<source>I2 - Second special junk packet</source>
<translation>I2 - Second special junk packet</translation>
<translation>I2 - другий спеціальний сміттєвий пакет</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="152"/>
<source>I3 - Third special junk packet</source>
<translation>I3 - Third special junk packet</translation>
<translation>I3 - третій спеціальний сміттєвий пакет</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="162"/>
<source>I4 - Fourth special junk packet</source>
<translation>I4 - Fourth special junk packet</translation>
<translation>I4 - четвертий спеціальний сміттєвий пакет</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="172"/>
<source>I5 - Fifth special junk packet</source>
<translation>I5 - Fifth special junk packet</translation>
<translation>I5 - п&apos;ятий спеціальний сміттєвий пакет</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="189"/>
@@ -620,32 +620,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="199"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - додавання заповнення вмісту</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="211"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - зміна ключа після часу</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="223"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - тайм-аут зміни ключа</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="235"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - відхилення після часу</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="247"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - тайм-аут keepalive</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="259"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - максимум спроб рукостискання</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="276"/>
@@ -718,82 +718,82 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="131"/>
<source>Jc - Junk packet count</source>
<translation>Jc - Junk packet count</translation>
<translation>Jc - кількість сміттєвих пакетів</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="141"/>
<source>Jmin - Junk packet minimum size</source>
<translation>Jmin - Junk packet minimum size</translation>
<translation>Jmin - мінімальний розмір сміттєвого пакета</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="151"/>
<source>Jmax - Junk packet maximum size</source>
<translation>Jmax - Junk packet maximum size</translation>
<translation>Jmax - максимальний розмір сміттєвого пакета</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="161"/>
<source>S1 - Init packet junk size</source>
<translation>S1 - Init packet junk size</translation>
<translation>S1 - розмір смiття в пакеті ініціалізації</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="171"/>
<source>S2 - Response packet junk size</source>
<translation>S2 - Response packet junk size</translation>
<translation>S2 - розмір смiття в пакеті відповіді</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="183"/>
<source>S3 - Cookie reply packet junk size</source>
<translation>S3 - Cookie reply packet junk size</translation>
<translation>S3 - розмір смiття в пакеті cookie reply</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="195"/>
<source>S4 - Transport packet junk size</source>
<translation>S4 - Transport packet junk size</translation>
<translation>S4 - розмір смiття в транспортному пакеті</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="207"/>
<source>H1 - Init packet magic header</source>
<translation>H1 - Init packet magic header</translation>
<translation>H1 - магічний заголовок пакета ініціалізації</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="219"/>
<source>H2 - Response packet magic header</source>
<translation>H2 - Response packet magic header</translation>
<translation>H2 - магічний заголовок пакета відповіді</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="231"/>
<source>H3 - Underload packet magic header</source>
<translation>H3 - Underload packet magic header</translation>
<translation>H3 - магічний заголовок пакета underload</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="243"/>
<source>H4 - Transport packet magic header</source>
<translation>H4 - Transport packet magic header</translation>
<translation>H4 - магічний заголовок транспортного пакета</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="253"/>
<source>I1 - Special junk 1</source>
<translation>I1 - Special junk 1</translation>
<translation>I1 - спеціальний сміттєвий пакет 1</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="263"/>
<source>I2 - Special junk 2</source>
<translation>I2 - Special junk 2</translation>
<translation>I2 - спеціальний сміттєвий пакет 2</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="273"/>
<source>I3 - Special junk 3</source>
<translation>I3 - Special junk 3</translation>
<translation>I3 - спеціальний сміттєвий пакет 3</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="283"/>
<source>I4 - Special junk 4</source>
<translation>I4 - Special junk 4</translation>
<translation>I4 - спеціальний сміттєвий пакет 4</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="293"/>
<source>I5 - Special junk 5</source>
<translation>I5 - Special junk 5</translation>
<translation>I5 - спеціальний сміттєвий пакет 5</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="310"/>
@@ -803,32 +803,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="326"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - додавання заповнення вмісту</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="339"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - зміна ключа після часу</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="352"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - тайм-аут зміни ключа</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="365"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - відхилення після часу</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="378"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - тайм-аут keepalive</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="391"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - максимум спроб рукостискання</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="408"/>

View File

@@ -590,27 +590,27 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="132"/>
<source>I1 - First special junk packet</source>
<translation>I1 - First special junk packet</translation>
<translation>I1 - پہلا خصوصی جنک پیکٹ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="142"/>
<source>I2 - Second special junk packet</source>
<translation>I2 - Second special junk packet</translation>
<translation>I2 - دوسرا خصوصی جنک پیکٹ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="152"/>
<source>I3 - Third special junk packet</source>
<translation>I3 - Third special junk packet</translation>
<translation>I3 - تیسرا خصوصی جنک پیکٹ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="162"/>
<source>I4 - Fourth special junk packet</source>
<translation>I4 - Fourth special junk packet</translation>
<translation>I4 - چوتھا خصوصی جنک پیکٹ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="172"/>
<source>I5 - Fifth special junk packet</source>
<translation>I5 - Fifth special junk packet</translation>
<translation>I5 - پانچواں خصوصی جنک پیکٹ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="189"/>
@@ -620,32 +620,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="199"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - کنٹینٹ پیڈنگ اضافہ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="211"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - وقت کے بعد ری کی</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="223"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - ری کی ٹائم آؤٹ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="235"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - وقت کے بعد رد</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="247"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - کیپ الائیو ٹائم آؤٹ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="259"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - زیادہ سے زیادہ ہینڈشیک کوششیں</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="276"/>
@@ -718,82 +718,82 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="131"/>
<source>Jc - Junk packet count</source>
<translation>Jc - Junk packet count</translation>
<translation>Jc - جنک پیکٹ کی تعداد</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="141"/>
<source>Jmin - Junk packet minimum size</source>
<translation>Jmin - Junk packet minimum size</translation>
<translation>Jmin - جنک پیکٹ کا کم از کم سائز</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="151"/>
<source>Jmax - Junk packet maximum size</source>
<translation>Jmax - Junk packet maximum size</translation>
<translation>Jmax - جنک پیکٹ کا زیادہ سے زیادہ سائز</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="161"/>
<source>S1 - Init packet junk size</source>
<translation>S1 - Init packet junk size</translation>
<translation>S1 - init پیکٹ جنک سائز</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="171"/>
<source>S2 - Response packet junk size</source>
<translation>S2 - Response packet junk size</translation>
<translation>S2 - جوابی پیکٹ جنک سائز</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="183"/>
<source>S3 - Cookie reply packet junk size</source>
<translation>S3 - Cookie reply packet junk size</translation>
<translation>S3 - کوکی جوابی پیکٹ جنک سائز</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="195"/>
<source>S4 - Transport packet junk size</source>
<translation>S4 - Transport packet junk size</translation>
<translation>S4 - ٹرانسپورٹ پیکٹ جنک سائز</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="207"/>
<source>H1 - Init packet magic header</source>
<translation>H1 - Init packet magic header</translation>
<translation>H1 - init پیکٹ میجک ہیڈر</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="219"/>
<source>H2 - Response packet magic header</source>
<translation>H2 - Response packet magic header</translation>
<translation>H2 - جوابی پیکٹ میجک ہیڈر</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="231"/>
<source>H3 - Underload packet magic header</source>
<translation>H3 - Underload packet magic header</translation>
<translation>H3 - underload پیکٹ میجک ہیڈر</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="243"/>
<source>H4 - Transport packet magic header</source>
<translation>H4 - Transport packet magic header</translation>
<translation>H4 - ٹرانسپورٹ پیکٹ میجک ہیڈر</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="253"/>
<source>I1 - Special junk 1</source>
<translation>I1 - Special junk 1</translation>
<translation>I1 - خصوصی جنک 1</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="263"/>
<source>I2 - Special junk 2</source>
<translation>I2 - Special junk 2</translation>
<translation>I2 - خصوصی جنک 2</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="273"/>
<source>I3 - Special junk 3</source>
<translation>I3 - Special junk 3</translation>
<translation>I3 - خصوصی جنک 3</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="283"/>
<source>I4 - Special junk 4</source>
<translation>I4 - Special junk 4</translation>
<translation>I4 - خصوصی جنک 4</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="293"/>
<source>I5 - Special junk 5</source>
<translation>I5 - Special junk 5</translation>
<translation>I5 - خصوصی جنک 5</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="310"/>
@@ -803,32 +803,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="326"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - کنٹینٹ پیڈنگ اضافہ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="339"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - وقت کے بعد ری کی</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="352"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - ری کی ٹائم آؤٹ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="365"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - وقت کے بعد رد</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="378"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - کیپ الائیو ٹائم آؤٹ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="391"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - زیادہ سے زیادہ ہینڈشیک کوششیں</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="408"/>

View File

@@ -590,27 +590,27 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="132"/>
<source>I1 - First special junk packet</source>
<translation>I1 - First special junk packet</translation>
<translation>I1 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="142"/>
<source>I2 - Second special junk packet</source>
<translation>I2 - Second special junk packet</translation>
<translation>I2 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="152"/>
<source>I3 - Third special junk packet</source>
<translation>I3 - Third special junk packet</translation>
<translation>I3 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="162"/>
<source>I4 - Fourth special junk packet</source>
<translation>I4 - Fourth special junk packet</translation>
<translation>I4 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="172"/>
<source>I5 - Fifth special junk packet</source>
<translation>I5 - Fifth special junk packet</translation>
<translation>I5 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="189"/>
@@ -620,32 +620,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="199"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="211"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="223"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="235"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="247"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - Keepalive </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="259"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="276"/>
@@ -718,82 +718,82 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="131"/>
<source>Jc - Junk packet count</source>
<translation>Jc - Junk packet count</translation>
<translation>Jc - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="141"/>
<source>Jmin - Junk packet minimum size</source>
<translation>Jmin - Junk packet minimum size</translation>
<translation>Jmin - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="151"/>
<source>Jmax - Junk packet maximum size</source>
<translation>Jmax - Junk packet maximum size</translation>
<translation>Jmax - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="161"/>
<source>S1 - Init packet junk size</source>
<translation>S1 - Init packet junk size</translation>
<translation>S1 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="171"/>
<source>S2 - Response packet junk size</source>
<translation>S2 - Response packet junk size</translation>
<translation>S2 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="183"/>
<source>S3 - Cookie reply packet junk size</source>
<translation>S3 - Cookie reply packet junk size</translation>
<translation>S3 - Cookie </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="195"/>
<source>S4 - Transport packet junk size</source>
<translation>S4 - Transport packet junk size</translation>
<translation>S4 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="207"/>
<source>H1 - Init packet magic header</source>
<translation>H1 - Init packet magic header</translation>
<translation>H1 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="219"/>
<source>H2 - Response packet magic header</source>
<translation>H2 - Response packet magic header</translation>
<translation>H2 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="231"/>
<source>H3 - Underload packet magic header</source>
<translation>H3 - Underload packet magic header</translation>
<translation>H3 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="243"/>
<source>H4 - Transport packet magic header</source>
<translation>H4 - Transport packet magic header</translation>
<translation>H4 - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="253"/>
<source>I1 - Special junk 1</source>
<translation>I1 - Special junk 1</translation>
<translation>I1 - 1</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="263"/>
<source>I2 - Special junk 2</source>
<translation>I2 - Special junk 2</translation>
<translation>I2 - 2</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="273"/>
<source>I3 - Special junk 3</source>
<translation>I3 - Special junk 3</translation>
<translation>I3 - 3</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="283"/>
<source>I4 - Special junk 4</source>
<translation>I4 - Special junk 4</translation>
<translation>I4 - 4</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="293"/>
<source>I5 - Special junk 5</source>
<translation>I5 - Special junk 5</translation>
<translation>I5 - 5</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="310"/>
@@ -803,32 +803,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="326"/>
<source>ContentPaddingAddition - Content padding addition</source>
<translation>ContentPaddingAddition - Content padding addition</translation>
<translation>ContentPaddingAddition - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="339"/>
<source>RekeyAfterTime - Rekey after time</source>
<translation>RekeyAfterTime - Rekey after time</translation>
<translation>RekeyAfterTime - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="352"/>
<source>RekeyTimeout - Rekey timeout</source>
<translation>RekeyTimeout - Rekey timeout</translation>
<translation>RekeyTimeout - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="365"/>
<source>RejectAfterTime - Reject after time</source>
<translation>RejectAfterTime - Reject after time</translation>
<translation>RejectAfterTime - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="378"/>
<source>KeepaliveTimeout - Keepalive timeout</source>
<translation>KeepaliveTimeout - Keepalive timeout</translation>
<translation>KeepaliveTimeout - Keepalive </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="391"/>
<source>MaxHandshakeAttempts - Max handshake attempts</source>
<translation>MaxHandshakeAttempts - Max handshake attempts</translation>
<translation>MaxHandshakeAttempts - </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="408"/>

View File

@@ -8,6 +8,16 @@
namespace {
Logger logger("FocusController");
QString rootObjectName(const QObject *object)
{
if (object == nullptr) {
return QStringLiteral("null");
}
const QString className = QString::fromLatin1(object->metaObject()->className());
const QString name = object->objectName();
return name.isEmpty() ? className : className + QLatin1Char('/') + name;
}
}
FocusController::FocusController(QQmlApplicationEngine *engine, QObject *parent)
@@ -76,6 +86,7 @@ void FocusController::setFocusOnDefaultItem()
void FocusController::pushRootObject(QObject *object)
{
m_rootObjects.push(object);
logger.debug() << "pushRootObject:" << rootObjectName(object) << "depth=" << m_rootObjects.size();
dropListView();
// setFocusOnDefaultItem();
}
@@ -83,20 +94,29 @@ void FocusController::pushRootObject(QObject *object)
void FocusController::dropRootObject(QObject *object)
{
if (m_rootObjects.empty()) {
logger.warning() << "dropRootObject: the stack is already empty, this drop had no matching push, object="
<< rootObjectName(object);
return;
}
if (m_rootObjects.top() == object) {
m_rootObjects.pop();
logger.debug() << "dropRootObject:" << rootObjectName(object) << "depth=" << m_rootObjects.size();
dropListView();
setFocusOnDefaultItem();
} else {
logger.warning() << "TRY TO DROP WRONG ROOT OBJECT: " << m_rootObjects.top() << " SHOULD BE: " << object;
logger.warning() << "TRY TO DROP WRONG ROOT OBJECT: top=" << rootObjectName(m_rootObjects.top())
<< "SHOULD BE:" << rootObjectName(object) << "depth=" << m_rootObjects.size()
<< ", the stack is left as is and stays out of sync";
}
}
void FocusController::resetRootObject()
{
if (!m_rootObjects.empty()) {
logger.debug() << "resetRootObject: dropping" << m_rootObjects.size() << "objects, top="
<< rootObjectName(m_rootObjects.top());
}
m_rootObjects.clear();
}

View File

@@ -35,6 +35,11 @@
#include "core/models/protocols/wireGuardProtocolConfig.h"
#include "core/models/protocols/openVpnProtocolConfig.h"
#include "core/models/protocols/xrayProtocolConfig.h"
#include "logger.h"
namespace {
Logger logger("InstallUiController");
}
InstallUiController::InstallUiController(InstallController *installController,
ServersController *serversController,
@@ -306,7 +311,19 @@ void InstallUiController::updateServerConfig(const QString &serverId, int contai
const bool asyncUpdate = container == DockerContainer::MtProxy || container == DockerContainer::Telemt
|| container == DockerContainer::Xray || container == DockerContainer::SSXray;
if (asyncUpdate) {
if (m_serverConfigUpdateInProgress) {
if (container == DockerContainer::Xray || container == DockerContainer::SSXray) {
logger.warning() << "Xray save rejected, a save is already running";
} else {
logger.warning() << "InstallUiController: server config save rejected for container"
<< ContainerUtils::containerToString(container) << ", an update is already running";
}
return;
}
m_serverConfigUpdateInProgress = true;
const bool emitBusy = container == DockerContainer::MtProxy || container == DockerContainer::Telemt;
if (emitBusy)
emit serverIsBusy(true);
@@ -316,6 +333,7 @@ void InstallUiController::updateServerConfig(const QString &serverId, int contai
[this, watcher, serverId, container, closePage, protocolTypeCopy, emitBusy]() {
const ErrorCode errorCode = watcher->result();
watcher->deleteLater();
m_serverConfigUpdateInProgress = false;
if (emitBusy)
emit serverIsBusy(false);
@@ -326,6 +344,9 @@ void InstallUiController::updateServerConfig(const QString &serverId, int contai
updateProtocolConfigModel(serverId, static_cast<int>(container), static_cast<int>(protocolTypeCopy));
emit updateContainerFinished(tr("Settings updated successfully"), closePage);
} else {
logger.error() << "InstallUiController: async server config save failed for container"
<< ContainerUtils::containerToString(container) << ", error="
<< static_cast<int>(errorCode);
emit installationErrorOccurred(errorCode);
}
});
@@ -352,6 +373,8 @@ void InstallUiController::updateServerConfig(const QString &serverId, int contai
return;
}
logger.error() << "InstallUiController: server config save failed for container"
<< ContainerUtils::containerToString(container) << ", error=" << static_cast<int>(errorCode);
emit installationErrorOccurred(errorCode);
}

View File

@@ -160,7 +160,9 @@ private:
ServerCredentials m_processedServerCredentials;
QString m_privateKeyPassphrase;
bool m_serverConfigUpdateInProgress = false;
void updateProtocolConfigModel(const QString &serverId, int containerIndex, int protocolIndex);
bool buildContainerConfigFromModel(int containerIndex, int protocolIndex, ContainerConfig &containerConfig);

View File

@@ -13,14 +13,6 @@
using namespace amnezia;
using namespace ProtocolUtils;
namespace
{
QString awgToggleValue(bool enabled)
{
return enabled ? QString(protocols::awg::awgBoolOn) : QString();
}
} // namespace
AwgConfigModel::AwgConfigModel(QObject *parent) : QAbstractListModel(parent)
{
}
@@ -58,11 +50,9 @@ bool AwgConfigModel::setData(const QModelIndex &index, const QVariant &value, in
case Roles::ClientRejectAfterTimeRole: m_protocolConfig.clientConfig->rejectAfterTime = strValue; break;
case Roles::ClientKeepaliveTimeoutRole: m_protocolConfig.clientConfig->keepaliveTimeout = strValue; break;
case Roles::ClientMaxHandshakeAttemptsRole: m_protocolConfig.clientConfig->maxHandshakeAttempts = strValue; break;
case Roles::ClientRandomTrailersRole:
m_protocolConfig.clientConfig->randomTrailers = awgToggleValue(value.toBool());
break;
case Roles::ClientDisableCookiesRole:
m_protocolConfig.clientConfig->disableCookies = awgToggleValue(value.toBool());
m_protocolConfig.clientConfig->disableCookies =
value.toBool() ? QString(protocols::awg::awgBoolOn) : QString(protocols::awg::awgBoolOff);
break;
case Roles::ServerJunkPacketCountRole: m_protocolConfig.serverConfig.junkPacketCount = strValue; break;
case Roles::ServerJunkPacketMinSizeRole: m_protocolConfig.serverConfig.junkPacketMinSize = strValue; break;
@@ -99,10 +89,12 @@ bool AwgConfigModel::setData(const QModelIndex &index, const QVariant &value, in
break;
}
case Roles::ServerRandomTrailersRole:
m_protocolConfig.serverConfig.randomTrailers = awgToggleValue(value.toBool());
m_protocolConfig.serverConfig.randomTrailers =
value.toBool() ? QString(protocols::awg::awgBoolOn) : QString(protocols::awg::awgBoolOff);
break;
case Roles::ServerDisableCookiesRole:
m_protocolConfig.serverConfig.disableCookies = awgToggleValue(value.toBool());
m_protocolConfig.serverConfig.disableCookies =
value.toBool() ? QString(protocols::awg::awgBoolOn) : QString(protocols::awg::awgBoolOff);
break;
default:
return false;

View File

@@ -5,6 +5,7 @@
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
#include "core/utils/networkUtilities.h"
#include "core/utils/containers/containerUtils.h"
#include <QHostAddress>
#include <QRegularExpression>
@@ -34,10 +35,11 @@ bool XrayConfigModel::setData(const QModelIndex& index, const QVariant& value, i
const bool wasUnsavedChanges = hasUnsavedChanges();
auto& srv = m_protocolConfig.serverConfig;
auto& tpl = m_protocolConfig.clientTemplate;
auto& xhttp = srv.xhttp;
auto& mkcp = srv.mkcp;
auto& pad = xhttp.xPadding;
auto& mux = xhttp.xmux;
auto& mux = tpl.xmux;
QString str = value.toString();
@@ -56,9 +58,11 @@ bool XrayConfigModel::setData(const QModelIndex& index, const QVariant& value, i
break;
// ── Security ──────────────────────────────────────────────────────
case Roles::FingerprintRole: srv.fingerprint = str;
case Roles::FingerprintRole: tpl.fingerprint = str;
break;
case Roles::SniRole: srv.sni = str;
case Roles::SniRole:
srv.sni = str;
srv.site = str;
break;
case Roles::AlpnRole: srv.alpn = str;
break;
@@ -70,7 +74,7 @@ bool XrayConfigModel::setData(const QModelIndex& index, const QVariant& value, i
break;
case Roles::XhttpPathRole: xhttp.path = str;
break;
case Roles::XhttpUplinkMethodRole: xhttp.uplinkMethod = str;
case Roles::XhttpUplinkMethodRole: tpl.uplinkMethod = str;
break;
case Roles::XhttpDisableGrpcRole: xhttp.disableGrpc = value.toBool();
break;
@@ -90,7 +94,7 @@ bool XrayConfigModel::setData(const QModelIndex& index, const QVariant& value, i
case Roles::XhttpUplinkDataKeyRole: xhttp.uplinkDataKey = str;
break;
case Roles::XhttpUplinkChunkSizeRole: xhttp.uplinkChunkSize = str;
case Roles::XhttpUplinkChunkSizeRole: tpl.uplinkChunkSize = str;
break;
case Roles::XhttpScMaxBufferedPostsRole: xhttp.scMaxBufferedPosts = str;
break;
@@ -98,9 +102,9 @@ bool XrayConfigModel::setData(const QModelIndex& index, const QVariant& value, i
break;
case Roles::XhttpScMaxEachPostBytesMaxRole: xhttp.scMaxEachPostBytesMax = str;
break;
case Roles::XhttpScMinPostsIntervalMsMinRole: xhttp.scMinPostsIntervalMsMin = str;
case Roles::XhttpScMinPostsIntervalMsMinRole: tpl.scMinPostsIntervalMsMin = str;
break;
case Roles::XhttpScMinPostsIntervalMsMaxRole: xhttp.scMinPostsIntervalMsMax = str;
case Roles::XhttpScMinPostsIntervalMsMaxRole: tpl.scMinPostsIntervalMsMax = str;
break;
case Roles::XhttpScStreamUpServerSecsMinRole: xhttp.scStreamUpServerSecsMin = str;
break;
@@ -114,11 +118,11 @@ bool XrayConfigModel::setData(const QModelIndex& index, const QVariant& value, i
break;
case Roles::MkcpDownlinkCapacityRole: mkcp.downlinkCapacity = str;
break;
case Roles::MkcpReadBufferSizeRole: mkcp.readBufferSize = str;
case Roles::MkcpMtuRole: mkcp.mtu = str;
break;
case Roles::MkcpWriteBufferSizeRole: mkcp.writeBufferSize = str;
case Roles::MkcpCwndMultiplierRole: mkcp.cwndMultiplier = str;
break;
case Roles::MkcpCongestionRole: mkcp.congestion = value.toBool();
case Roles::MkcpMaxSendingWindowRole: mkcp.maxSendingWindow = str;
break;
// ── xPadding ──────────────────────────────────────────────────────
@@ -181,10 +185,11 @@ QVariant XrayConfigModel::data(const QModelIndex& index, int role) const
}
const auto& srv = m_protocolConfig.serverConfig;
const auto& tpl = m_protocolConfig.clientTemplate;
const auto& xhttp = srv.xhttp;
const auto& mkcp = srv.mkcp;
const auto& pad = xhttp.xPadding;
const auto& mux = xhttp.xmux;
const auto& mux = tpl.xmux;
switch (role)
{
@@ -196,7 +201,7 @@ QVariant XrayConfigModel::data(const QModelIndex& index, int role) const
case Roles::FlowRole: return srv.flow;
// ── Security ──────────────────────────────────────────────────────
case Roles::FingerprintRole: return srv.fingerprint;
case Roles::FingerprintRole: return tpl.fingerprint;
case Roles::SniRole: return srv.sni;
case Roles::AlpnRole: return srv.alpn;
@@ -204,7 +209,7 @@ QVariant XrayConfigModel::data(const QModelIndex& index, int role) const
case Roles::XhttpModeRole: return xhttp.mode;
case Roles::XhttpHostRole: return xhttp.host;
case Roles::XhttpPathRole: return xhttp.path;
case Roles::XhttpUplinkMethodRole: return xhttp.uplinkMethod;
case Roles::XhttpUplinkMethodRole: return tpl.uplinkMethod;
case Roles::XhttpDisableGrpcRole: return xhttp.disableGrpc;
case Roles::XhttpDisableSseRole: return xhttp.disableSse;
@@ -215,12 +220,12 @@ QVariant XrayConfigModel::data(const QModelIndex& index, int role) const
case Roles::XhttpUplinkDataPlacementRole: return xhttp.uplinkDataPlacement;
case Roles::XhttpUplinkDataKeyRole: return xhttp.uplinkDataKey;
case Roles::XhttpUplinkChunkSizeRole: return xhttp.uplinkChunkSize;
case Roles::XhttpUplinkChunkSizeRole: return tpl.uplinkChunkSize;
case Roles::XhttpScMaxBufferedPostsRole: return xhttp.scMaxBufferedPosts;
case Roles::XhttpScMaxEachPostBytesMinRole: return xhttp.scMaxEachPostBytesMin;
case Roles::XhttpScMaxEachPostBytesMaxRole: return xhttp.scMaxEachPostBytesMax;
case Roles::XhttpScMinPostsIntervalMsMinRole: return xhttp.scMinPostsIntervalMsMin;
case Roles::XhttpScMinPostsIntervalMsMaxRole: return xhttp.scMinPostsIntervalMsMax;
case Roles::XhttpScMinPostsIntervalMsMinRole: return tpl.scMinPostsIntervalMsMin;
case Roles::XhttpScMinPostsIntervalMsMaxRole: return tpl.scMinPostsIntervalMsMax;
case Roles::XhttpScStreamUpServerSecsMinRole: return xhttp.scStreamUpServerSecsMin;
case Roles::XhttpScStreamUpServerSecsMaxRole: return xhttp.scStreamUpServerSecsMax;
@@ -228,9 +233,9 @@ QVariant XrayConfigModel::data(const QModelIndex& index, int role) const
case Roles::MkcpTtiRole: return mkcp.tti;
case Roles::MkcpUplinkCapacityRole: return mkcp.uplinkCapacity;
case Roles::MkcpDownlinkCapacityRole: return mkcp.downlinkCapacity;
case Roles::MkcpReadBufferSizeRole: return mkcp.readBufferSize;
case Roles::MkcpWriteBufferSizeRole: return mkcp.writeBufferSize;
case Roles::MkcpCongestionRole: return mkcp.congestion;
case Roles::MkcpMtuRole: return mkcp.mtu;
case Roles::MkcpCwndMultiplierRole: return mkcp.cwndMultiplier;
case Roles::MkcpMaxSendingWindowRole: return mkcp.maxSendingWindow;
// ── xPadding ──────────────────────────────────────────────────────
case Roles::XPaddingBytesMinRole: return pad.bytesMin;
@@ -274,11 +279,18 @@ void XrayConfigModel::updateModel(amnezia::DockerContainer container, const amne
if (!m_protocolConfig.serverConfig.isThirdPartyConfig) {
applyDefaultsToServerConfig(m_protocolConfig.serverConfig, false);
if (m_protocolConfig.clientTemplate.fingerprint.isEmpty()) {
m_protocolConfig.clientTemplate.fingerprint = protocols::xray::defaultFingerprint;
}
if (m_protocolConfig.clientTemplate.uplinkMethod.isEmpty()) {
m_protocolConfig.clientTemplate.uplinkMethod = protocols::xray::defaultXhttpUplinkMethod;
}
}
m_originalProtocolConfig = m_protocolConfig;
endResetModel();
if (wasUnsavedChanges != hasUnsavedChanges()) {
emit hasUnsavedChangesChanged();
}
@@ -286,88 +298,54 @@ void XrayConfigModel::updateModel(amnezia::DockerContainer container, const amne
void XrayConfigModel::applyDefaultsToServerConfig(amnezia::XrayServerConfig &config, bool fillFlowDefault)
{
if (config.port.isEmpty()) {
config.port = protocols::xray::defaultPort;
}
if (config.transportProto.isEmpty()) {
config.transportProto = ProtocolUtils::transportProtoToString(
ProtocolUtils::defaultTransportProto(amnezia::Proto::Xray), amnezia::Proto::Xray);
}
if (config.site.isEmpty()) {
config.site = protocols::xray::defaultSite;
}
if (config.transport.isEmpty()) {
config.transport = protocols::xray::defaultTransport;
}
if (config.security.isEmpty()) {
config.security = protocols::xray::defaultSecurity;
}
if (fillFlowDefault && config.flow.isEmpty()) {
config.flow = protocols::xray::defaultFlow;
}
if (config.fingerprint.isEmpty()) {
config.fingerprint = protocols::xray::defaultFingerprint;
} else if (config.fingerprint.contains(QLatin1String("Mozilla/5.0"), Qt::CaseInsensitive)) {
config.fingerprint = QString::fromLatin1(protocols::xray::defaultFingerprint);
}
if (config.sni.isEmpty()) {
config.sni = protocols::xray::defaultSni;
}
if (config.alpn.isEmpty()) {
config.alpn = protocols::xray::defaultAlpn;
}
// XHTTP transport defaults
if (config.xhttp.host.isEmpty()) {
config.xhttp.host = protocols::xray::defaultXhttpHost;
}
if (config.xhttp.mode.isEmpty()) {
config.xhttp.mode = protocols::xray::defaultXhttpMode;
}
if (config.xhttp.uplinkMethod.isEmpty()) {
config.xhttp.uplinkMethod = protocols::xray::defaultXhttpUplinkMethod;
}
if (config.xhttp.sessionPlacement.isEmpty()) {
config.xhttp.sessionPlacement = protocols::xray::defaultXhttpSessionPlacement;
}
if (config.xhttp.sessionKey.isEmpty()) {
config.xhttp.sessionKey = protocols::xray::defaultXhttpSessionKey;
}
if (config.xhttp.seqPlacement.isEmpty()) {
config.xhttp.seqPlacement = protocols::xray::defaultXhttpSeqPlacement;
}
if (config.xhttp.uplinkDataPlacement.isEmpty()) {
config.xhttp.uplinkDataPlacement = protocols::xray::defaultXhttpUplinkDataPlacement;
}
// xPadding defaults
if (config.xhttp.xPadding.placement.isEmpty()) {
config.xhttp.xPadding.placement = protocols::xray::defaultXPaddingPlacement;
}
if (config.xhttp.xPadding.method.isEmpty()) {
config.xhttp.xPadding.method = protocols::xray::defaultXPaddingMethod;
}
config.applyDefaults(fillFlowDefault);
}
amnezia::XrayProtocolConfig XrayConfigModel::getProtocolConfig()
{
const bool serverSettingsChanged =
!m_protocolConfig.serverConfig.hasEqualServerSettings(m_originalProtocolConfig.serverConfig);
const bool clientTemplateChanged =
m_protocolConfig.clientTemplate.contentFingerprint()
!= m_originalProtocolConfig.clientTemplate.contentFingerprint();
if (serverSettingsChanged) {
// A client-only edit (fingerprint, xmux and the rest of the template) no longer applies through a
// local rebuild. Like every other protocol, the cached client config is dropped here, and the normal
// mechanism rebuilds it through createConfig on the next connect, picking up the new template.
if (serverSettingsChanged || clientTemplateChanged) {
m_protocolConfig.clearClientConfig();
}
return m_protocolConfig;
}
amnezia::XrayServerConfig XrayConfigModel::pendingServerConfig(const QString &pendingPort) const
{
amnezia::XrayServerConfig pending = m_protocolConfig.serverConfig;
if (!pendingPort.isEmpty()) {
pending.port = pendingPort;
}
return pending;
}
bool XrayConfigModel::pendingChangeTouchesServer(const QString &pendingPort) const
{
return !pendingServerConfig(pendingPort).hasEqualServerSettings(m_originalProtocolConfig.serverConfig);
}
bool XrayConfigModel::pendingChangeBreaksIssuedConfigs(const QString &pendingPort) const
{
return pendingServerConfig(pendingPort).breaksIssuedConfigs(m_originalProtocolConfig.serverConfig);
}
bool XrayConfigModel::pendingChangeRequiresReinstall(const QString &pendingPort) const
{
const auto effectivePort = [](const QString &port) -> QString {
return port.isEmpty() ? QString::fromLatin1(protocols::xray::defaultPort) : port;
};
const amnezia::XrayServerConfig pending = pendingServerConfig(pendingPort);
return effectivePort(pending.port) != effectivePort(m_originalProtocolConfig.serverConfig.port);
}
bool XrayConfigModel::isServerSettingsEqual() const
{
return m_protocolConfig.serverConfig.hasEqualServerSettings(m_originalProtocolConfig.serverConfig);
@@ -375,7 +353,8 @@ bool XrayConfigModel::isServerSettingsEqual() const
bool XrayConfigModel::hasUnsavedChanges() const
{
return !isServerSettingsEqual();
return m_protocolConfig.serverConfig.toJson() != m_originalProtocolConfig.serverConfig.toJson()
|| m_protocolConfig.clientTemplate.toJson() != m_originalProtocolConfig.clientTemplate.toJson();
}
QHash<int, QByteArray> XrayConfigModel::roleNames() const
@@ -422,9 +401,9 @@ QHash<int, QByteArray> XrayConfigModel::roleNames() const
roles[MkcpTtiRole] = "mkcpTti";
roles[MkcpUplinkCapacityRole] = "mkcpUplinkCapacity";
roles[MkcpDownlinkCapacityRole] = "mkcpDownlinkCapacity";
roles[MkcpReadBufferSizeRole] = "mkcpReadBufferSize";
roles[MkcpWriteBufferSizeRole] = "mkcpWriteBufferSize";
roles[MkcpCongestionRole] = "mkcpCongestion";
roles[MkcpMtuRole] = "mkcpMtu";
roles[MkcpCwndMultiplierRole] = "mkcpCwndMultiplier";
roles[MkcpMaxSendingWindowRole] = "mkcpMaxSendingWindow";
// xPadding
roles[XPaddingBytesMinRole] = "xPaddingBytesMin";
@@ -459,23 +438,35 @@ void XrayConfigModel::resetToDefaults()
beginResetModel();
m_protocolConfig.serverConfig = amnezia::XrayServerConfig{};
applyDefaultsToServerConfig(m_protocolConfig.serverConfig);
m_protocolConfig.clientTemplate = amnezia::XrayClientTemplate{};
m_protocolConfig.clientTemplate.formatVersion = 1;
endResetModel();
if (wasUnsavedChanges != hasUnsavedChanges()) {
emit hasUnsavedChangesChanged();
}
}
void XrayConfigModel::applyServerConfig(const amnezia::XrayServerConfig &serverConfig)
void XrayConfigModel::applyServerConfig(const amnezia::XrayServerConfig &serverConfig,
const amnezia::XrayClientTemplate &clientTemplate)
{
const bool wasUnsavedChanges = hasUnsavedChanges();
beginResetModel();
m_protocolConfig.serverConfig = serverConfig;
m_protocolConfig.clientTemplate = clientTemplate;
if (m_protocolConfig.clientTemplate.formatVersion == 0) {
m_protocolConfig.clientTemplate.formatVersion = 1;
}
if (!m_protocolConfig.serverConfig.isThirdPartyConfig) {
m_protocolConfig.serverConfig.applyDefaults(false);
}
// Clear client config since server settings changed
m_protocolConfig.clearClientConfig();
endResetModel();
if (wasUnsavedChanges != hasUnsavedChanges()) {
emit hasUnsavedChangesChanged();
}
@@ -512,7 +503,7 @@ QStringList XrayConfigModel::alpnOptions()
QStringList XrayConfigModel::xhttpModeOptions()
{
return { "Auto", "Packet-up", "Stream-up", "Stream-one" };
return { "Stream-up", "Stream-one" };
}
QStringList XrayConfigModel::xhttpUplinkMethodOptions()
@@ -532,8 +523,7 @@ QStringList XrayConfigModel::xhttpSeqPlacementOptions()
QStringList XrayConfigModel::xhttpUplinkDataPlacementOptions()
{
// Matches splithttp uplink payload placement (packet-up / advanced)
return { "Body", "Auto", "Header", "Cookie" };
return { "Body", "Auto" };
}
QStringList XrayConfigModel::xPaddingPlacementOptions()
@@ -562,14 +552,14 @@ QString XrayConfigModel::mkcpDefaultDownlinkCapacity()
return QString::fromLatin1(protocols::xray::defaultMkcpDownlinkCapacity);
}
QString XrayConfigModel::mkcpDefaultReadBufferSize()
QString XrayConfigModel::mkcpDefaultMtu()
{
return QString::fromLatin1(protocols::xray::defaultMkcpReadBufferSize);
return QString::fromLatin1(protocols::xray::defaultMkcpMtu);
}
QString XrayConfigModel::mkcpDefaultWriteBufferSize()
QString XrayConfigModel::mkcpDefaultCwndMultiplier()
{
return QString::fromLatin1(protocols::xray::defaultMkcpWriteBufferSize);
return QString::fromLatin1(protocols::xray::defaultMkcpCwndMultiplier);
}
QString XrayConfigModel::portDefault()

View File

@@ -57,11 +57,11 @@ public:
// ── Transport — mKCP ──────────────────────────────────────────
MkcpTtiRole,
MkcpMtuRole,
MkcpUplinkCapacityRole,
MkcpDownlinkCapacityRole,
MkcpReadBufferSizeRole,
MkcpWriteBufferSizeRole,
MkcpCongestionRole,
MkcpCwndMultiplierRole,
MkcpMaxSendingWindowRole,
// ── xPadding ──────────────────────────────────────────────────
XPaddingBytesMinRole,
@@ -112,8 +112,8 @@ public:
Q_INVOKABLE static QString mkcpDefaultTti();
Q_INVOKABLE static QString mkcpDefaultUplinkCapacity();
Q_INVOKABLE static QString mkcpDefaultDownlinkCapacity();
Q_INVOKABLE static QString mkcpDefaultReadBufferSize();
Q_INVOKABLE static QString mkcpDefaultWriteBufferSize();
Q_INVOKABLE static QString mkcpDefaultMtu();
Q_INVOKABLE static QString mkcpDefaultCwndMultiplier();
Q_INVOKABLE static QString portDefault();
Q_INVOKABLE static QString sniDefault();
@@ -135,13 +135,20 @@ public:
Q_INVOKABLE static bool isValidPath(const QString &path);
Q_INVOKABLE QStringList validationErrors() const;
Q_INVOKABLE bool pendingChangeTouchesServer(const QString &pendingPort) const;
Q_INVOKABLE bool pendingChangeBreaksIssuedConfigs(const QString &pendingPort) const;
Q_INVOKABLE bool pendingChangeRequiresReinstall(const QString &pendingPort) const;
public slots:
void updateModel(amnezia::DockerContainer container, const amnezia::XrayProtocolConfig& protocolConfig);
amnezia::XrayProtocolConfig getProtocolConfig();
amnezia::XrayServerConfig pendingServerConfig(const QString &pendingPort) const;
bool isServerSettingsEqual() const;
bool hasUnsavedChanges() const;
void resetToDefaults();
void applyServerConfig(const amnezia::XrayServerConfig &serverConfig);
void applyServerConfig(const amnezia::XrayServerConfig &serverConfig,
const amnezia::XrayClientTemplate &clientTemplate);
signals:
void hasUnsavedChangesChanged();

View File

@@ -13,6 +13,7 @@ QJsonObject XrayConfigSnapshot::toJson() const
obj["displayName"] = displayName;
obj["createdAt"] = createdAt.toString(Qt::ISODate);
obj["serverConfig"] = serverConfig.toJson();
obj["clientTemplate"] = clientTemplate.toJson();
return obj;
}
@@ -23,6 +24,11 @@ XrayConfigSnapshot XrayConfigSnapshot::fromJson(const QJsonObject &json)
s.displayName = json.value("displayName").toString();
s.createdAt = QDateTime::fromString(json.value("createdAt").toString(), Qt::ISODate);
s.serverConfig = amnezia::XrayServerConfig::fromJson(json.value("serverConfig").toObject());
if (json.contains("clientTemplate")) {
s.clientTemplate = amnezia::XrayClientTemplate::fromJson(json.value("clientTemplate").toObject());
} else {
s.clientTemplate.materializeFromLegacy(json.value("serverConfig").toObject());
}
return s;
}
@@ -100,13 +106,15 @@ void XrayConfigSnapshotsModel::reload()
endResetModel();
}
void XrayConfigSnapshotsModel::createFromCurrent(const amnezia::XrayServerConfig &serverConfig)
void XrayConfigSnapshotsModel::createFromCurrent(const amnezia::XrayServerConfig &serverConfig,
const amnezia::XrayClientTemplate &clientTemplate)
{
XrayConfigSnapshot snapshot;
snapshot.id = QUuid::createUuid().toString(QUuid::WithoutBraces);
snapshot.displayName = buildDisplayName(serverConfig);
snapshot.createdAt = QDateTime::currentDateTime();
snapshot.serverConfig = serverConfig;
snapshot.clientTemplate = clientTemplate;
beginInsertRows(QModelIndex(), m_configs.size(), m_configs.size());
m_configs.append(snapshot);
@@ -200,7 +208,8 @@ void XrayConfigSnapshotsModel::createFromCurrentModel()
if (!m_xrayConfigModel) {
return;
}
createFromCurrent(m_xrayConfigModel->getProtocolConfig().serverConfig);
const amnezia::XrayProtocolConfig current = m_xrayConfigModel->getProtocolConfig();
createFromCurrent(current.serverConfig, current.clientTemplate);
}
void XrayConfigSnapshotsModel::applyConfigToCurrentModel(int index)
@@ -208,9 +217,12 @@ void XrayConfigSnapshotsModel::applyConfigToCurrentModel(int index)
if (!m_xrayConfigModel) {
return;
}
amnezia::XrayServerConfig cfg = applyConfig(index);
if (cfg.port.isEmpty()) {
return; // guard against invalid index
if (index < 0 || index >= m_configs.size()) {
return;
}
m_xrayConfigModel->applyServerConfig(cfg);
const XrayConfigSnapshot &snapshot = m_configs.at(index);
if (snapshot.serverConfig.port.isEmpty()) {
return;
}
m_xrayConfigModel->applyServerConfig(snapshot.serverConfig, snapshot.clientTemplate);
}

View File

@@ -19,6 +19,7 @@ struct XrayConfigSnapshot
QString displayName; // auto-generated: "XHTTP TLS Reality", "RAW Reality", etc.
QDateTime createdAt;
amnezia::XrayServerConfig serverConfig;
amnezia::XrayClientTemplate clientTemplate;
QJsonObject toJson() const;
static XrayConfigSnapshot fromJson(const QJsonObject &json);
@@ -44,7 +45,8 @@ public:
public slots:
void reload();
Q_INVOKABLE void createFromCurrent(const amnezia::XrayServerConfig &serverConfig);
Q_INVOKABLE void createFromCurrent(const amnezia::XrayServerConfig &serverConfig,
const amnezia::XrayClientTemplate &clientTemplate = {});
Q_INVOKABLE amnezia::XrayServerConfig applyConfig(int index) const;
Q_INVOKABLE void removeConfig(int index);

View File

@@ -79,6 +79,7 @@ PageType {
Layout.leftMargin: 16
Layout.rightMargin: 16
text: "xtls-rprx-vision-udp443"
descriptionText: qsTr("For apps that require QUIC. Browsers may stop loading.")
enabled: visionAllowed
checked: flow === "xtls-rprx-vision-udp443" && visionAllowed
onClicked: flow = "xtls-rprx-vision-udp443"
@@ -118,7 +119,25 @@ PageType {
text: qsTr("Save")
clickedFunc: function () {
var headerText = qsTr("Save settings?")
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
var touchesServer = XrayConfigModel.pendingChangeTouchesServer("")
var breaksIssued = XrayConfigModel.pendingChangeBreaksIssuedConfigs("")
var requiresReinstall = XrayConfigModel.pendingChangeRequiresReinstall("")
var descriptionText
if (requiresReinstall) {
if (breaksIssued) {
descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it. You will need to share the connection again.")
} else {
descriptionText = qsTr("The server will be recreated. This takes up to a minute, and connections that were already shared keep working.")
}
} else if (touchesServer) {
if (breaksIssued) {
descriptionText = qsTr("The server configuration will be updated. All users with whom you shared a connection with will no longer be able to connect to it. You will need to share the connection again.")
} else {
descriptionText = qsTr("The server configuration will be updated. The container will not be recreated.")
}
} else {
descriptionText = qsTr("The server will not be changed now. The new settings apply the next time you connect.")
}
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function () {

View File

@@ -307,13 +307,32 @@ PageType {
enabled: visible
text: qsTr("Save")
clickedFunc: function () {
saveButton.forceActiveFocus()
var errs = XrayConfigModel.validationErrors()
if (errs.length > 0) {
PageController.showErrorMessage(errs.join("\n"))
return
}
var headerText = qsTr("Save settings?")
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
var touchesServer = XrayConfigModel.pendingChangeTouchesServer("")
var breaksIssued = XrayConfigModel.pendingChangeBreaksIssuedConfigs("")
var requiresReinstall = XrayConfigModel.pendingChangeRequiresReinstall("")
var descriptionText
if (requiresReinstall) {
if (breaksIssued) {
descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it. You will need to share the connection again.")
} else {
descriptionText = qsTr("The server will be recreated. This takes up to a minute, and connections that were already shared keep working.")
}
} else if (touchesServer) {
if (breaksIssued) {
descriptionText = qsTr("The server configuration will be updated. All users with whom you shared a connection with will no longer be able to connect to it. You will need to share the connection again.")
} else {
descriptionText = qsTr("The server configuration will be updated. The container will not be recreated.")
}
} else {
descriptionText = qsTr("The server will not be changed now. The new settings apply the next time you connect.")
}
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function () {

View File

@@ -219,8 +219,28 @@ PageType {
PageController.showErrorMessage(errs.join("\n"))
return
}
var pendingPort = textFieldWithHeaderType.textField.text
var touchesServer = XrayConfigModel.pendingChangeTouchesServer(pendingPort)
var breaksIssued = XrayConfigModel.pendingChangeBreaksIssuedConfigs(pendingPort)
var requiresReinstall = XrayConfigModel.pendingChangeRequiresReinstall(pendingPort)
var headerText = qsTr("Save settings?")
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
var descriptionText
if (requiresReinstall) {
if (breaksIssued) {
descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it. You will need to share the connection again.")
} else {
descriptionText = qsTr("The server will be recreated. This takes up to a minute, and connections that were already shared keep working.")
}
} else if (touchesServer) {
if (breaksIssued) {
descriptionText = qsTr("The server configuration will be updated. All users with whom you shared a connection with will no longer be able to connect to it. You will need to share the connection again.")
} else {
descriptionText = qsTr("The server configuration will be updated. The container will not be recreated.")
}
} else {
descriptionText = qsTr("The server will not be changed now. The new settings apply the next time you connect.")
}
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {

View File

@@ -182,16 +182,42 @@ PageType {
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 8
headerText: qsTr("readBufferSize")
hintText: qsTr("Read buffer size (MB). Range: 12147483647.")
placeholderText: XrayConfigModel.mkcpDefaultReadBufferSize()
textField.text: mkcpReadBufferSize
headerText: qsTr("mtu")
hintText: qsTr("Maximum packet size in bytes. Range: 5761460.")
placeholderText: XrayConfigModel.mkcpDefaultMtu()
textField.text: mkcpMtu
textField.maximumLength: 4
textField.validator: RegularExpressionValidator { regularExpression: /^\d*$/ }
textField.onTextEdited: root.editDirty = (textField.text !== mkcpMtu)
textField.onEditingFinished: {
var v = root.clampInt(textField.text, 576, 1460)
if (v !== mkcpMtu) mkcpMtu = v
else if (textField.text !== v) textField.text = v
if (mkcpMaxSendingWindow !== "") {
var floorNow = parseInt(v !== "" ? v : XrayConfigModel.mkcpDefaultMtu(), 10)
if (parseInt(mkcpMaxSendingWindow, 10) < floorNow) {
mkcpMaxSendingWindow = String(floorNow)
}
}
root.editDirty = false
}
}
TextFieldWithHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 8
headerText: qsTr("cwndMultiplier")
hintText: qsTr("Congestion window multiplier. Minimum: 1.")
placeholderText: XrayConfigModel.mkcpDefaultCwndMultiplier()
textField.text: mkcpCwndMultiplier
textField.maximumLength: 10
textField.validator: RegularExpressionValidator { regularExpression: /^\d*$/ }
textField.onTextEdited: root.editDirty = (textField.text !== mkcpReadBufferSize)
textField.onTextEdited: root.editDirty = (textField.text !== mkcpCwndMultiplier)
textField.onEditingFinished: {
var v = root.clampInt(textField.text, 1, 2147483647)
if (v !== mkcpReadBufferSize) mkcpReadBufferSize = v
if (v !== mkcpCwndMultiplier) mkcpCwndMultiplier = v
else if (textField.text !== v) textField.text = v
root.editDirty = false
}
@@ -202,29 +228,20 @@ PageType {
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 8
headerText: qsTr("writeBufferSize")
hintText: qsTr("Write buffer size (MB). Range: 12147483647.")
placeholderText: XrayConfigModel.mkcpDefaultWriteBufferSize()
textField.text: mkcpWriteBufferSize
headerText: qsTr("maxSendingWindow")
hintText: qsTr("Send window in bytes. Must not be smaller than mtu. Leave empty to let Xray decide.")
textField.text: mkcpMaxSendingWindow
textField.maximumLength: 10
textField.validator: RegularExpressionValidator { regularExpression: /^\d*$/ }
textField.onTextEdited: root.editDirty = (textField.text !== mkcpWriteBufferSize)
textField.onTextEdited: root.editDirty = (textField.text !== mkcpMaxSendingWindow)
textField.onEditingFinished: {
var v = root.clampInt(textField.text, 1, 2147483647)
if (v !== mkcpWriteBufferSize) mkcpWriteBufferSize = v
var mtuFloor = parseInt(mkcpMtu !== "" ? mkcpMtu : XrayConfigModel.mkcpDefaultMtu(), 10)
var v = root.clampInt(textField.text, mtuFloor, 2147483647)
if (v !== mkcpMaxSendingWindow) mkcpMaxSendingWindow = v
else if (textField.text !== v) textField.text = v
root.editDirty = false
}
}
SwitcherType {
Layout.fillWidth: true
Layout.margins: 16
Layout.topMargin: 8
text: qsTr("Congestion")
checked: mkcpCongestion
onToggled: mkcpCongestion = checked
}
}
// ══════════════════════════════════════════════════════════
@@ -542,7 +559,7 @@ PageType {
Layout.leftMargin: 16
Layout.rightMargin: 16
text: xhttpUplinkDataPlacement
descriptionText: qsTr("Header/Cookie apply only in Packet-up mode")
descriptionText: qsTr("Where the uploaded data is carried in the request")
headerText: qsTr("UplinkDataPlacement")
drawerParent: root
listView: ListViewWithRadioButtonType {
@@ -769,13 +786,32 @@ PageType {
enabled: visible
text: qsTr("Save")
clickedFunc: function () {
saveButton.forceActiveFocus()
var errs = XrayConfigModel.validationErrors()
if (errs.length > 0) {
PageController.showErrorMessage(errs.join("\n"))
return
}
var headerText = qsTr("Save settings?")
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
var touchesServer = XrayConfigModel.pendingChangeTouchesServer("")
var breaksIssued = XrayConfigModel.pendingChangeBreaksIssuedConfigs("")
var requiresReinstall = XrayConfigModel.pendingChangeRequiresReinstall("")
var descriptionText
if (requiresReinstall) {
if (breaksIssued) {
descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it. You will need to share the connection again.")
} else {
descriptionText = qsTr("The server will be recreated. This takes up to a minute, and connections that were already shared keep working.")
}
} else if (touchesServer) {
if (breaksIssued) {
descriptionText = qsTr("The server configuration will be updated. All users with whom you shared a connection with will no longer be able to connect to it. You will need to share the connection again.")
} else {
descriptionText = qsTr("The server configuration will be updated. The container will not be recreated.")
}
} else {
descriptionText = qsTr("The server will not be changed now. The new settings apply the next time you connect.")
}
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function () {

View File

@@ -90,8 +90,27 @@ PageType {
enabled: visible
text: qsTr("Save")
clickedFunc: function () {
saveButton.forceActiveFocus()
var headerText = qsTr("Save settings?")
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
var touchesServer = XrayConfigModel.pendingChangeTouchesServer("")
var breaksIssued = XrayConfigModel.pendingChangeBreaksIssuedConfigs("")
var requiresReinstall = XrayConfigModel.pendingChangeRequiresReinstall("")
var descriptionText
if (requiresReinstall) {
if (breaksIssued) {
descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it. You will need to share the connection again.")
} else {
descriptionText = qsTr("The server will be recreated. This takes up to a minute, and connections that were already shared keep working.")
}
} else if (touchesServer) {
if (breaksIssued) {
descriptionText = qsTr("The server configuration will be updated. All users with whom you shared a connection with will no longer be able to connect to it. You will need to share the connection again.")
} else {
descriptionText = qsTr("The server configuration will be updated. The container will not be recreated.")
}
} else {
descriptionText = qsTr("The server will not be changed now. The new settings apply the next time you connect.")
}
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function () {

View File

@@ -219,8 +219,27 @@ PageType {
enabled: visible
text: qsTr("Save")
clickedFunc: function () {
saveButton.forceActiveFocus()
var headerText = qsTr("Save settings?")
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
var touchesServer = XrayConfigModel.pendingChangeTouchesServer("")
var breaksIssued = XrayConfigModel.pendingChangeBreaksIssuedConfigs("")
var requiresReinstall = XrayConfigModel.pendingChangeRequiresReinstall("")
var descriptionText
if (requiresReinstall) {
if (breaksIssued) {
descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it. You will need to share the connection again.")
} else {
descriptionText = qsTr("The server will be recreated. This takes up to a minute, and connections that were already shared keep working.")
}
} else if (touchesServer) {
if (breaksIssued) {
descriptionText = qsTr("The server configuration will be updated. All users with whom you shared a connection with will no longer be able to connect to it. You will need to share the connection again.")
} else {
descriptionText = qsTr("The server configuration will be updated. The container will not be recreated.")
}
} else {
descriptionText = qsTr("The server will not be changed now. The new settings apply the next time you connect.")
}
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function () {

View File

@@ -232,8 +232,27 @@ PageType {
enabled: visible
text: qsTr("Save")
clickedFunc: function () {
saveButton.forceActiveFocus()
var headerText = qsTr("Save settings?")
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
var touchesServer = XrayConfigModel.pendingChangeTouchesServer("")
var breaksIssued = XrayConfigModel.pendingChangeBreaksIssuedConfigs("")
var requiresReinstall = XrayConfigModel.pendingChangeRequiresReinstall("")
var descriptionText
if (requiresReinstall) {
if (breaksIssued) {
descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it. You will need to share the connection again.")
} else {
descriptionText = qsTr("The server will be recreated. This takes up to a minute, and connections that were already shared keep working.")
}
} else if (touchesServer) {
if (breaksIssued) {
descriptionText = qsTr("The server configuration will be updated. All users with whom you shared a connection with will no longer be able to connect to it. You will need to share the connection again.")
} else {
descriptionText = qsTr("The server configuration will be updated. The container will not be recreated.")
}
} else {
descriptionText = qsTr("The server will not be changed now. The new settings apply the next time you connect.")
}
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function () {