Compare commits

..

2 Commits

Author SHA1 Message Date
yp
56ab82f87f fix: Use shared OpenSSL on Android (#2736) 2026-06-16 10:57:32 +07:00
lunardunno
3984acbb44 feat: updating install_docker.sh script (#2661)
* Updating install_docker.sh script

Implementing a Docker service status check.
The Docker reinstall step has been removed due to the implementation of Docker service checking.
Implementing locale checking and assignment.
Implementation of execution of some actions through commands with sudo, to reduce delays caused by differences in the values ​​of the PATH variable for the root user and the user included in the sudo group.
Implementation of a verification step for the install containerization app to avoid installing unsupported podman-docker applications.

* adding message handling to install controller

Adding handling for "Containerization app is not supported" and "Service status not active" messages to the controller.

* Error Codes added

Error Codes added for ServerContainerizationNotSupported & DockerServiceNotActive

* Adding extended descriptions of new errors

* fix last line in errorCodes.h

* fix last line in errorStrings.cpp

* Changing the names of errors

* various changes in the script

The messages output for processing by the server controller have been changed: "Container runtime is not supported" and "Container runtime service is not running."
The redundant check and output of the "Packet manager not found" message, as well as the interruption of script execution, have been eliminated, as this situation is handled by the server controller at an earlier stage (check_server_is_busy.sh) and only there.
Added installation of the whish package if it is missing from the OS, for subsequent re-execution of the install_docker.sh and check_server_is_busy.sh scripts.
Implemented an alternative method for detecting the package manager if the whish package is initially missing from the OS.
The algorithm for setting the $pm variable (package manager) has been changed.

* processed phrases have been changed

The phrases processed by the server controller have been changed.

* Attempting to use "command -v"

Switching to using "command -v" instead of "which".

* "which" as main, "command" as backup.

* "which" as main, "command" as backup for check user

* which  LOCK_CMD with sudo

Run the "which" with sudo to check the $LOCK_CMD variable in case the user's PATH variable has incorrect values ​​if the user is not root and is only a member of the sudo group.

* suppressing sudo password prompt

* suppressing sudo password prompt

* suppressing sudo password prompt install_docker.sh

* Changing the phrase for check stdout

"sudo:" with "not found" instead of "command not found"

* Changing phrases for check stdout check_user_in_sudo.sh‎

* sudo|docker and not found, in one line

* check only sudoers
2026-06-15 22:28:38 +07:00
19 changed files with 129 additions and 713 deletions

View File

@@ -835,23 +835,9 @@ ErrorCode InstallController::installDockerWorker(const ServerCredentials &creden
qDebug().noquote() << "InstallController::installDockerWorker" << stdOut;
if (container == DockerContainer::MtProxy || container == DockerContainer::Telemt) {
QString conntrackOut;
auto cbConntrack = [&](const QString &data, libssh::Client &) {
conntrackOut += data + "\n";
return ErrorCode::NoError;
};
sshSession.runScript(
credentials,
sshSession.replaceVars(amnezia::scriptData(SharedScriptType::install_conntrack),
amnezia::genBaseVars(credentials, DockerContainer::None, QString(), QString())),
cbConntrack, cbConntrack);
qDebug().noquote() << "InstallController::installDockerWorker install_conntrack:" << conntrackOut;
}
if (container == DockerContainer::Awg2) {
QRegularExpression regex(R"(Linux\s+(\d+)\.(\d+)[^\d]*)");
QRegularExpressionMatch match = regex.match(stdOut);
QRegularExpression kernelVersionRegex(R"(Linux\s+(\d+)\.(\d+)[^\d]*)");
QRegularExpressionMatch match = kernelVersionRegex.match(stdOut);
if (match.hasMatch()) {
int majorVersion = match.captured(1).toInt();
int minorVersion = match.captured(2).toInt();
@@ -864,8 +850,19 @@ ErrorCode InstallController::installDockerWorker(const ServerCredentials &creden
if (stdOut.contains("lock"))
return ErrorCode::ServerPacketManagerError;
if (stdOut.contains("command not found"))
if (stdOut.contains("Container runtime is not supported"))
return ErrorCode::ServerContainerRuntimeNotSupported;
QRegularExpression notFoundRegex(
R"(^.*(?:sudo:|docker:).*not found.*$)",
QRegularExpression::MultilineOption);
if (notFoundRegex.match(stdOut).hasMatch()) {
return ErrorCode::ServerDockerFailedError;
}
if (stdOut.contains("Container runtime service not running"))
return ErrorCode::ContainerRuntimeServiceNotRunning;
return error;
}
@@ -902,7 +899,7 @@ ErrorCode InstallController::isUserInSudo(const ServerCredentials &credentials,
return ErrorCode::ServerUserNotInSudo;
if (stdOut.contains("can't cd to") || stdOut.contains("Permission denied") || stdOut.contains("No such file or directory"))
return ErrorCode::ServerUserDirectoryNotAccessible;
if (stdOut.contains("sudoers") || stdOut.contains("is not allowed to run sudo on"))
if (stdOut.contains(QRegularExpression(R"(\bsudoers\b)")) || stdOut.contains("is not allowed to") || stdOut.contains("can't do that"))
return ErrorCode::ServerUserNotAllowedInSudoers;
if (stdOut.contains("password is required") || stdOut.contains("authentication is required"))
return ErrorCode::ServerUserPasswordRequired;

View File

@@ -71,62 +71,48 @@ ErrorCode MtProxyInstaller::queryDiagnostics(SshSession &sshSession, const Serve
DockerContainer container, int listenPort,
MtProxyContainerDiagnostics &out)
{
out = { };
if (container == DockerContainer::MtProxy || container == DockerContainer::Telemt) {
const QString containerName = ContainerUtils::containerToString(container);
const bool isTelemt = container == DockerContainer::Telemt;
const QString sportFilter = QString::number(listenPort);
const QString peersCmd = QStringLiteral("sudo conntrack -L -p tcp --dport ") + sportFilter
+ QStringLiteral(" 2>/dev/null | grep ESTABLISHED | awk '{for(i=1;i<=NF;i++) if($i ~ /^src=/){print "
"substr($i,5); break}}'");
const QString publicFilter = QStringLiteral(" | grep -vE "
"'^(10\\.|127\\.|169\\.254\\.|192\\.168\\.|172\\.(1[6-9]|2[0-9]|3["
"01])\\.|::1$|fe80:|f[cd][0-9a-f][0-9a-f]:)'");
const QString clientsCmd =
QStringLiteral("CLIENTS=$(") + peersCmd + publicFilter + QStringLiteral(" | sort -u | grep -c .); ");
const QString confFile =
isTelemt ? QStringLiteral("/data/config.toml") : QStringLiteral("/data/proxy-multi.conf");
const QString statsUrl = QString();
const QString script = QStringLiteral("CN=") + containerName + QStringLiteral("; ")
+ QStringLiteral("PORT_OK=$(sudo ss -tlnp 2>/dev/null | grep -q :") + QString::number(listenPort)
+ QStringLiteral(" && echo yes || echo no); ")
+ QStringLiteral("TG_OK=$(curl -s --max-time 5 -o /dev/null -w '%{http_code}' "
"https://core.telegram.org/getProxySecret 2>/dev/null | grep -q '200' && echo yes || "
"echo no); ")
+ clientsCmd + QStringLiteral("CONF_TIME=$(sudo docker exec \"$CN\" sh -c 'stat -c \"%y\" ") + confFile
+ QStringLiteral(" 2>/dev/null | cut -d. -f1' 2>/dev/null || echo unknown); ")
+ QStringLiteral("echo \"PORT_OK=${PORT_OK}\"; ") + QStringLiteral("echo \"TG_OK=${TG_OK}\"; ")
+ QStringLiteral("echo \"CLIENTS=${CLIENTS:-0}\"; ") + QStringLiteral("echo \"CONF_TIME=${CONF_TIME}\"; ")
+ QStringLiteral("echo \"STATS=") + statsUrl + QStringLiteral("\";");
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data;
return ErrorCode::NoError;
};
const ErrorCode errorCode = sshSession.runScript(credentials, script, cbReadStdOut);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
for (const QString &line : stdOut.split('\n', Qt::SkipEmptyParts)) {
if (line.startsWith(QLatin1String("PORT_OK="))) {
out.portReachable = line.mid(8).trimmed() == QLatin1String("yes");
} else if (line.startsWith(QLatin1String("TG_OK="))) {
out.upstreamReachable = line.mid(6).trimmed() == QLatin1String("yes");
} else if (line.startsWith(QLatin1String("CLIENTS="))) {
out.clientsConnected = line.mid(8).trimmed().toInt();
} else if (line.startsWith(QLatin1String("CONF_TIME="))) {
out.lastConfigRefresh = line.mid(10).trimmed();
} else if (line.startsWith(QLatin1String("STATS="))) {
out.statsEndpoint = line.mid(6).trimmed();
}
}
return ErrorCode::NoError;
out = {};
if (container != DockerContainer::MtProxy && container != DockerContainer::Telemt) {
return ErrorCode::InternalError;
}
const QString containerName = ContainerUtils::containerToString(container);
const QString script =
QStringLiteral(
"PORT_OK=$(sudo docker exec %1 sh -c 'ss -tlnp 2>/dev/null | grep -q :%2 && echo yes || echo no' 2>/dev/null || echo no); "
"TG_OK=$(curl -s --max-time 5 -o /dev/null -w '%%{http_code}' https://core.telegram.org/getProxySecret 2>/dev/null | grep -q '200' && echo yes || echo no); "
"CLIENTS=$(sudo docker exec amnezia-mtproxy sh -c 'curl -s --max-time 3 http://localhost:2398/stats 2>/dev/null | grep -o \"total_special_connections:[0-9]*\" | cut -d: -f2' 2>/dev/null); "
"CONF_TIME=$(sudo docker exec amnezia-mtproxy sh -c 'stat -c \"%%y\" /data/proxy-multi.conf 2>/dev/null | cut -d. -f1' 2>/dev/null || echo unknown); "
"echo \"PORT_OK=${PORT_OK}\"; "
"echo \"TG_OK=${TG_OK}\"; "
"echo \"CLIENTS=${CLIENTS:-0}\"; "
"echo \"CONF_TIME=${CONF_TIME}\"; "
"echo \"STATS=http://localhost:2398/stats\";")
.arg(containerName)
.arg(listenPort);
return ErrorCode::InternalError;
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data;
return ErrorCode::NoError;
};
const ErrorCode errorCode = sshSession.runScript(credentials, script, cbReadStdOut);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
for (const QString &line : stdOut.split('\n', Qt::SkipEmptyParts)) {
if (line.startsWith(QLatin1String("PORT_OK="))) {
out.portReachable = line.mid(8).trimmed() == QLatin1String("yes");
} else if (line.startsWith(QLatin1String("TG_OK="))) {
out.upstreamReachable = line.mid(6).trimmed() == QLatin1String("yes");
} else if (line.startsWith(QLatin1String("CLIENTS="))) {
out.clientsConnected = line.mid(8).trimmed().toInt();
} else if (line.startsWith(QLatin1String("CONF_TIME="))) {
out.lastConfigRefresh = line.mid(10).trimmed();
} else if (line.startsWith(QLatin1String("STATS="))) {
out.statsEndpoint = line.mid(6).trimmed();
}
}
return ErrorCode::NoError;
}
void MtProxyInstaller::uploadClientSettingsSnapshot(SshSession &sshSession, const ServerCredentials &credentials,

View File

@@ -271,7 +271,6 @@ namespace amnezia
constexpr char workersModeAuto[] = "auto";
constexpr char workersModeManual[] = "manual";
constexpr int maxWorkers = 32;
constexpr int botTagHexLength = 32;
}
} // namespace protocols

View File

@@ -38,6 +38,8 @@ namespace amnezia
XrayServerConfigInvalid = 215,
XrayServerNoVlessClients = 216,
XrayRealityKeysReadFailed = 217,
ServerContainerRuntimeNotSupported = 218,
ContainerRuntimeServiceNotRunning = 219,
// Ssh connection errors
SshRequestDeniedError = 300,
@@ -124,5 +126,3 @@ namespace amnezia
Q_DECLARE_METATYPE(amnezia::ErrorCode)
#endif // ERRORCODES_H

View File

@@ -39,6 +39,8 @@ QString errorString(ErrorCode code) {
case(ErrorCode::XrayRealityKeysReadFailed):
errorMessage = QObject::tr("Server error: failed to read XRay Reality keys from the server");
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;
// Libssh errors
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;

View File

@@ -50,7 +50,6 @@ QString amnezia::scriptName(SharedScriptType type)
switch (type) {
case SharedScriptType::prepare_host: return QLatin1String("prepare_host.sh");
case SharedScriptType::install_docker: return QLatin1String("install_docker.sh");
case SharedScriptType::install_conntrack: return QLatin1String("install_conntrack.sh");
case SharedScriptType::build_container: return QLatin1String("build_container.sh");
case SharedScriptType::remove_container: return QLatin1String("remove_container.sh");
case SharedScriptType::remove_all_containers: return QLatin1String("remove_all_containers.sh");

View File

@@ -21,7 +21,6 @@ enum SharedScriptType {
// General scripts
prepare_host,
install_docker,
install_conntrack,
build_container,
remove_container,
remove_all_containers,

View File

@@ -1,7 +1,8 @@
if which apt-get > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/dpkg/lock-frontend";\
elif which dnf > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/cache/dnf/* /var/run/dnf/* /var/lib/dnf/* /var/lib/rpm/*";\
elif which yum > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/yum.pid";\
elif which zypper > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/zypp.pid";\
elif which pacman > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/pacman/db.lck";\
else echo "Packet manager not found"; echo "Internal error"; exit 1; fi;\
if command -v $LOCK_CMD > /dev/null 2>&1; then sudo $LOCK_CMD $LOCK_FILE 2>/dev/null; else echo "$LOCK_CMD not installed"; fi
if which apt-get > /dev/null 2>&1 || command -v apt-get > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/dpkg/lock-frontend";\
elif which dnf > /dev/null 2>&1 || command -v dnf > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/cache/dnf/* /var/run/dnf/* /var/lib/dnf/* /var/lib/rpm/*";\
elif which yum > /dev/null 2>&1 || command -v yum > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/yum.pid";\
elif which zypper > /dev/null 2>&1 || command -v zypper > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/zypp.pid";\
elif which pacman > /dev/null 2>&1 || command -v pacman > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/pacman/db.lck";\
else echo "Packet manager not found"; echo "Internal error"; exit 1;\
fi;\
if sudo -n which $LOCK_CMD > /dev/null 2>&1 || command -v $LOCK_CMD > /dev/null 2>&1; then sudo -n $LOCK_CMD $LOCK_FILE 2>/dev/null; else echo "$LOCK_CMD not installed"; fi

View File

@@ -1,8 +1,8 @@
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); opt="--version";\
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); opt="--version";\
elif which yum > /dev/null 2>&1; then pm=$(which yum); opt="--version";\
elif which zypper > /dev/null 2>&1; then pm=$(which zypper); opt="--version";\
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); opt="--version";\
if pm=$(which apt-get 2>/dev/null || command -v apt-get 2>/dev/null); then opt="--version";\
elif pm=$(which dnf 2>/dev/null || command -v dnf 2>/dev/null); then opt="--version";\
elif pm=$(which yum 2>/dev/null || command -v yum 2>/dev/null); then opt="--version";\
elif pm=$(which zypper 2>/dev/null || command -v zypper 2>/dev/null); then opt="--version";\
elif pm=$(which pacman 2>/dev/null || command -v pacman 2>/dev/null); then opt="--version";\
else pm="uname"; opt="-a";\
fi;\
CUR_USER=$(whoami 2>/dev/null || echo $HOME | sed 's/.*\///');\

View File

@@ -1,10 +0,0 @@
if command -v conntrack > /dev/null 2>&1; then echo "conntrack already installed"; exit 0; fi;\
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install --install-recommends"; check_pkgs="-yq update"; conntrack_pkg="conntrack"; dist="debian";\
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; conntrack_pkg="conntrack-tools"; dist="fedora";\
elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; conntrack_pkg="conntrack-tools"; dist="centos";\
elif which zypper > /dev/null 2>&1; then pm=$(which zypper); silent_inst="-nq install"; check_pkgs="-nq refresh"; conntrack_pkg="conntrack-tools"; dist="opensuse";\
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); silent_inst="-S --noconfirm --noprogressbar --quiet"; check_pkgs="-Sup"; conntrack_pkg="conntrack-tools"; dist="archlinux";\
else echo "Packet manager not found"; exit 0; fi;\
if [ "$dist" = "debian" ]; then export DEBIAN_FRONTEND=noninteractive; fi;\
sudo $pm $check_pkgs; sudo $pm $silent_inst $conntrack_pkg;\
command -v conntrack > /dev/null 2>&1 && echo "conntrack installed" || echo "conntrack install failed"

View File

@@ -1,25 +1,34 @@
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install --install-recommends"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\
elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\
elif which zypper > /dev/null 2>&1; then pm=$(which zypper); silent_inst="-nq install"; check_pkgs="-nq refresh"; docker_pkg="docker"; dist="opensuse";\
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); silent_inst="-S --noconfirm --noprogressbar --quiet"; check_pkgs="-Sup"; docker_pkg="docker"; dist="archlinux";\
else echo "Packet manager not found"; exit 1; fi;\
echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg";\
if pm=$(which apt-get 2>/dev/null || command -v apt-get 2>/dev/null); then silent_inst="-yq install --install-recommends"; what_pkg="-s install"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\
elif pm=$(which dnf 2>/dev/null || command -v dnf 2>/dev/null); then silent_inst="-yq install"; what_pkg="--assumeno install --setopt=tsflags=test"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\
elif pm=$(which yum 2>/dev/null || command -v yum 2>/dev/null); then silent_inst="-y -q install"; what_pkg="--assumeno install --setopt=tsflags=test"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\
elif pm=$(which zypper 2>/dev/null || command -v zypper 2>/dev/null); then silent_inst="-nq install"; what_pkg="--dry-run install"; check_pkgs="-nq refresh"; docker_pkg="docker"; dist="suse";\
elif pm=$(which pacman 2>/dev/null || command -v pacman 2>/dev/null); then silent_inst="-S --noconfirm --noprogressbar --quiet"; what_pkg="-Sp"; check_pkgs="-Sup"; docker_pkg="docker"; dist="archlinux";\
fi;\
echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, What pkg command: $what_pkg, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg, Language: $LANG";\
echo $LANG | grep -qE '^(en_US.UTF-8|C.UTF-8|C)$' || export LC_ALL=C;\
if [ "$dist" = "debian" ]; then export DEBIAN_FRONTEND=noninteractive; fi;\
if ! command -v sudo > /dev/null 2>&1; then $pm $check_pkgs; $pm $silent_inst sudo; fi;\
if ! command -v fuser > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst psmisc; fi;\
if ! command -v lsof > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst lsof; fi;\
if ! command -v docker > /dev/null 2>&1; then \
sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\
sleep 5; sudo systemctl enable --now docker; sleep 5;\
if ! sudo -n sh -c 'command -v which > /dev/null 2>&1'; then sudo -n $pm $check_pkgs; sudo -n $pm $silent_inst which; fi;\
if ! sudo -n sh -c 'command -v fuser > /dev/null 2>&1'; then sudo -n $pm $check_pkgs; sudo -n $pm $silent_inst psmisc; fi;\
if ! sudo -n sh -c 'command -v lsof > /dev/null 2>&1'; then sudo -n $pm $check_pkgs; sudo -n $pm $silent_inst lsof; fi;\
if ! sudo -n sh -c 'command -v docker > /dev/null 2>&1'; then \
sudo -n $pm $check_pkgs;\
if ! sudo -n $pm $what_pkg $docker_pkg 2>/dev/null | grep -qi podman; then \
sudo -n $pm $silent_inst $docker_pkg;\
sleep 5; sudo -n systemctl enable --now docker; sleep 5;\
else \
echo "Container runtime is not supported";\
exit 1;\
fi;\
fi;\
if [ "$(cat /sys/module/apparmor/parameters/enabled 2>/dev/null)" = "Y" ]; then \
if ! command -v apparmor_parser > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst apparmor; fi;\
if [ "$(sudo -n cat /sys/module/apparmor/parameters/enabled 2>/dev/null)" = "Y" ]; then \
if ! sudo -n sh -c 'command -v apparmor_parser > /dev/null 2>&1'; then \
sudo -n $pm $check_pkgs; sudo -n $pm $silent_inst apparmor;\
fi;\
fi;\
if [ "$(systemctl is-active docker)" != "active" ]; then \
sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\
sleep 5; sudo systemctl start docker; sleep 5;\
if [ "$(sudo -n systemctl is-active docker)" != "active" ]; then \
sleep 5; sudo -n systemctl start docker; sleep 5;\
if [ "$(sudo -n systemctl is-active docker)" != "active" ]; then echo "Container runtime service not running"; fi;\
fi;\
if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install sudo, command not found"; exit 1; fi;\
docker --version;\
sudo -n docker --version || docker --version;\
uname -sr

View File

@@ -18,7 +18,6 @@
<file>dns/Dockerfile</file>
<file>dns/run_container.sh</file>
<file>install_docker.sh</file>
<file>install_conntrack.sh</file>
<file>ipsec/configure_container.sh</file>
<file>ipsec/Dockerfile</file>
<file>ipsec/mobileconfig.plist</file>

View File

@@ -398,9 +398,6 @@ bool MtProxyConfigModel::isValidPublicHost(const QString &host) const {
return NetworkUtilities::checkIPv4Format(t);
}
if (a.protocol() == QHostAddress::IPv6Protocol) {
if (a.isNull() || a.isLoopback() || a == QHostAddress(QHostAddress::AnyIPv6)) {
return false;
}
return true;
}
static const QRegularExpression onlyAsciiDigits(QStringLiteral(R"(^\d+$)"));

View File

@@ -1,13 +1,7 @@
#include "telemtConfigModel.h"
#include "ui/models/utils/mtproxy_public_host_input.h"
#include <QHostAddress>
#include <QRegExp>
#include <QRegularExpression>
#include <qqml.h>
#include "core/utils/networkUtilities.h"
#include "core/utils/qrCodeUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
@@ -15,9 +9,7 @@
using namespace amnezia;
TelemtConfigModel::TelemtConfigModel(QObject *parent) : QAbstractListModel(parent) {
qmlRegisterType<PublicHostInputValidator>("TelemtConfig", 1, 0, "PublicHostInputValidator");
}
TelemtConfigModel::TelemtConfigModel(QObject *parent) : QAbstractListModel(parent) {}
void TelemtConfigModel::applyDefaults(TelemtProtocolConfig &c) {
if (c.port.isEmpty()) {
@@ -57,11 +49,7 @@ bool TelemtConfigModel::setData(const QModelIndex &index, const QVariant &value,
break;
}
case Roles::TagRole: {
const QString tag = sanitizeMtProxyTagFieldText(value.toString());
if (!isValidMtProxyTag(tag)) {
return false;
}
m_protocolConfig.tag = tag;
m_protocolConfig.tag = value.toString();
break;
}
case Roles::IsEnabledRole: {
@@ -69,11 +57,7 @@ bool TelemtConfigModel::setData(const QModelIndex &index, const QVariant &value,
break;
}
case Roles::PublicHostRole: {
const QString h = value.toString().trimmed();
if (!isValidPublicHost(h)) {
return false;
}
m_protocolConfig.publicHost = h;
m_protocolConfig.publicHost = value.toString();
break;
}
case Roles::TransportModeRole: {
@@ -81,11 +65,7 @@ bool TelemtConfigModel::setData(const QModelIndex &index, const QVariant &value,
break;
}
case Roles::TlsDomainRole: {
const QString d = value.toString().trimmed();
if (!isValidFakeTlsDomain(d)) {
return false;
}
m_protocolConfig.tlsDomain = d;
m_protocolConfig.tlsDomain = value.toString();
break;
}
case Roles::AdditionalSecretsRole: {
@@ -105,19 +85,11 @@ bool TelemtConfigModel::setData(const QModelIndex &index, const QVariant &value,
break;
}
case Roles::NatInternalIpRole: {
const QString ip = value.toString().trimmed();
if (!isValidOptionalIpv4(ip)) {
return false;
}
m_protocolConfig.natInternalIp = ip;
m_protocolConfig.natInternalIp = value.toString();
break;
}
case Roles::NatExternalIpRole: {
const QString ip = value.toString().trimmed();
if (!isValidOptionalIpv4(ip)) {
return false;
}
m_protocolConfig.natExternalIp = ip;
m_protocolConfig.natExternalIp = value.toString();
break;
}
case Roles::MaskEnabledRole: {
@@ -407,293 +379,6 @@ QString TelemtConfigModel::workersModeManual() const {
return QString::fromUtf8(protocols::telemt::workersModeManual);
}
bool TelemtConfigModel::isValidPublicHost(const QString &host) const {
const QString t = host.trimmed();
if (t.isEmpty()) {
return true;
}
if (t.length() > 253) {
return false;
}
QHostAddress a(t);
if (a.protocol() == QHostAddress::IPv4Protocol) {
return NetworkUtilities::checkIPv4Format(t);
}
if (a.protocol() == QHostAddress::IPv6Protocol) {
// Reject unusable special addresses such as "::" (any), loopback and null.
if (a.isNull() || a.isLoopback() || a == QHostAddress(QHostAddress::AnyIPv6)) {
return false;
}
return true;
}
static const QRegularExpression onlyAsciiDigits(QStringLiteral(R"(^\d+$)"));
if (onlyAsciiDigits.match(t).hasMatch()) {
return false;
}
return NetworkUtilities::domainRegExp().exactMatch(t);
}
bool TelemtConfigModel::isPublicHostInputAllowed(const QString &text) const {
return mtproxyPublicHostInputAllowed(text);
}
bool TelemtConfigModel::isPublicHostTypingIncomplete(const QString &text) const {
const QString t = text.trimmed();
if (isValidPublicHost(t)) {
return false;
}
static const QRegularExpression onlyDigitDot(QStringLiteral(R"(^[0-9.]+$)"));
if (onlyDigitDot.match(t).hasMatch()) {
if (t.endsWith(QLatin1Char('.'))) {
return true;
}
const QStringList parts = t.split(QLatin1Char('.'), Qt::KeepEmptyParts);
if (parts.size() < 4) {
return true;
}
for (const QString &part: parts) {
if (part.isEmpty()) {
return true;
}
}
return false;
}
if (t.contains(QLatin1Char(':'))) {
if (t.contains(QLatin1String(":::"))) {
return false;
}
if (t.endsWith(QLatin1Char(':'))) {
return true;
}
QHostAddress a(t);
if (a.protocol() == QHostAddress::IPv6Protocol) {
return false;
}
if (!t.contains(QLatin1String("::")) && t.count(QLatin1Char(':')) < 7 && !t.contains(QLatin1Char('.'))) {
return true;
}
return false;
}
if (!t.contains(QLatin1Char('.'))) {
return true;
}
return false;
}
bool TelemtConfigModel::isValidMtProxyTag(const QString &tag) const {
if (tag.isEmpty()) {
return true;
}
static const QRegularExpression re(
QStringLiteral("^([0-9a-fA-F]{%1})$").arg(protocols::telemt::botTagHexLength));
return re.match(tag).hasMatch();
}
bool TelemtConfigModel::isMtProxyTagTypingIncomplete(const QString &text) const {
const QString t = text.trimmed();
if (t.isEmpty()) {
return true;
}
static const QRegularExpression hexOnly(QStringLiteral(R"(^[0-9a-fA-F]*$)"));
if (!hexOnly.match(t).hasMatch()) {
return false;
}
return t.size() < protocols::telemt::botTagHexLength;
}
int TelemtConfigModel::mtProxyBotTagHexLength() const {
return protocols::telemt::botTagHexLength;
}
bool TelemtConfigModel::isValidFakeTlsDomain(const QString &domain) const {
const QString t = domain.trimmed();
if (t.isEmpty()) {
return true;
}
if (t.length() > 253) {
return false;
}
QHostAddress addr;
if (addr.setAddress(t)) {
return false;
}
static const QRegularExpression onlyAsciiDigits(QStringLiteral(R"(^\d+$)"));
if (onlyAsciiDigits.match(t).hasMatch()) {
return false;
}
QRegExp re(NetworkUtilities::domainRegExp());
re.setCaseSensitivity(Qt::CaseInsensitive);
if (!re.exactMatch(t)) {
return false;
}
// ee + 32 hex (base secret) + hex(UTF-8 domain); keep headroom under typical client limits.
if (t.toUtf8().size() > 111) {
return false;
}
return true;
}
QString TelemtConfigModel::normalizeFakeTlsDomainInput(const QString &input) const {
QString t = input.trimmed();
if (t.startsWith(QLatin1String("https://"), Qt::CaseInsensitive)) {
t = t.mid(8);
} else if (t.startsWith(QLatin1String("http://"), Qt::CaseInsensitive)) {
t = t.mid(7);
}
if (const int slash = t.indexOf(QLatin1Char('/')); slash >= 0) {
t = t.left(slash);
}
if (const int at = t.indexOf(QLatin1Char('@')); at >= 0) {
t = t.mid(at + 1);
}
if (const int colon = t.indexOf(QLatin1Char(':')); colon >= 0) {
t = t.left(colon);
}
if (t.startsWith(QLatin1String("www."), Qt::CaseInsensitive)) {
const QString rest = t.mid(4);
if (rest.contains(QLatin1Char('.'))) {
t = rest;
}
}
return t.trimmed();
}
bool TelemtConfigModel::isFakeTlsDomainTypingIncomplete(const QString &text) const {
const QString t = text.trimmed();
if (t.isEmpty()) {
return true;
}
if (isValidFakeTlsDomain(t)) {
return false;
}
if (t.contains(QLatin1Char('/')) || t.contains(QLatin1Char(':')) || t.contains(QLatin1Char('@'))
|| t.contains(QLatin1Char(' '))) {
return false;
}
if (t.contains(QLatin1String(".."))) {
return false;
}
if (!t.contains(QLatin1Char('.'))) {
return true;
}
if (t.endsWith(QLatin1Char('.'))) {
return true;
}
static const QRegularExpression legalPartial(QStringLiteral(R"(^[a-zA-Z0-9.-]*$)"));
if (!legalPartial.match(t).hasMatch()) {
return false;
}
return true;
}
bool TelemtConfigModel::isFakeTlsDomainInputAllowed(const QString &text) const {
if (text.length() > 253) {
return false;
}
static const QRegularExpression re(QStringLiteral(R"(^[a-zA-Z0-9.-]*$)"));
return re.match(text).hasMatch();
}
QString TelemtConfigModel::sanitizeFakeTlsDomainFieldText(const QString &input) const {
const QString t = normalizeFakeTlsDomainInput(input);
QString out;
out.reserve(t.size());
for (const QChar &c: t) {
const ushort u = c.unicode();
const bool letter = (u >= 'a' && u <= 'z') || (u >= 'A' && u <= 'Z');
const bool digit = (u >= '0' && u <= '9');
if (letter || digit || u == '.' || u == '-') {
out.append(c);
}
}
if (out.size() > 253) {
out.truncate(253);
}
return out;
}
QString TelemtConfigModel::sanitizePublicHostFieldText(const QString &input) const {
QString out;
const int cap = qMin(input.size(), 253);
out.reserve(cap);
for (const QChar &c: input) {
if (out.size() >= 253) {
break;
}
const ushort u = c.unicode();
if ((u >= 'a' && u <= 'z') || (u >= 'A' && u <= 'Z') || (u >= '0' && u <= '9') || u == '.' || u == ':' ||
u == '-') {
out.append(c);
}
}
return out;
}
QString TelemtConfigModel::sanitizePortFieldText(const QString &input) const {
QString out;
out.reserve(qMin(input.size(), 5));
for (const QChar &c: input) {
const ushort u = c.unicode();
if (u >= '0' && u <= '9' && out.size() < 5) {
out.append(c);
}
}
return out;
}
QString TelemtConfigModel::sanitizeMtProxyTagFieldText(const QString &input) const {
QString trimmed = input.trimmed();
if (trimmed.startsWith(QLatin1String("0x"), Qt::CaseInsensitive)) {
trimmed = trimmed.mid(2).trimmed();
}
// Prefer a contiguous 32-hex run (paste from bot message with extra text).
static const QRegularExpression runHex(QStringLiteral(R"(([0-9a-fA-F]{32}))"));
const QRegularExpressionMatch m = runHex.match(trimmed);
if (m.hasMatch()) {
return m.captured(1);
}
const int cap = protocols::telemt::botTagHexLength;
QString out;
out.reserve(qMin(trimmed.size(), cap));
for (const QChar &c: trimmed) {
if (out.size() >= cap) {
break;
}
const ushort u = c.unicode();
if ((u >= '0' && u <= '9') || (u >= 'a' && u <= 'f') || (u >= 'A' && u <= 'F')) {
out.append(c);
}
}
return out;
}
QString TelemtConfigModel::sanitizeOptionalIpv4FieldText(const QString &input) const {
QString out;
out.reserve(qMin(input.size(), 15));
for (const QChar &c: input) {
if (out.size() >= 15) {
break;
}
const ushort u = c.unicode();
if ((u >= '0' && u <= '9') || u == '.') {
out.append(c);
}
}
return out;
}
bool TelemtConfigModel::isValidOptionalIpv4(const QString &ip) const {
const QString t = ip.trimmed();
if (t.isEmpty()) {
return true;
}
return NetworkUtilities::checkIPv4Format(t);
}
QHash<int, QByteArray> TelemtConfigModel::roleNames() const {
QHash<int, QByteArray> roles;

View File

@@ -116,44 +116,12 @@ public slots:
Q_INVOKABLE QString workersModeManual() const;
Q_INVOKABLE bool isValidPublicHost(const QString &host) const;
Q_INVOKABLE bool isPublicHostInputAllowed(const QString &text) const;
Q_INVOKABLE bool isPublicHostTypingIncomplete(const QString &text) const;
Q_INVOKABLE bool isValidMtProxyTag(const QString &tag) const;
Q_INVOKABLE bool isMtProxyTagTypingIncomplete(const QString &text) const;
Q_INVOKABLE int mtProxyBotTagHexLength() const;
Q_INVOKABLE bool isValidFakeTlsDomain(const QString &domain) const;
Q_INVOKABLE bool isFakeTlsDomainTypingIncomplete(const QString &text) const;
Q_INVOKABLE bool isFakeTlsDomainInputAllowed(const QString &text) const;
Q_INVOKABLE QString sanitizeFakeTlsDomainFieldText(const QString &input) const;
Q_INVOKABLE QString sanitizePublicHostFieldText(const QString &input) const;
Q_INVOKABLE QString sanitizePortFieldText(const QString &input) const;
Q_INVOKABLE QString sanitizeMtProxyTagFieldText(const QString &input) const;
Q_INVOKABLE QString sanitizeOptionalIpv4FieldText(const QString &input) const;
Q_INVOKABLE bool isValidOptionalIpv4(const QString &ip) const;
protected:
QHash<int, QByteArray> roleNames() const override;
private:
static void applyDefaults(amnezia::TelemtProtocolConfig &c);
QString normalizeFakeTlsDomainInput(const QString &input) const;
amnezia::DockerContainer m_container = amnezia::DockerContainer::None;
QJsonObject m_fullConfig;
amnezia::TelemtProtocolConfig m_protocolConfig;

View File

@@ -1017,7 +1017,6 @@ PageType {
headerText: qsTr("Server port")
textField.placeholderText: MtProxyConfigModel.defaultPort()
textField.maximumLength: 5
textField.inputMethodHints: Qt.ImhDigitsOnly
textField.validator: IntValidator {
bottom: 1
top: 65535
@@ -1026,16 +1025,8 @@ PageType {
var savedPort = port
textField.text = (savedPort === MtProxyConfigModel.defaultPort()) ? "" : savedPort
}
textField.onTextChanged: {
var cur = portTextField.textField.text
var clean = MtProxyConfigModel.sanitizePortFieldText(cur)
if (clean !== cur) {
textField.text = clean
textField.cursorPosition = clean.length
}
}
textField.onEditingFinished: {
textField.text = MtProxyConfigModel.sanitizePortFieldText(textField.text)
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
}
}
@@ -1189,22 +1180,10 @@ PageType {
visible: transportMode === "faketls"
headerText: qsTr("FakeTLS domain")
textField.placeholderText: root.previousTlsDomain
textField.validator: RegularExpressionValidator {
regularExpression: /^[A-Za-z0-9.-]*$/
}
Component.onCompleted: {
var savedDomain = tlsDomain
textField.text = (savedDomain === MtProxyConfigModel.defaultTlsDomain() || savedDomain === "") ? "" : savedDomain
}
textField.onTextChanged: {
var t = tlsDomainTextField.textField.text
if (t === "" || MtProxyConfigModel.isFakeTlsDomainTypingIncomplete(t)
|| MtProxyConfigModel.isValidFakeTlsDomain(t)) {
tlsDomainTextField.errorText = ""
} else {
tlsDomainTextField.errorText = qsTr("Enter a valid domain name")
}
}
textField.onEditingFinished: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
var domainValue = textField.text === "" ? MtProxyConfigModel.defaultTlsDomain() : textField.text
@@ -1581,41 +1560,15 @@ PageType {
headerText: qsTr("Workers count")
textField.placeholderText: "2"
textField.text: workers
textField.maximumLength: 2
textField.inputMethodHints: Qt.ImhDigitsOnly
// Range input like the port field: IntValidator bounds the value and the
// clamp keeps it within 0..maxWorkers on every change (rejects 33+, neg.).
textField.maximumLength: 3
textField.validator: IntValidator {
bottom: 0
bottom: 1
top: MtProxyConfigModel.maxWorkers()
}
textField.onTextChanged: {
var cur = workersTextField.textField.text
if (cur === "") {
return
}
var n = parseInt(cur, 10)
var maxW = MtProxyConfigModel.maxWorkers()
if (isNaN(n) || n < 0) { n = 0 }
if (n > maxW) { n = maxW }
var clamped = String(n)
if (clamped !== cur) {
textField.text = clamped
textField.cursorPosition = clamped.length
}
}
textField.onEditingFinished: {
var v = workersTextField.textField.text
if (v !== "") {
var m = parseInt(v, 10)
var maxW2 = MtProxyConfigModel.maxWorkers()
if (isNaN(m) || m < 0) { m = 0 }
if (m > maxW2) { m = maxW2 }
v = String(m)
textField.text = v
}
if (v !== workers) {
workers = v
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
if (textField.text !== workers) {
workers = textField.text
MtProxyConfigModel.setWorkers(workers)
}
}

View File

@@ -8,7 +8,6 @@ import PageEnum 1.0
import ContainerProps 1.0
import ProtocolEnum 1.0
import Style 1.0
import TelemtConfig 1.0
import "./"
import "../Controls2"
@@ -42,35 +41,6 @@ PageType {
property string savedTlsDomain: ""
property string savedPublicHost: ""
readonly property var natIpv4InputFormat: /^(\d{1,3}\.){0,3}\d{0,3}$/
function natIpv4FieldShowInvalidError(text) {
var t = text ? String(text).replace(/^\s+|\s+$/g, '') : ""
if (t === "")
return false
if (TelemtConfigModel.isValidOptionalIpv4(t))
return false
var parts = t.split('.')
var j
for (j = 0; j < parts.length; j++) {
if (parts[j].length > 3)
return true
}
if (parts.length > 4)
return true
if (t.indexOf('.') < 0 && t.length > 3)
return true
if (t.endsWith('.'))
return false
if (parts.length < 4)
return false
for (var i = 0; i < parts.length; i++) {
if (parts[i] === "")
return true
}
return true
}
onSavedTransportModeChanged: {
if (savedTransportMode === "faketls") {
root.syncedSecretTabIndex = 1
@@ -919,26 +889,8 @@ PageType {
headerText: qsTr("Public host / IP")
textField.placeholderText: ServersUiController.serverHostName(ServersUiController.processedServerId)
textField.text: publicHost
textField.maximumLength: 253
textField.validator: PublicHostInputValidator {
}
textField.onTextChanged: {
var t = publicHostTextField.textField.text
if (TelemtConfigModel.isPublicHostTypingIncomplete(t)) {
publicHostTextField.errorText = ""
} else if (!TelemtConfigModel.isValidPublicHost(t)) {
publicHostTextField.errorText = qsTr("Enter a valid IP address or domain name")
} else {
publicHostTextField.errorText = ""
}
}
textField.onEditingFinished: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
if (!TelemtConfigModel.isValidPublicHost(textField.text)) {
publicHostTextField.errorText = qsTr("Enter a valid IP address or domain name")
return
}
publicHostTextField.errorText = ""
if (textField.text !== publicHost) {
publicHost = textField.text
TelemtConfigModel.setPublicHost(publicHost)
@@ -980,7 +932,6 @@ PageType {
headerText: qsTr("Server port")
textField.placeholderText: TelemtConfigModel.defaultPort()
textField.maximumLength: 5
textField.inputMethodHints: Qt.ImhDigitsOnly
textField.validator: IntValidator {
bottom: 1
top: 65535
@@ -989,16 +940,8 @@ PageType {
var savedPort = port
textField.text = (savedPort === TelemtConfigModel.defaultPort()) ? "" : savedPort
}
textField.onTextChanged: {
var cur = portTextField.textField.text
var clean = TelemtConfigModel.sanitizePortFieldText(cur)
if (clean !== cur) {
textField.text = clean
textField.cursorPosition = clean.length
}
}
textField.onEditingFinished: {
textField.text = TelemtConfigModel.sanitizePortFieldText(textField.text)
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
var portValue = textField.text === "" ? TelemtConfigModel.defaultPort() : textField.text
if (portValue !== port) {
port = portValue
@@ -1026,43 +969,13 @@ PageType {
Layout.rightMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("Promoted channel tag (optional)")
textField.placeholderText: qsTr("32 hex chars from @MTProxyBot (e.g. 3b7b2fa9…)")
textField.placeholderText: qsTr("leave empty if not needed")
textField.text: tag
textField.maximumLength: TelemtConfigModel.mtProxyBotTagHexLength()
textField.onTextChanged: {
var cur = tagTextField.textField.text
var clean = TelemtConfigModel.sanitizeMtProxyTagFieldText(cur)
if (clean !== cur) {
textField.text = clean
textField.cursorPosition = clean.length
return
}
var tt = tagTextField.textField.text
if (tt === "") {
tagTextField.errorText = ""
return
}
if (TelemtConfigModel.isMtProxyTagTypingIncomplete(tt)) {
tagTextField.errorText = ""
return
}
if (!TelemtConfigModel.isValidMtProxyTag(tt)) {
tagTextField.errorText = qsTr("Proxy tag must be exactly 32 hexadecimal characters (0-9, A-F).")
return
}
tagTextField.errorText = ""
}
textField.maximumLength: 64
textField.onEditingFinished: {
var raw = textField.text.replace(/^\s+|\s+$/g, '')
var normalized = TelemtConfigModel.sanitizeMtProxyTagFieldText(raw)
textField.text = normalized
if (!TelemtConfigModel.isValidMtProxyTag(normalized)) {
tagTextField.errorText = qsTr("Proxy tag must be exactly 32 hexadecimal characters (0-9, A-F). Leave empty if unused.")
return
}
tagTextField.errorText = ""
if (normalized !== tag) {
tag = normalized
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
if (textField.text !== tag) {
tag = textField.text
TelemtConfigModel.setTag(tag)
}
}
@@ -1092,26 +1005,17 @@ PageType {
}
}
CaptionTextType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 16 * 2
text: qsTr("Transport mode")
color: AmneziaStyle.color.mutedGray
font.pixelSize: 12
}
DropDownType {
id: transportModeDropDown
Layout.fillWidth: true
Layout.topMargin: 16 * 2
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 16
drawerParent: root
drawerHeight: 0.35
headerText: qsTr("Transport mode")
descriptionText: qsTr("Transport mode")
text: transportMode === "faketls" ? qsTr("FakeTLS") : qsTr("Standard MTProto")
listView: Component {
@@ -1146,30 +1050,13 @@ PageType {
visible: transportMode === "faketls"
headerText: qsTr("FakeTLS domain")
textField.placeholderText: root.previousTlsDomain
textField.validator: RegularExpressionValidator {
regularExpression: /^[A-Za-z0-9.-]*$/
}
Component.onCompleted: {
var savedDomain = tlsDomain
textField.text = (savedDomain === TelemtConfigModel.defaultTlsDomain() || savedDomain === "") ? "" : savedDomain
}
textField.onTextChanged: {
var t = tlsDomainTextField.textField.text
if (t === "" || TelemtConfigModel.isFakeTlsDomainTypingIncomplete(t)
|| TelemtConfigModel.isValidFakeTlsDomain(t)) {
tlsDomainTextField.errorText = ""
} else {
tlsDomainTextField.errorText = qsTr("Enter a valid domain name")
}
}
textField.onEditingFinished: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
var domainValue = textField.text === "" ? TelemtConfigModel.defaultTlsDomain() : textField.text
if (!TelemtConfigModel.isValidFakeTlsDomain(domainValue)) {
tlsDomainTextField.errorText = qsTr("Enter a valid domain name")
return
}
tlsDomainTextField.errorText = ""
if (domainValue !== tlsDomain) {
tlsDomain = domainValue
TelemtConfigModel.setTlsDomain(tlsDomain)
@@ -1356,41 +1243,15 @@ PageType {
headerText: qsTr("Workers count")
textField.placeholderText: "2"
textField.text: workers
textField.maximumLength: 2
textField.inputMethodHints: Qt.ImhDigitsOnly
// Range input like the port field: IntValidator bounds the value and the
// clamp keeps it within 0..maxWorkers on every change (rejects 33+, neg.).
textField.maximumLength: 3
textField.validator: IntValidator {
bottom: 0
bottom: 1
top: TelemtConfigModel.maxWorkers()
}
textField.onTextChanged: {
var cur = workersTextField.textField.text
if (cur === "") {
return
}
var n = parseInt(cur, 10)
var maxW = TelemtConfigModel.maxWorkers()
if (isNaN(n) || n < 0) { n = 0 }
if (n > maxW) { n = maxW }
var clamped = String(n)
if (clamped !== cur) {
textField.text = clamped
textField.cursorPosition = clamped.length
}
}
textField.onEditingFinished: {
var v = workersTextField.textField.text
if (v !== "") {
var m = parseInt(v, 10)
var maxW2 = TelemtConfigModel.maxWorkers()
if (isNaN(m) || m < 0) { m = 0 }
if (m > maxW2) { m = maxW2 }
v = String(m)
textField.text = v
}
if (v !== workers) {
workers = v
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
if (textField.text !== workers) {
workers = textField.text
TelemtConfigModel.setWorkers(workers)
}
}
@@ -1427,24 +1288,8 @@ PageType {
headerText: qsTr("Internal IP")
textField.placeholderText: "172.17.0.2"
textField.text: natInternalIp
textField.maximumLength: 15
textField.validator: RegularExpressionValidator {
regularExpression: root.natIpv4InputFormat
}
textField.onTextChanged: {
if (root.natIpv4FieldShowInvalidError(textField.text)) {
natInternalIpTextField.errorText = qsTr("Enter a valid IPv4 address")
} else {
natInternalIpTextField.errorText = ""
}
}
textField.onEditingFinished: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
if (!TelemtConfigModel.isValidOptionalIpv4(textField.text)) {
natInternalIpTextField.errorText = qsTr("Enter a valid IPv4 address")
return
}
natInternalIpTextField.errorText = ""
if (textField.text !== natInternalIp) {
natInternalIp = textField.text
TelemtConfigModel.setNatInternalIp(natInternalIp)
@@ -1462,24 +1307,8 @@ PageType {
headerText: qsTr("External IP")
textField.placeholderText: "1.2.3.4"
textField.text: natExternalIp
textField.maximumLength: 15
textField.validator: RegularExpressionValidator {
regularExpression: root.natIpv4InputFormat
}
textField.onTextChanged: {
if (root.natIpv4FieldShowInvalidError(textField.text)) {
natExternalIpTextField.errorText = qsTr("Enter a valid IPv4 address")
} else {
natExternalIpTextField.errorText = ""
}
}
textField.onEditingFinished: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
if (!TelemtConfigModel.isValidOptionalIpv4(textField.text)) {
natExternalIpTextField.errorText = qsTr("Enter a valid IPv4 address")
return
}
natExternalIpTextField.errorText = ""
if (textField.text !== natExternalIp) {
natExternalIp = textField.text
TelemtConfigModel.setNatExternalIp(natExternalIp)

View File

@@ -320,7 +320,7 @@ PageType {
Layout.rightMargin: 16
visible: isQrCodeVisible
horizontalAlignment: Text.AlignHCenter
text: qsTr("To read the QR code in the Amnezia app, tap + in the main menu'QR code'")
text: qsTr("To read the QR code in the Amnezia app, select \"Add server\" → \"I have data to connect\"\"QR code, key or settings file\"")
}
}
}

View File

@@ -650,6 +650,9 @@ class OpenSSLConan(ConanFile):
if self._use_nmake:
self.cpp_info.components["ssl"].libs = ["libssl"]
self.cpp_info.components["crypto"].libs = ["libcrypto"]
elif self.settings.os == "Android" and self.options.shared:
self.cpp_info.components["ssl"].libs = ["ssl_3"]
self.cpp_info.components["crypto"].libs = ["crypto_3"]
else:
self.cpp_info.components["ssl"].libs = ["ssl"]
self.cpp_info.components["crypto"].libs = ["crypto"]