mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-20 02:00:55 +07:00
fix Public host|Port|Promoted tag|FakeTLS domain|Internal/External IP
This commit is contained in:
@@ -271,6 +271,7 @@ namespace amnezia
|
|||||||
constexpr char workersModeAuto[] = "auto";
|
constexpr char workersModeAuto[] = "auto";
|
||||||
constexpr char workersModeManual[] = "manual";
|
constexpr char workersModeManual[] = "manual";
|
||||||
constexpr int maxWorkers = 32;
|
constexpr int maxWorkers = 32;
|
||||||
|
constexpr int botTagHexLength = 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace protocols
|
} // namespace protocols
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
#include "telemtConfigModel.h"
|
#include "telemtConfigModel.h"
|
||||||
|
|
||||||
#include <QRegularExpression>
|
#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/qrCodeUtils.h"
|
||||||
#include "core/utils/constants/configKeys.h"
|
#include "core/utils/constants/configKeys.h"
|
||||||
#include "core/utils/constants/protocolConstants.h"
|
#include "core/utils/constants/protocolConstants.h"
|
||||||
@@ -9,7 +15,9 @@
|
|||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
|
|
||||||
TelemtConfigModel::TelemtConfigModel(QObject *parent) : QAbstractListModel(parent) {}
|
TelemtConfigModel::TelemtConfigModel(QObject *parent) : QAbstractListModel(parent) {
|
||||||
|
qmlRegisterType<PublicHostInputValidator>("TelemtConfig", 1, 0, "PublicHostInputValidator");
|
||||||
|
}
|
||||||
|
|
||||||
void TelemtConfigModel::applyDefaults(TelemtProtocolConfig &c) {
|
void TelemtConfigModel::applyDefaults(TelemtProtocolConfig &c) {
|
||||||
if (c.port.isEmpty()) {
|
if (c.port.isEmpty()) {
|
||||||
@@ -49,7 +57,11 @@ bool TelemtConfigModel::setData(const QModelIndex &index, const QVariant &value,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Roles::TagRole: {
|
case Roles::TagRole: {
|
||||||
m_protocolConfig.tag = value.toString();
|
const QString tag = sanitizeMtProxyTagFieldText(value.toString());
|
||||||
|
if (!isValidMtProxyTag(tag)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_protocolConfig.tag = tag;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Roles::IsEnabledRole: {
|
case Roles::IsEnabledRole: {
|
||||||
@@ -57,7 +69,11 @@ bool TelemtConfigModel::setData(const QModelIndex &index, const QVariant &value,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Roles::PublicHostRole: {
|
case Roles::PublicHostRole: {
|
||||||
m_protocolConfig.publicHost = value.toString();
|
const QString h = value.toString().trimmed();
|
||||||
|
if (!isValidPublicHost(h)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_protocolConfig.publicHost = h;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Roles::TransportModeRole: {
|
case Roles::TransportModeRole: {
|
||||||
@@ -65,7 +81,11 @@ bool TelemtConfigModel::setData(const QModelIndex &index, const QVariant &value,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Roles::TlsDomainRole: {
|
case Roles::TlsDomainRole: {
|
||||||
m_protocolConfig.tlsDomain = value.toString();
|
const QString d = value.toString().trimmed();
|
||||||
|
if (!isValidFakeTlsDomain(d)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_protocolConfig.tlsDomain = d;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Roles::AdditionalSecretsRole: {
|
case Roles::AdditionalSecretsRole: {
|
||||||
@@ -85,11 +105,19 @@ bool TelemtConfigModel::setData(const QModelIndex &index, const QVariant &value,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Roles::NatInternalIpRole: {
|
case Roles::NatInternalIpRole: {
|
||||||
m_protocolConfig.natInternalIp = value.toString();
|
const QString ip = value.toString().trimmed();
|
||||||
|
if (!isValidOptionalIpv4(ip)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_protocolConfig.natInternalIp = ip;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Roles::NatExternalIpRole: {
|
case Roles::NatExternalIpRole: {
|
||||||
m_protocolConfig.natExternalIp = value.toString();
|
const QString ip = value.toString().trimmed();
|
||||||
|
if (!isValidOptionalIpv4(ip)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_protocolConfig.natExternalIp = ip;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Roles::MaskEnabledRole: {
|
case Roles::MaskEnabledRole: {
|
||||||
@@ -379,6 +407,293 @@ QString TelemtConfigModel::workersModeManual() const {
|
|||||||
return QString::fromUtf8(protocols::telemt::workersModeManual);
|
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> TelemtConfigModel::roleNames() const {
|
||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
|
|
||||||
|
|||||||
@@ -116,12 +116,44 @@ public slots:
|
|||||||
|
|
||||||
Q_INVOKABLE QString workersModeManual() const;
|
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:
|
protected:
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void applyDefaults(amnezia::TelemtProtocolConfig &c);
|
static void applyDefaults(amnezia::TelemtProtocolConfig &c);
|
||||||
|
|
||||||
|
QString normalizeFakeTlsDomainInput(const QString &input) const;
|
||||||
|
|
||||||
amnezia::DockerContainer m_container = amnezia::DockerContainer::None;
|
amnezia::DockerContainer m_container = amnezia::DockerContainer::None;
|
||||||
QJsonObject m_fullConfig;
|
QJsonObject m_fullConfig;
|
||||||
amnezia::TelemtProtocolConfig m_protocolConfig;
|
amnezia::TelemtProtocolConfig m_protocolConfig;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import PageEnum 1.0
|
|||||||
import ContainerProps 1.0
|
import ContainerProps 1.0
|
||||||
import ProtocolEnum 1.0
|
import ProtocolEnum 1.0
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
import TelemtConfig 1.0
|
||||||
|
|
||||||
import "./"
|
import "./"
|
||||||
import "../Controls2"
|
import "../Controls2"
|
||||||
@@ -41,6 +42,35 @@ PageType {
|
|||||||
property string savedTlsDomain: ""
|
property string savedTlsDomain: ""
|
||||||
property string savedPublicHost: ""
|
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: {
|
onSavedTransportModeChanged: {
|
||||||
if (savedTransportMode === "faketls") {
|
if (savedTransportMode === "faketls") {
|
||||||
root.syncedSecretTabIndex = 1
|
root.syncedSecretTabIndex = 1
|
||||||
@@ -889,8 +919,26 @@ PageType {
|
|||||||
headerText: qsTr("Public host / IP")
|
headerText: qsTr("Public host / IP")
|
||||||
textField.placeholderText: ServersUiController.serverHostName(ServersUiController.processedServerId)
|
textField.placeholderText: ServersUiController.serverHostName(ServersUiController.processedServerId)
|
||||||
textField.text: publicHost
|
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.onEditingFinished: {
|
||||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
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) {
|
if (textField.text !== publicHost) {
|
||||||
publicHost = textField.text
|
publicHost = textField.text
|
||||||
TelemtConfigModel.setPublicHost(publicHost)
|
TelemtConfigModel.setPublicHost(publicHost)
|
||||||
@@ -932,6 +980,7 @@ PageType {
|
|||||||
headerText: qsTr("Server port")
|
headerText: qsTr("Server port")
|
||||||
textField.placeholderText: TelemtConfigModel.defaultPort()
|
textField.placeholderText: TelemtConfigModel.defaultPort()
|
||||||
textField.maximumLength: 5
|
textField.maximumLength: 5
|
||||||
|
textField.inputMethodHints: Qt.ImhDigitsOnly
|
||||||
textField.validator: IntValidator {
|
textField.validator: IntValidator {
|
||||||
bottom: 1
|
bottom: 1
|
||||||
top: 65535
|
top: 65535
|
||||||
@@ -940,8 +989,16 @@ PageType {
|
|||||||
var savedPort = port
|
var savedPort = port
|
||||||
textField.text = (savedPort === TelemtConfigModel.defaultPort()) ? "" : savedPort
|
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.onEditingFinished: {
|
||||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
textField.text = TelemtConfigModel.sanitizePortFieldText(textField.text)
|
||||||
var portValue = textField.text === "" ? TelemtConfigModel.defaultPort() : textField.text
|
var portValue = textField.text === "" ? TelemtConfigModel.defaultPort() : textField.text
|
||||||
if (portValue !== port) {
|
if (portValue !== port) {
|
||||||
port = portValue
|
port = portValue
|
||||||
@@ -969,13 +1026,43 @@ PageType {
|
|||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.bottomMargin: 16
|
Layout.bottomMargin: 16
|
||||||
headerText: qsTr("Promoted channel tag (optional)")
|
headerText: qsTr("Promoted channel tag (optional)")
|
||||||
textField.placeholderText: qsTr("leave empty if not needed")
|
textField.placeholderText: qsTr("32 hex chars from @MTProxyBot (e.g. 3b7b2fa9…)")
|
||||||
textField.text: tag
|
textField.text: tag
|
||||||
textField.maximumLength: 64
|
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.onEditingFinished: {
|
textField.onEditingFinished: {
|
||||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
var raw = textField.text.replace(/^\s+|\s+$/g, '')
|
||||||
if (textField.text !== tag) {
|
var normalized = TelemtConfigModel.sanitizeMtProxyTagFieldText(raw)
|
||||||
tag = textField.text
|
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
|
||||||
TelemtConfigModel.setTag(tag)
|
TelemtConfigModel.setTag(tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1059,13 +1146,30 @@ PageType {
|
|||||||
visible: transportMode === "faketls"
|
visible: transportMode === "faketls"
|
||||||
headerText: qsTr("FakeTLS domain")
|
headerText: qsTr("FakeTLS domain")
|
||||||
textField.placeholderText: root.previousTlsDomain
|
textField.placeholderText: root.previousTlsDomain
|
||||||
|
textField.validator: RegularExpressionValidator {
|
||||||
|
regularExpression: /^[A-Za-z0-9.-]*$/
|
||||||
|
}
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
var savedDomain = tlsDomain
|
var savedDomain = tlsDomain
|
||||||
textField.text = (savedDomain === TelemtConfigModel.defaultTlsDomain() || savedDomain === "") ? "" : savedDomain
|
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.onEditingFinished: {
|
||||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
||||||
var domainValue = textField.text === "" ? TelemtConfigModel.defaultTlsDomain() : textField.text
|
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) {
|
if (domainValue !== tlsDomain) {
|
||||||
tlsDomain = domainValue
|
tlsDomain = domainValue
|
||||||
TelemtConfigModel.setTlsDomain(tlsDomain)
|
TelemtConfigModel.setTlsDomain(tlsDomain)
|
||||||
@@ -1323,8 +1427,24 @@ PageType {
|
|||||||
headerText: qsTr("Internal IP")
|
headerText: qsTr("Internal IP")
|
||||||
textField.placeholderText: "172.17.0.2"
|
textField.placeholderText: "172.17.0.2"
|
||||||
textField.text: natInternalIp
|
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.onEditingFinished: {
|
||||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
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) {
|
if (textField.text !== natInternalIp) {
|
||||||
natInternalIp = textField.text
|
natInternalIp = textField.text
|
||||||
TelemtConfigModel.setNatInternalIp(natInternalIp)
|
TelemtConfigModel.setNatInternalIp(natInternalIp)
|
||||||
@@ -1342,8 +1462,24 @@ PageType {
|
|||||||
headerText: qsTr("External IP")
|
headerText: qsTr("External IP")
|
||||||
textField.placeholderText: "1.2.3.4"
|
textField.placeholderText: "1.2.3.4"
|
||||||
textField.text: natExternalIp
|
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.onEditingFinished: {
|
||||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
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) {
|
if (textField.text !== natExternalIp) {
|
||||||
natExternalIp = textField.text
|
natExternalIp = textField.text
|
||||||
TelemtConfigModel.setNatExternalIp(natExternalIp)
|
TelemtConfigModel.setNatExternalIp(natExternalIp)
|
||||||
|
|||||||
Reference in New Issue
Block a user