mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-16 15:23:03 +03:00
Compare commits
2 Commits
fix/mtprox
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56ab82f87f | ||
|
|
3984acbb44 |
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -271,7 +271,6 @@ namespace amnezia
|
||||
constexpr char workersModeAuto[] = "auto";
|
||||
constexpr char workersModeManual[] = "manual";
|
||||
constexpr int maxWorkers = 32;
|
||||
constexpr int botTagHexLength = 32;
|
||||
}
|
||||
|
||||
} // namespace protocols
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -21,7 +21,6 @@ enum SharedScriptType {
|
||||
// General scripts
|
||||
prepare_host,
|
||||
install_docker,
|
||||
install_conntrack,
|
||||
build_container,
|
||||
remove_container,
|
||||
remove_all_containers,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/.*\///');\
|
||||
|
||||
@@ -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"
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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+$)"));
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"]
|
||||
|
||||
Reference in New Issue
Block a user