mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-17 15:53:10 +03:00
Compare commits
12 Commits
feature/al
...
user-check
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adaea28627 | ||
|
|
9fbea76b74 | ||
|
|
b3ff120bcf | ||
|
|
9dea98f020 | ||
|
|
c4701d4e7a | ||
|
|
48903ca3a1 | ||
|
|
0c9fd4aef4 | ||
|
|
b2af2e46ac | ||
|
|
efc76a0683 | ||
|
|
c4a553c166 | ||
|
|
69a00b0252 | ||
|
|
4257c08b43 |
20
README_RU.md
20
README_RU.md
@@ -6,11 +6,11 @@
|
||||
[](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
|
||||
|
||||
### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
|
||||
[AmneziaVPN](https://amnezia.org) — это open sourse VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
|
||||
[AmneziaVPN](https://amnezia.org) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
|
||||
|
||||
[](https://amnezia.org)
|
||||
|
||||
### [Сайт](https://amnezia.org) | [Зеркало на сайт](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
|
||||
### [Сайт](https://amnezia.org) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
|
||||
|
||||
> [!TIP]
|
||||
> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org).
|
||||
@@ -30,7 +30,7 @@
|
||||
- Классические VPN-протоколы: OpenVPN, WireGuard и IKEv2.
|
||||
- Протоколы с маскировкой трафика (обфускацией): OpenVPN с плагином [Cloak](https://github.com/cbeuw/Cloak), Shadowsocks (OpenVPN over Shadowsocks), [AmneziaWG](https://docs.amnezia.org/documentation/amnezia-wg/) and XRay.
|
||||
- Поддержка Split Tunneling — добавляйте любые сайты или приложения в список, чтобы включить VPN только для них.
|
||||
- Поддерживает платформы: Windows, MacOS, Linux, Android, iOS.
|
||||
- Поддерживает платформы: Windows, macOS, Linux, Android, iOS.
|
||||
- Поддержка конфигурации протокола AmneziaWG на [бета-прошивке Keenetic](https://docs.keenetic.com/ua/air/kn-1611/en/6319-latest-development-release.html#UUID-186c4108-5afd-c10b-f38a-cdff6c17fab3_section-idm33192196168192-improved).
|
||||
|
||||
## Ссылки
|
||||
@@ -38,10 +38,10 @@
|
||||
- [https://amnezia.org](https://amnezia.org) - Веб-сайт проекта | [Альтернативная ссылка (зеркало)](https://storage.googleapis.com/kldscp/amnezia.org)
|
||||
- [https://docs.amnezia.org](https://docs.amnezia.org) - Документация
|
||||
- [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
|
||||
- [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Канал поддржки в Telegram (Английский)
|
||||
- [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Канал поддржки в Telegram (Фарси)
|
||||
- [https://t.me/amnezia_vpn_mm](https://t.me/amnezia_vpn_mm) - Канал поддржки в Telegram (Мьянма)
|
||||
- [https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Канал поддржки в Telegram (Русский)
|
||||
- [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Канал поддержки в Telegram (Английский)
|
||||
- [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Канал поддержки в Telegram (Фарси)
|
||||
- [https://t.me/amnezia_vpn_mm](https://t.me/amnezia_vpn_mm) - Канал поддержки в Telegram (Мьянма)
|
||||
- [https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Канал поддержки в Telegram (Русский)
|
||||
- [https://vpnpay.io/en/amnezia-premium/](https://vpnpay.io/en/amnezia-premium/) - Amnezia Premium | [Зеркало](https://storage.googleapis.com/kldscp/vpnpay.io/ru/amnezia-premium\)
|
||||
|
||||
## Технологии
|
||||
@@ -80,8 +80,8 @@ git submodule update --init --recursive
|
||||
Проверьте папку deploy для скриптов сборки.
|
||||
|
||||
### Как собрать iOS-приложение из исходного кода на MacOS
|
||||
1. Убедитесь, что у вас установлен XCode версии 14 или выше.
|
||||
2. Для генерации проекта XCode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули:
|
||||
1. Убедитесь, что у вас установлен Xcode версии 14 или выше.
|
||||
2. Для генерации проекта Xcode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули:
|
||||
- MacOS
|
||||
- iOS
|
||||
- Модуль совместимости с Qt 5
|
||||
@@ -117,7 +117,7 @@ $QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR
|
||||
export PATH=$(PATH):/path/to/GOPATH/bin
|
||||
```
|
||||
|
||||
6. Откройте проект в XCode. Теперь вы можете тестировать, архивировать или публиковать приложение.
|
||||
6. Откройте проект в Xcode. Теперь вы можете тестировать, архивировать или публиковать приложение.
|
||||
|
||||
Если сборка завершится с ошибкой:
|
||||
```
|
||||
|
||||
Submodule client/3rd-prebuilt updated: e555c78bcf...efad1a5b5c
@@ -31,10 +31,6 @@ add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
|
||||
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
|
||||
add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}")
|
||||
|
||||
if(IOS)
|
||||
set(PACKAGES ${PACKAGES} Multimedia)
|
||||
endif()
|
||||
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
set(PACKAGES ${PACKAGES} Widgets)
|
||||
endif()
|
||||
@@ -48,10 +44,6 @@ set(LIBS ${LIBS}
|
||||
Qt6::Core5Compat Qt6::Concurrent
|
||||
)
|
||||
|
||||
if(IOS)
|
||||
set(LIBS ${LIBS} Qt6::Multimedia)
|
||||
endif()
|
||||
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
set(LIBS ${LIBS} Qt6::Widgets)
|
||||
endif()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QProcess>
|
||||
#include <QRegularExpression>
|
||||
#include <QString>
|
||||
#include <QTemporaryDir>
|
||||
#include <QTemporaryFile>
|
||||
@@ -19,13 +20,17 @@
|
||||
#include "settings.h"
|
||||
#include "utilities.h"
|
||||
|
||||
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
||||
bool isAwg, QObject *parent)
|
||||
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings,
|
||||
const QSharedPointer<ServerController> &serverController, bool isAwg,
|
||||
QObject *parent)
|
||||
: ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg)
|
||||
{
|
||||
m_serverConfigPath = m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
|
||||
m_serverPublicKeyPath = m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath;
|
||||
m_serverPskKeyPath = m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
|
||||
m_serverConfigPath =
|
||||
m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
|
||||
m_serverPublicKeyPath =
|
||||
m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath;
|
||||
m_serverPskKeyPath =
|
||||
m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
|
||||
m_configTemplate = m_isAwg ? ProtocolScriptType::awg_template : ProtocolScriptType::wireguard_template;
|
||||
|
||||
m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard;
|
||||
@@ -63,9 +68,31 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
|
||||
return connData;
|
||||
}
|
||||
|
||||
QList<QHostAddress> WireguardConfigurator::getIpsFromConf(const QString &input)
|
||||
{
|
||||
QRegularExpression regex("AllowedIPs = (\\d+\\.\\d+\\.\\d+\\.\\d+)");
|
||||
QRegularExpressionMatchIterator matchIterator = regex.globalMatch(input);
|
||||
|
||||
QList<QHostAddress> ips;
|
||||
|
||||
while (matchIterator.hasNext()) {
|
||||
QRegularExpressionMatch match = matchIterator.next();
|
||||
const QString address_string { match.captured(1) };
|
||||
const QHostAddress address { address_string };
|
||||
if (address.isNull()) {
|
||||
qWarning() << "Couldn't recognize the ip address: " << address_string;
|
||||
} else {
|
||||
ips << address;
|
||||
}
|
||||
}
|
||||
|
||||
return ips;
|
||||
}
|
||||
|
||||
WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
|
||||
DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
||||
const QJsonObject &containerConfig,
|
||||
ErrorCode &errorCode)
|
||||
{
|
||||
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
|
||||
connData.host = credentials.hostName;
|
||||
@@ -76,65 +103,45 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
||||
return connData;
|
||||
}
|
||||
|
||||
// Get list of already created clients (only IP addresses)
|
||||
QString nextIpNumber;
|
||||
{
|
||||
QString script = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
stdOut += data + "\n";
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
QString getIpsScript = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
stdOut += data + "\n";
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
|
||||
errorCode = m_serverController->runContainerScript(credentials, container, script, cbReadStdOut);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return connData;
|
||||
}
|
||||
errorCode = m_serverController->runContainerScript(credentials, container, getIpsScript, cbReadStdOut);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return connData;
|
||||
}
|
||||
auto ips = getIpsFromConf(stdOut);
|
||||
|
||||
stdOut.replace("AllowedIPs = ", "");
|
||||
stdOut.replace("/32", "");
|
||||
QStringList ips = stdOut.split("\n", Qt::SkipEmptyParts);
|
||||
|
||||
// remove extra IPs from each line for case when user manually edited the wg0.conf
|
||||
// and added there more IPs for route his itnernal networks, like:
|
||||
// ...
|
||||
// AllowedIPs = 10.8.1.6/32, 192.168.1.0/24, 192.168.2.0/24, ...
|
||||
// ...
|
||||
// without this code - next IP would be 1 if last item in 'ips' has format above
|
||||
QStringList vpnIps;
|
||||
for (const auto &ip : ips) {
|
||||
vpnIps.append(ip.split(",", Qt::SkipEmptyParts).first().trimmed());
|
||||
}
|
||||
ips = vpnIps;
|
||||
|
||||
// Calc next IP address
|
||||
if (ips.isEmpty()) {
|
||||
nextIpNumber = "2";
|
||||
QHostAddress nextIp = [&] {
|
||||
QHostAddress result;
|
||||
QHostAddress lastIp;
|
||||
if (ips.empty()) {
|
||||
lastIp.setAddress(containerConfig.value(m_protocolName)
|
||||
.toObject()
|
||||
.value(config_key::subnet_address)
|
||||
.toString(protocols::wireguard::defaultSubnetAddress));
|
||||
} else {
|
||||
int next = ips.last().split(".").last().toInt() + 1;
|
||||
if (next > 254) {
|
||||
errorCode = ErrorCode::AddressPoolError;
|
||||
return connData;
|
||||
}
|
||||
nextIpNumber = QString::number(next);
|
||||
lastIp = ips.last();
|
||||
}
|
||||
}
|
||||
|
||||
QString subnetIp = containerConfig.value(m_protocolName).toObject().value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
|
||||
{
|
||||
QStringList l = subnetIp.split(".", Qt::SkipEmptyParts);
|
||||
if (l.isEmpty()) {
|
||||
errorCode = ErrorCode::AddressPoolError;
|
||||
return connData;
|
||||
quint8 lastOctet = static_cast<quint8>(lastIp.toIPv4Address());
|
||||
switch (lastOctet) {
|
||||
case 254: result.setAddress(lastIp.toIPv4Address() + 3); break;
|
||||
case 255: result.setAddress(lastIp.toIPv4Address() + 2); break;
|
||||
default: result.setAddress(lastIp.toIPv4Address() + 1); break;
|
||||
}
|
||||
l.removeLast();
|
||||
l.append(nextIpNumber);
|
||||
|
||||
connData.clientIP = l.join(".");
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
|
||||
connData.clientIP = nextIp.toString();
|
||||
|
||||
// Get keys
|
||||
connData.serverPubKey = m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
|
||||
connData.serverPubKey =
|
||||
m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
|
||||
connData.serverPubKey.replace("\n", "");
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return connData;
|
||||
@@ -161,10 +168,12 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
||||
return connData;
|
||||
}
|
||||
|
||||
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'").arg(m_serverConfigPath);
|
||||
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'")
|
||||
.arg(m_serverConfigPath);
|
||||
|
||||
errorCode = m_serverController->runScript(
|
||||
credentials, m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
|
||||
credentials,
|
||||
m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
|
||||
|
||||
return connData;
|
||||
}
|
||||
@@ -173,8 +182,8 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode)
|
||||
{
|
||||
QString scriptData = amnezia::scriptData(m_configTemplate, container);
|
||||
QString config =
|
||||
m_serverController->replaceVars(scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||
QString config = m_serverController->replaceVars(
|
||||
scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||
|
||||
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
@@ -208,16 +217,16 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
|
||||
return QJsonDocument(jConfig).toJson();
|
||||
}
|
||||
|
||||
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString)
|
||||
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns,
|
||||
const bool isApiConfig, QString &protocolConfigString)
|
||||
{
|
||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||
|
||||
return protocolConfigString;
|
||||
}
|
||||
|
||||
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString)
|
||||
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns,
|
||||
const bool isApiConfig, QString &protocolConfigString)
|
||||
{
|
||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef WIREGUARD_CONFIGURATOR_H
|
||||
#define WIREGUARD_CONFIGURATOR_H
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QObject>
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
@@ -12,8 +13,8 @@ class WireguardConfigurator : public ConfiguratorBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, bool isAwg,
|
||||
QObject *parent = nullptr);
|
||||
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
||||
bool isAwg, QObject *parent = nullptr);
|
||||
|
||||
struct ConnectionData
|
||||
{
|
||||
@@ -26,15 +27,18 @@ public:
|
||||
QString port;
|
||||
};
|
||||
|
||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
||||
ErrorCode &errorCode);
|
||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
||||
|
||||
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
|
||||
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
|
||||
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString);
|
||||
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||
QString &protocolConfigString);
|
||||
|
||||
static ConnectionData genClientKeys();
|
||||
|
||||
private:
|
||||
QList<QHostAddress> getIpsFromConf(const QString &input);
|
||||
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
||||
|
||||
|
||||
@@ -47,9 +47,6 @@ void CoreController::initModels()
|
||||
m_sitesModel.reset(new SitesModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
|
||||
|
||||
m_allowedDnsModel.reset(new AllowedDnsModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("AllowedDnsModel", m_allowedDnsModel.get());
|
||||
|
||||
m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
|
||||
|
||||
@@ -132,9 +129,6 @@ void CoreController::initControllers()
|
||||
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
|
||||
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
||||
|
||||
m_allowedDnsController.reset(new AllowedDnsController(m_settings, m_allowedDnsModel));
|
||||
m_engine->rootContext()->setContextProperty("AllowedDnsController", m_allowedDnsController.get());
|
||||
|
||||
m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
|
||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
|
||||
|
||||
@@ -219,7 +213,6 @@ void CoreController::initSignalHandlers()
|
||||
initAutoConnectHandler();
|
||||
initAmneziaDnsToggledHandler();
|
||||
initPrepareConfigHandler();
|
||||
initStrictKillSwitchHandler();
|
||||
}
|
||||
|
||||
void CoreController::initNotificationHandler()
|
||||
@@ -346,12 +339,6 @@ void CoreController::initPrepareConfigHandler()
|
||||
});
|
||||
}
|
||||
|
||||
void CoreController::initStrictKillSwitchHandler()
|
||||
{
|
||||
connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged,
|
||||
m_vpnConnection.get(), &VpnConnection::onKillSwitchModeChanged);
|
||||
}
|
||||
|
||||
QSharedPointer<PageController> CoreController::pageController() const
|
||||
{
|
||||
return m_pageController;
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "ui/controllers/api/apiConfigsController.h"
|
||||
#include "ui/controllers/api/apiSettingsController.h"
|
||||
#include "ui/controllers/appSplitTunnelingController.h"
|
||||
#include "ui/controllers/allowedDnsController.h"
|
||||
#include "ui/controllers/connectionController.h"
|
||||
#include "ui/controllers/exportController.h"
|
||||
#include "ui/controllers/focusController.h"
|
||||
@@ -19,7 +18,6 @@
|
||||
#include "ui/controllers/sitesController.h"
|
||||
#include "ui/controllers/systemController.h"
|
||||
|
||||
#include "ui/models/allowed_dns_model.h"
|
||||
#include "ui/models/containers_model.h"
|
||||
#include "ui/models/languageModel.h"
|
||||
#include "ui/models/protocols/cloakConfigModel.h"
|
||||
@@ -82,7 +80,6 @@ private:
|
||||
void initAutoConnectHandler();
|
||||
void initAmneziaDnsToggledHandler();
|
||||
void initPrepareConfigHandler();
|
||||
void initStrictKillSwitchHandler();
|
||||
|
||||
QQmlApplicationEngine *m_engine {}; // TODO use parent child system here?
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
@@ -105,7 +102,6 @@ private:
|
||||
QScopedPointer<SitesController> m_sitesController;
|
||||
QScopedPointer<SystemController> m_systemController;
|
||||
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
|
||||
QScopedPointer<AllowedDnsController> m_allowedDnsController;
|
||||
|
||||
QScopedPointer<ApiSettingsController> m_apiSettingsController;
|
||||
QScopedPointer<ApiConfigsController> m_apiConfigsController;
|
||||
@@ -116,7 +112,6 @@ private:
|
||||
QSharedPointer<LanguageModel> m_languageModel;
|
||||
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
||||
QSharedPointer<SitesModel> m_sitesModel;
|
||||
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
|
||||
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
|
||||
QSharedPointer<ClientManagementModel> m_clientManagementModel;
|
||||
|
||||
|
||||
@@ -26,6 +26,10 @@ namespace
|
||||
constexpr char apiPayload[] = "api_payload";
|
||||
constexpr char keyPayload[] = "key_payload";
|
||||
}
|
||||
|
||||
constexpr QLatin1String errorResponsePattern1("No active configuration found for");
|
||||
constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for");
|
||||
constexpr QLatin1String errorResponsePattern3("Account not found.");
|
||||
}
|
||||
|
||||
GatewayController::GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent)
|
||||
@@ -194,16 +198,16 @@ QStringList GatewayController::getProxyUrls()
|
||||
QList<QSslError> sslErrors;
|
||||
QNetworkReply *reply;
|
||||
|
||||
QStringList proxyStorageUrl;
|
||||
QStringList proxyStorageUrls;
|
||||
if (m_isDevEnvironment) {
|
||||
proxyStorageUrl = QStringList { DEV_S3_ENDPOINT };
|
||||
proxyStorageUrls = QString(DEV_S3_ENDPOINT).split(", ");
|
||||
} else {
|
||||
proxyStorageUrl = QStringList { PROD_S3_ENDPOINT };
|
||||
proxyStorageUrls = QString(PROD_S3_ENDPOINT).split(", ");
|
||||
}
|
||||
|
||||
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
|
||||
|
||||
for (const auto &proxyStorageUrl : proxyStorageUrl) {
|
||||
for (const auto &proxyStorageUrl : proxyStorageUrls) {
|
||||
request.setUrl(proxyStorageUrl);
|
||||
reply = amnApp->networkManager()->get(request);
|
||||
|
||||
@@ -262,7 +266,16 @@ bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray
|
||||
} else if (responseBody.contains("html")) {
|
||||
qDebug() << "The response contains an html tag";
|
||||
return true;
|
||||
} else if (reply->error() == QNetworkReply::NetworkError::NoError && checkEncryption) {
|
||||
} else if (reply->error() == QNetworkReply::NetworkError::ContentNotFoundError) {
|
||||
if (responseBody.contains(errorResponsePattern1) || responseBody.contains(errorResponsePattern2)
|
||||
|| responseBody.contains(errorResponsePattern3)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else if (reply->error() != QNetworkReply::NetworkError::NoError) {
|
||||
return true;
|
||||
} else if (checkEncryption) {
|
||||
try {
|
||||
QSimpleCrypto::QBlockCipher blockCipher;
|
||||
static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
|
||||
|
||||
@@ -757,10 +757,6 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
||||
|
||||
ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, DockerContainer container)
|
||||
{
|
||||
if (credentials.userName == "root") {
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
stdOut += data + "\n";
|
||||
@@ -774,8 +770,16 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
|
||||
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo);
|
||||
ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
||||
|
||||
if (!stdOut.contains("sudo"))
|
||||
if (credentials.userName != "root" && stdOut.contains("sudo:") && !stdOut.contains("uname:") && stdOut.contains("not found"))
|
||||
return ErrorCode::SudoPackageIsNotPreinstalled;
|
||||
if (credentials.userName != "root" && !stdOut.contains("sudo") && !stdOut.contains("wheel"))
|
||||
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"))
|
||||
return ErrorCode::ServerUserNotAllowedInSudoers;
|
||||
if (stdOut.contains("password is required"))
|
||||
return ErrorCode::ServerUserPasswordRequired;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,10 @@ namespace amnezia
|
||||
ServerCancelInstallation = 204,
|
||||
ServerUserNotInSudo = 205,
|
||||
ServerPacketManagerError = 206,
|
||||
SudoPackageIsNotPreinstalled = 207,
|
||||
ServerUserDirectoryNotAccessible = 208,
|
||||
ServerUserNotAllowedInSudoers = 209,
|
||||
ServerUserPasswordRequired = 210,
|
||||
|
||||
// Ssh connection errors
|
||||
SshRequestDeniedError = 300,
|
||||
|
||||
@@ -20,8 +20,12 @@ QString errorString(ErrorCode code) {
|
||||
case(ErrorCode::ServerContainerMissingError): errorMessage = QObject::tr("Server error: Docker container missing"); break;
|
||||
case(ErrorCode::ServerDockerFailedError): errorMessage = QObject::tr("Server error: Docker failed"); break;
|
||||
case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break;
|
||||
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user does not have permission to use sudo"); break;
|
||||
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break;
|
||||
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user is not a member of the sudo group"); break;
|
||||
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Package manager error"); break;
|
||||
case(ErrorCode::SudoPackageIsNotPreinstalled): errorMessage = QObject::tr("The sudo package is not pre-installed"); break;
|
||||
case(ErrorCode::ServerUserDirectoryNotAccessible): errorMessage = QObject::tr("The server user's home directory is not accessible"); break;
|
||||
case(ErrorCode::ServerUserNotAllowedInSudoers): errorMessage = QObject::tr("Action not allowed in sudoers"); break;
|
||||
case(ErrorCode::ServerUserPasswordRequired): errorMessage = QObject::tr("The user's password is required"); break;
|
||||
|
||||
// Libssh errors
|
||||
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
|
||||
|
||||
@@ -371,9 +371,6 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||
if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) {
|
||||
return false;
|
||||
}
|
||||
if (!parseStringList(obj, "allowedDnsServers", config.m_allowedDnsServers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
config.m_killSwitchEnabled = QVariant(obj.value("killSwitchOption").toString()).toBool();
|
||||
|
||||
|
||||
@@ -48,13 +48,6 @@ QJsonObject InterfaceConfig::toJson() const {
|
||||
}
|
||||
json.insert("excludedAddresses", jsExcludedAddresses);
|
||||
|
||||
|
||||
QJsonArray jsAllowedDnsServers;
|
||||
for (const QString& i : m_allowedDnsServers) {
|
||||
jsAllowedDnsServers.append(QJsonValue(i));
|
||||
}
|
||||
json.insert("allowedDnsServers", jsAllowedDnsServers);
|
||||
|
||||
QJsonArray disabledApps;
|
||||
for (const QString& i : m_vpnDisabledApps) {
|
||||
disabledApps.append(QJsonValue(i));
|
||||
|
||||
@@ -37,7 +37,6 @@ class InterfaceConfig {
|
||||
QList<IPAddress> m_allowedIPAddressRanges;
|
||||
QStringList m_excludedAddresses;
|
||||
QStringList m_vpnDisabledApps;
|
||||
QStringList m_allowedDnsServers;
|
||||
bool m_killSwitchEnabled;
|
||||
#if defined(MZ_ANDROID) || defined(MZ_IOS)
|
||||
QString m_installationId;
|
||||
|
||||
@@ -123,7 +123,6 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
|
||||
int appSplitTunnelType = rawConfig.value(amnezia::config_key::appSplitTunnelType).toInt();
|
||||
QJsonArray splitTunnelApps = rawConfig.value(amnezia::config_key::splitTunnelApps).toArray();
|
||||
QJsonArray allowedDns = rawConfig.value(amnezia::config_key::allowedDnsServers).toArray();
|
||||
|
||||
QJsonObject wgConfig = rawConfig.value(protocolName + "_config_data").toObject();
|
||||
|
||||
@@ -227,8 +226,6 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
|
||||
json.insert("vpnDisabledApps", splitTunnelApps);
|
||||
|
||||
json.insert("allowedDnsServers", allowedDns);
|
||||
|
||||
json.insert(amnezia::config_key::killSwitchOption, rawConfig.value(amnezia::config_key::killSwitchOption));
|
||||
|
||||
if (protocolName == amnezia::config_key::awg) {
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include "killswitch.h"
|
||||
|
||||
constexpr const int WG_TUN_PROC_TIMEOUT = 5000;
|
||||
constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg";
|
||||
|
||||
@@ -184,7 +182,7 @@ bool WireguardUtilsLinux::deleteInterface() {
|
||||
QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name"));
|
||||
|
||||
// double-check + ensure our firewall is installed and enabled
|
||||
KillSwitch::instance()->disableKillSwitch();
|
||||
LinuxFirewall::uninstall();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include "killswitch.h"
|
||||
|
||||
constexpr const int WG_TUN_PROC_TIMEOUT = 5000;
|
||||
constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg";
|
||||
|
||||
@@ -182,7 +180,7 @@ bool WireguardUtilsMacos::deleteInterface() {
|
||||
QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name"));
|
||||
|
||||
// double-check + ensure our firewall is installed and enabled
|
||||
KillSwitch::instance()->disableKillSwitch();
|
||||
MacOSFirewall::uninstall();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -29,8 +29,6 @@
|
||||
#include "logger.h"
|
||||
#include "platforms/windows/windowsutils.h"
|
||||
|
||||
#include "killswitch.h"
|
||||
|
||||
#define IPV6_ADDRESS_SIZE 16
|
||||
|
||||
// ID for the Firewall Sublayer
|
||||
@@ -182,24 +180,11 @@ bool WindowsFirewall::enableInterface(int vpnAdapterIndex) {
|
||||
} \
|
||||
}
|
||||
|
||||
logger.info() << "Enabling Killswitch Using Adapter:" << vpnAdapterIndex;
|
||||
if (vpnAdapterIndex < 0)
|
||||
{
|
||||
IPAddress allv4("0.0.0.0/0");
|
||||
if (!blockTrafficTo(allv4, MED_WEIGHT,
|
||||
"Block Internet", "killswitch")) {
|
||||
return false;
|
||||
}
|
||||
IPAddress allv6("::/0");
|
||||
if (!blockTrafficTo(allv6, MED_WEIGHT,
|
||||
"Block Internet", "killswitch")) {
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
logger.info() << "Enabling firewall Using Adapter:" << vpnAdapterIndex;
|
||||
FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT,
|
||||
"Allow usage of VPN Adapter"));
|
||||
"Allow usage of VPN Adapter"));
|
||||
FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic"));
|
||||
FW_OK(allowHyperVTraffic(MAX_WEIGHT, "Allow Hyper-V Traffic"));
|
||||
FW_OK(allowHyperVTraffic(MED_WEIGHT, "Allow Hyper-V Traffic"));
|
||||
FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT,
|
||||
"Allow all for AmneziaVPN.exe"));
|
||||
FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS"));
|
||||
@@ -277,14 +262,6 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||
}
|
||||
}
|
||||
|
||||
for (const QString& dns : config.m_allowedDnsServers) {
|
||||
logger.debug() << "Allow DNS: " << dns;
|
||||
if (!allowTrafficTo(QHostAddress(dns), 53, HIGH_WEIGHT,
|
||||
"Allow DNS-Server", config.m_serverPublicKey)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!config.m_excludedAddresses.empty()) {
|
||||
for (const QString& i : config.m_excludedAddresses) {
|
||||
logger.debug() << "excludedAddresses range: " << i;
|
||||
@@ -336,41 +313,37 @@ bool WindowsFirewall::disablePeerTraffic(const QString& pubkey) {
|
||||
}
|
||||
|
||||
bool WindowsFirewall::disableKillSwitch() {
|
||||
return KillSwitch::instance()->disableKillSwitch();
|
||||
}
|
||||
|
||||
bool WindowsFirewall::allowAllTraffic() {
|
||||
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||
auto cleanup = qScopeGuard([&] {
|
||||
if (result != ERROR_SUCCESS) {
|
||||
FwpmTransactionAbort0(m_sessionHandle);
|
||||
}
|
||||
});
|
||||
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||
auto cleanup = qScopeGuard([&] {
|
||||
if (result != ERROR_SUCCESS) {
|
||||
logger.error() << "FwpmTransactionBegin0 failed. Return value:.\n"
|
||||
<< result;
|
||||
return false;
|
||||
FwpmTransactionAbort0(m_sessionHandle);
|
||||
}
|
||||
});
|
||||
if (result != ERROR_SUCCESS) {
|
||||
logger.error() << "FwpmTransactionBegin0 failed. Return value:.\n"
|
||||
<< result;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& filterID : m_peerRules.values()) {
|
||||
FwpmFilterDeleteById0(m_sessionHandle, filterID);
|
||||
}
|
||||
for (const auto& filterID : m_peerRules.values()) {
|
||||
FwpmFilterDeleteById0(m_sessionHandle, filterID);
|
||||
}
|
||||
|
||||
for (const auto& filterID : qAsConst(m_activeRules)) {
|
||||
FwpmFilterDeleteById0(m_sessionHandle, filterID);
|
||||
}
|
||||
for (const auto& filterID : qAsConst(m_activeRules)) {
|
||||
FwpmFilterDeleteById0(m_sessionHandle, filterID);
|
||||
}
|
||||
|
||||
// Commit!
|
||||
result = FwpmTransactionCommit0(m_sessionHandle);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n"
|
||||
<< result;
|
||||
return false;
|
||||
}
|
||||
m_peerRules.clear();
|
||||
m_activeRules.clear();
|
||||
logger.debug() << "Firewall Disabled!";
|
||||
return true;
|
||||
// Commit!
|
||||
result = FwpmTransactionCommit0(m_sessionHandle);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n"
|
||||
<< result;
|
||||
return false;
|
||||
}
|
||||
m_peerRules.clear();
|
||||
m_activeRules.clear();
|
||||
logger.debug() << "Firewall Disabled!";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath,
|
||||
|
||||
@@ -43,7 +43,6 @@ class WindowsFirewall final : public QObject {
|
||||
bool enablePeerTraffic(const InterfaceConfig& config);
|
||||
bool disablePeerTraffic(const QString& pubkey);
|
||||
bool disableKillSwitch();
|
||||
bool allowAllTraffic();
|
||||
|
||||
private:
|
||||
static bool initSublayer();
|
||||
|
||||
@@ -171,11 +171,6 @@ ErrorCode OpenVpnProtocol::start()
|
||||
return lastError();
|
||||
}
|
||||
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
|
||||
IpcClient::Interface()->allowTrafficTo(QStringList(NetworkUtilities::getIPAddress(
|
||||
m_configData.value(amnezia::config_key::hostName).toString())));
|
||||
#endif
|
||||
|
||||
// Detect default gateway
|
||||
#ifdef Q_OS_MAC
|
||||
QProcess p;
|
||||
|
||||
@@ -95,8 +95,6 @@ namespace amnezia
|
||||
constexpr char splitTunnelApps[] = "splitTunnelApps";
|
||||
constexpr char appSplitTunnelType[] = "appSplitTunnelType";
|
||||
|
||||
constexpr char allowedDnsServers[] = "allowedDnsServers";
|
||||
|
||||
constexpr char killSwitchOption[] = "killSwitchOption";
|
||||
|
||||
constexpr char crc[] = "crc";
|
||||
|
||||
@@ -129,7 +129,6 @@
|
||||
<file>ui/qml/Components/SettingsContainersListView.qml</file>
|
||||
<file>ui/qml/Components/ShareConnectionDrawer.qml</file>
|
||||
<file>ui/qml/Components/TransportProtoSelector.qml</file>
|
||||
<file>ui/qml/Components/AddSitePanel.qml</file>
|
||||
<file>ui/qml/Config/GlobalConfig.qml</file>
|
||||
<file>ui/qml/Config/qmldir</file>
|
||||
<file>ui/qml/Controls2/BackButtonType.qml</file>
|
||||
@@ -144,9 +143,7 @@
|
||||
<file>ui/qml/Controls2/DropDownType.qml</file>
|
||||
<file>ui/qml/Controls2/FlickableType.qml</file>
|
||||
<file>ui/qml/Controls2/Header2Type.qml</file>
|
||||
<file>ui/qml/Controls2/BaseHeaderType.qml</file>
|
||||
<file>ui/qml/Controls2/HeaderTypeWithButton.qml</file>
|
||||
<file>ui/qml/Controls2/HeaderTypeWithSwitcher.qml</file>
|
||||
<file>ui/qml/Controls2/HeaderType.qml</file>
|
||||
<file>ui/qml/Controls2/HorizontalRadioButton.qml</file>
|
||||
<file>ui/qml/Controls2/ImageButtonType.qml</file>
|
||||
<file>ui/qml/Controls2/LabelWithButtonType.qml</file>
|
||||
@@ -202,8 +199,6 @@
|
||||
<file>ui/qml/Pages2/PageSettingsBackup.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsConnection.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsDns.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsKillSwitch.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsLogging.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsServerData.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsServerInfo.qml</file>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "secure_qsettings.h"
|
||||
|
||||
#include "../client/3rd/QSimpleCrypto/src/include/QAead.h"
|
||||
#include "../client/3rd/QSimpleCrypto/src/include/QBlockCipher.h"
|
||||
#include "QAead.h"
|
||||
#include "QBlockCipher.h"
|
||||
#include "utilities.h"
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <QObject>
|
||||
#include <QSettings>
|
||||
|
||||
#include "../client/3rd/qtkeychain/qtkeychain/keychain.h"
|
||||
#include "keychain.h"
|
||||
|
||||
class SecureQSettings : public QObject
|
||||
{
|
||||
|
||||
@@ -1,2 +1,12 @@
|
||||
CUR_USER=$(whoami);\
|
||||
groups $CUR_USER
|
||||
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 pacman > /dev/null 2>&1; then pm=$(which pacman); opt="--version";\
|
||||
else pm="uname"; opt="-a";\
|
||||
fi;\
|
||||
CUR_USER=$(whoami 2>/dev/null || echo $HOME | sed 's/.*\///');\
|
||||
echo $LANG | grep -qE '^(en_US.UTF-8|C.UTF-8|C)$' || export LC_ALL=C;\
|
||||
sudo -K;\
|
||||
if [ "$CUR_USER" = "root" ] || ( groups "$CUR_USER" | grep -E '\<(sudo|wheel)\>' ); then \
|
||||
sudo -nu $CUR_USER $pm $opt > /dev/null; sudo -n $pm $opt > /dev/null;\
|
||||
fi
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
CUR_USER=$(whoami);\
|
||||
CUR_USER=$(whoami 2>/dev/null || echo ~ | sed 's/.*\///');\
|
||||
sudo mkdir -p $DOCKERFILE_FOLDER;\
|
||||
sudo chown $CUR_USER $DOCKERFILE_FOLDER;\
|
||||
if ! sudo docker network ls | grep -q amnezia-dns-net; then sudo docker network create \
|
||||
|
||||
@@ -443,16 +443,6 @@ void Settings::setKillSwitchEnabled(bool enabled)
|
||||
setValue("Conf/killSwitchEnabled", enabled);
|
||||
}
|
||||
|
||||
bool Settings::isStrictKillSwitchEnabled() const
|
||||
{
|
||||
return value("Conf/strictKillSwitchEnabled", false).toBool();
|
||||
}
|
||||
|
||||
void Settings::setStrictKillSwitchEnabled(bool enabled)
|
||||
{
|
||||
setValue("Conf/strictKillSwitchEnabled", enabled);
|
||||
}
|
||||
|
||||
QString Settings::getInstallationUuid(const bool needCreate)
|
||||
{
|
||||
auto uuid = value("Conf/installationUuid", "").toString();
|
||||
@@ -558,13 +548,3 @@ void Settings::disableHomeAdLabel()
|
||||
{
|
||||
setValue("Conf/homeAdLabelVisible", false);
|
||||
}
|
||||
|
||||
QStringList Settings::allowedDnsServers() const
|
||||
{
|
||||
return value("Conf/allowedDnsServers").toStringList();
|
||||
}
|
||||
|
||||
void Settings::setAllowedDnsServers(const QStringList &servers)
|
||||
{
|
||||
setValue("Conf/allowedDnsServers", servers);
|
||||
}
|
||||
|
||||
@@ -213,10 +213,6 @@ public:
|
||||
|
||||
bool isKillSwitchEnabled() const;
|
||||
void setKillSwitchEnabled(bool enabled);
|
||||
|
||||
bool isStrictKillSwitchEnabled() const;
|
||||
void setStrictKillSwitchEnabled(bool enabled);
|
||||
|
||||
QString getInstallationUuid(const bool needCreate);
|
||||
|
||||
void resetGatewayEndpoint();
|
||||
@@ -229,9 +225,6 @@ public:
|
||||
bool isHomeAdLabelVisible();
|
||||
void disableHomeAdLabel();
|
||||
|
||||
QStringList allowedDnsServers() const;
|
||||
void setAllowedDnsServers(const QStringList &servers);
|
||||
|
||||
signals:
|
||||
void saveLogsChanged(bool enabled);
|
||||
void screenshotsEnabledChanged(bool enabled);
|
||||
|
||||
@@ -3334,8 +3334,8 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="22"/>
|
||||
<source>The user does not have permission to use sudo</source>
|
||||
<translation>ليس لدي المستخدم الصلحيات لأستخدام sudo</translation>
|
||||
<source>The user is not a member of the sudo group</source>
|
||||
<translation>المستخدم ليس عضوًا في مجموعة sudo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="26"/>
|
||||
@@ -3399,7 +3399,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="23"/>
|
||||
<source>Server error: Packet manager error</source>
|
||||
<source>Server error: Package manager error</source>
|
||||
<translation>خطأ في الخادم: خطأ في مدير الحزم</translation>
|
||||
</message>
|
||||
<message>
|
||||
|
||||
@@ -3468,8 +3468,8 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="22"/>
|
||||
<source>The user does not have permission to use sudo</source>
|
||||
<translation>The user does not have permission to use sudo</translation>
|
||||
<source>The user is not a member of the sudo group</source>
|
||||
<translation>کاربر عضو گروه sudo نیست</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="26"/>
|
||||
@@ -3590,8 +3590,8 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="23"/>
|
||||
<source>Server error: Packet manager error</source>
|
||||
<translation>Server error: Packet manager error</translation>
|
||||
<source>Server error: Package manager error</source>
|
||||
<translation>خطای سرور: خطای مدیر بسته</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="34"/>
|
||||
|
||||
@@ -3434,13 +3434,13 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="22"/>
|
||||
<source>The user does not have permission to use sudo</source>
|
||||
<translation>उपयोगकर्ता के पास sudo का उपयोग करने की अनुमति नहीं है</translation>
|
||||
<source>The user is not a member of the sudo group</source>
|
||||
<translation>उपयोगकर्ता sudo समूह का सदस्य नहीं है</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="23"/>
|
||||
<source>Server error: Packet manager error</source>
|
||||
<translation>सर्वर त्रुटि: पैकेट प्रबंधक त्रुटि</translation>
|
||||
<source>Server error: Package manager error</source>
|
||||
<translation>सर्वर त्रुटि: पैकेज प्रबंधक त्रुटि</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="26"/>
|
||||
|
||||
@@ -3330,8 +3330,8 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="22"/>
|
||||
<source>The user does not have permission to use sudo</source>
|
||||
<translation>ဤအသုံးပြုသူသည် sudo ကိုအသုံးပြုရန်ခွင့်ပြုချက်မရှိပါ</translation>
|
||||
<source>The user is not a member of the sudo group</source>
|
||||
<translation>ဤအသုံးပြုသူသည် sudo အုပ်စု၏အဖွဲ့ဝင်မဟုတ်ပါ</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="26"/>
|
||||
@@ -3395,8 +3395,8 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="23"/>
|
||||
<source>Server error: Packet manager error</source>
|
||||
<translation>ဆာဗာ မှားယွင်းမှု: Packet Manager မှားယွင်းမှု</translation>
|
||||
<source>Server error: Package manager error</source>
|
||||
<translation>ဆာဗာ အမှား- Package manager အမှား</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="34"/>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<message>
|
||||
<location filename="../ui/qml/Components/AdLabel.qml" line="57"/>
|
||||
<source>Amnezia Premium - for access to any website</source>
|
||||
<translation>Amnezia Premium - для доступа к любым сайтам </translation>
|
||||
<translation>Amnezia Premium - для доступа к любым сайтам</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -15,37 +15,37 @@
|
||||
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="31"/>
|
||||
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="34"/>
|
||||
<source>Active</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Активна</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="34"/>
|
||||
<source>Inactive</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Не активна</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="47"/>
|
||||
<source>%1 out of %2</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>%1 из %2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="51"/>
|
||||
<source>Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to 200 Mbps</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Классический VPN для комфортной работы, загрузки больших файлов и просмотра видео. Доступ ко всем сайтам и онлайн-ресурсам. Скорость — до 200 Мбит/с</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="54"/>
|
||||
<source>Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and more. YouTube is not included in the free plan.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включен в бесплатный тариф.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="125"/>
|
||||
<source>amnezia_free_support_bot</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="127"/>
|
||||
<source>amnezia_premium_support_bot</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -53,17 +53,17 @@
|
||||
<message>
|
||||
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="203"/>
|
||||
<source>%1 installed successfully.</source>
|
||||
<translation type="unfinished">%1 успешно установлен.</translation>
|
||||
<translation>%1 успешно установлен.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="258"/>
|
||||
<source>API config reloaded</source>
|
||||
<translation type="unfinished">Конфигурация API перезагружена</translation>
|
||||
<translation>Конфигурация API перезагружена</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="262"/>
|
||||
<source>Successfully changed the country of connection to %1</source>
|
||||
<translation type="unfinished">Изменение страны подключения на %1</translation>
|
||||
<translation>Страна подключения изменена на %1</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -92,18 +92,18 @@
|
||||
<message>
|
||||
<location filename="../ui/models/api/apiServicesModel.cpp" line="68"/>
|
||||
<source>Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. Works for any sites with no restrictions. Speed up to %1 MBit/s. Unlimited traffic.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Amnezia Premium — VPN для комфортной работы, скачивания больших файлов и просмотра видео в высоком разрешении. Скорость до %1 Мбит/с. Безлимитный трафик.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/api/apiServicesModel.cpp" line="72"/>
|
||||
<location filename="../ui/models/api/apiServicesModel.cpp" line="85"/>
|
||||
<source>AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>AmneziaFree предоставляет бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включен в бесплатный тариф.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/api/apiServicesModel.cpp" line="82"/>
|
||||
<source>Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. Works for any sites with no restrictions.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Amnezia Premium — VPN для комфортной работы, скачивания больших файлов и просмотра видео в высоком разрешении. Работает для любых сайтов без ограничений.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/api/apiServicesModel.cpp" line="97"/>
|
||||
@@ -562,12 +562,12 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="97"/>
|
||||
<source>AmneziaWG settings</source>
|
||||
<translation type="unfinished">Настройки AmneziaWG</translation>
|
||||
<translation>Настройки AmneziaWG</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="105"/>
|
||||
<source>MTU</source>
|
||||
<translation type="unfinished">MTU</translation>
|
||||
<translation>MTU</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="181"/>
|
||||
@@ -577,7 +577,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="191"/>
|
||||
<source>Port</source>
|
||||
<translation type="unfinished">Порт</translation>
|
||||
<translation>Порт</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="278"/>
|
||||
@@ -592,7 +592,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="289"/>
|
||||
<source>Only the settings for this device will be changed</source>
|
||||
<translation>Будут изменены настройки только для этого устройства.</translation>
|
||||
<translation>Будут изменены настройки только для этого устройства</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="290"/>
|
||||
@@ -1036,12 +1036,12 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="91"/>
|
||||
<source>WG settings</source>
|
||||
<translation type="unfinished">Настройки WG</translation>
|
||||
<translation>Настройки WG</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="99"/>
|
||||
<source>MTU</source>
|
||||
<translation type="unfinished">MTU</translation>
|
||||
<translation>MTU</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="116"/>
|
||||
@@ -1051,12 +1051,12 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="126"/>
|
||||
<source>Port</source>
|
||||
<translation type="unfinished">Порт</translation>
|
||||
<translation>Порт</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="149"/>
|
||||
<source>Save</source>
|
||||
<translation type="unfinished">Сохранить</translation>
|
||||
<translation>Сохранить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="153"/>
|
||||
@@ -1508,7 +1508,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="46"/>
|
||||
<source>support@amnezia.org</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>support@amnezia.org</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Mail</source>
|
||||
@@ -1526,7 +1526,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="50"/>
|
||||
<source>mailto:support@amnezia.org</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="57"/>
|
||||
@@ -1536,7 +1536,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="58"/>
|
||||
<source>Discover the source code</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Посмотреть исходный код</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="61"/>
|
||||
@@ -1551,7 +1551,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="69"/>
|
||||
<source>Visit official website</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Посетить официальный сайт</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>https://amnezia.org</source>
|
||||
@@ -1578,12 +1578,12 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiAvailableCountries.qml" line="84"/>
|
||||
<source>Location for connection</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Страны для подключения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiAvailableCountries.qml" line="123"/>
|
||||
<source>Unable change server location while there is an active connection</source>
|
||||
<translation type="unfinished">Невозможно изменить локацию во время активного соединения</translation>
|
||||
<translation>Невозможно изменить локацию во время активного соединения</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -1591,57 +1591,57 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="45"/>
|
||||
<source>Active devices</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Активные устройства</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="46"/>
|
||||
<source>Manage currently connected devices</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Управление подключенными устройствами</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="55"/>
|
||||
<source>You can find the identifier on the Support tab or, for older versions of the app, by tapping '+' and then the three dots at the top of the page.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Вы можете найти support tag во вкладке «Поддержка» или, в более ранних версиях приложения, нажав «+» на нижней панели, а затем три точки вверху страницы.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="69"/>
|
||||
<source> (current device)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation> (текущее устройство)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="70"/>
|
||||
<source>Support tag: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Support tag: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="70"/>
|
||||
<source>Last updated: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Последнее обновление: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="75"/>
|
||||
<source>Cannot unlink device during active connection</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Невозможно отвязать устройство во время активного соединения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="79"/>
|
||||
<source>Are you sure you want to unlink this device?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Вы уверены, что хотите отвязать это устройство?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="80"/>
|
||||
<source>This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку "Подключиться".</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="81"/>
|
||||
<source>Continue</source>
|
||||
<translation type="unfinished">Продолжить</translation>
|
||||
<translation>Продолжить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="82"/>
|
||||
<source>Cancel</source>
|
||||
<translation type="unfinished">Отменить</translation>
|
||||
<translation>Отменить</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -1649,82 +1649,82 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="22"/>
|
||||
<source>Windows</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="23"/>
|
||||
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#windows</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="29"/>
|
||||
<source>macOS</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="30"/>
|
||||
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#macos</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="36"/>
|
||||
<source>Android</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="37"/>
|
||||
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#android</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="43"/>
|
||||
<source>AndroidTV</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="44"/>
|
||||
<source>https://docs.amnezia.org/ru/documentation/instructions/android_tv_connect/</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="50"/>
|
||||
<source>iOS</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="51"/>
|
||||
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#ios</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="57"/>
|
||||
<source>Linux</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="58"/>
|
||||
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#linux</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="64"/>
|
||||
<source>Routers</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="65"/>
|
||||
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#routers</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="101"/>
|
||||
<source>How to connect on another device</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Как подключить другие устройства</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="102"/>
|
||||
<source>Setup guides on the Amnezia website</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Инструкции по настройке</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -1739,82 +1739,82 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="23"/>
|
||||
<source>Save AmneziaVPN config</source>
|
||||
<translation type="unfinished">Сохранить конфигурацию AmneziaVPN</translation>
|
||||
<translation>Сохранить конфигурацию AmneziaVPN</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="48"/>
|
||||
<source>Configuration files</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Файл конфигурации</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="49"/>
|
||||
<source>For router setup or the AmneziaWG app</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Для настройки роутера или приложения AmneziaWG</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="61"/>
|
||||
<source>The configuration needs to be reissued</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Необходимо заново скачать конфигурацию и добавить ее в приложение</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="126"/>
|
||||
<source> configuration file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation> файл конфигурации</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="132"/>
|
||||
<source>Generate a new configuration file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Создать новый файл конфигурации</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="133"/>
|
||||
<source>The previously created one will stop working</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Ранее созданный файл перестанет работать</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="144"/>
|
||||
<source>Revoke the current configuration file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Отозвать текущий файл конфигурации</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="177"/>
|
||||
<source>Config file saved</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Файл конфигурации сохранен</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="191"/>
|
||||
<source>The config has been revoked</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Конфигурация была отозвана</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="198"/>
|
||||
<source>Generate a new %1 configuration file?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Создать новый %1 файл конфигурации?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="200"/>
|
||||
<source>Revoke the current %1 configuration file?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Отозвать текущий %1 файл конфигурации?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="203"/>
|
||||
<source>Your previous configuration file will no longer work, and it will not be possible to connect using it</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Ваш предыдущий файл конфигурации не будет работать, и вы больше не сможете использовать его для подключения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="204"/>
|
||||
<source>Download</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Скачать</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="204"/>
|
||||
<source>Continue</source>
|
||||
<translation type="unfinished">Продолжить</translation>
|
||||
<translation>Продолжить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="205"/>
|
||||
<source>Cancel</source>
|
||||
<translation type="unfinished">Отменить</translation>
|
||||
<translation>Отменить</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -1834,7 +1834,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="37"/>
|
||||
<source>Valid until</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Действует до</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Speed</source>
|
||||
@@ -1847,67 +1847,67 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="29"/>
|
||||
<source>Subscription status</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Статус подписки</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="45"/>
|
||||
<source>Active connections</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Активные соединения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="171"/>
|
||||
<source>Configurations have been updated for some countries. Download and install the updated configuration files</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Сетевые адреса одного или нескольких серверов были обновлены. Пожалуйста, удалите старые конфигурацию и загрузите новые файлы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/>
|
||||
<source>Subscription key</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Ключ для подключения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/>
|
||||
<source>Amnezia Premium subscription key</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Ключ подписки Amnezia Premium</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="194"/>
|
||||
<source>Save VPN key to file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Сохранить VPN-ключ в файле</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="195"/>
|
||||
<source>Copy VPN key</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Скопировать VPN ключ</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="216"/>
|
||||
<source>Configuration files</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Файл конфигурации</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="218"/>
|
||||
<source>Manage configuration files</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Управление файлами конфигурации</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="236"/>
|
||||
<source>Active devices</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Активные устройства</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="238"/>
|
||||
<source>Manage currently connected devices</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Управление подключенными устройствами</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="255"/>
|
||||
<source>Support</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Поддержка</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="270"/>
|
||||
<source>How to connect on another device</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Как подключить другие устройства</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="295"/>
|
||||
@@ -1941,22 +1941,22 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="332"/>
|
||||
<source>Unlink this device</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Отвязать это устройство</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="335"/>
|
||||
<source>Are you sure you want to unlink this device?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Вы уверены, что хотите отвязать это устройство?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="336"/>
|
||||
<source>This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку Подключиться.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="342"/>
|
||||
<source>Cannot unlink device during active connection</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Невозможно отвязать устройство во время активного соединения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="370"/>
|
||||
@@ -1979,57 +1979,57 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="22"/>
|
||||
<source>Telegram</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="30"/>
|
||||
<source>Email Support</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Email</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="31"/>
|
||||
<source>support@amnezia.org</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="38"/>
|
||||
<source>Email Billing & Orders</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>По вопросам оплаты</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="39"/>
|
||||
<source>help@vpnpay.io</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="46"/>
|
||||
<source>Website</source>
|
||||
<translation type="unfinished">Веб-сайт</translation>
|
||||
<translation>Сайт</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="47"/>
|
||||
<source>amnezia.org</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>amnezia.org</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="81"/>
|
||||
<source>Support</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Поддержка</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="82"/>
|
||||
<source>Our technical support specialists are available to assist you at any time</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Наши специалисты технической поддержки всегда готовы помочь вам.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="109"/>
|
||||
<source>Support tag</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="119"/>
|
||||
<source>Copied</source>
|
||||
<translation type="unfinished">Скопировано</translation>
|
||||
<translation>Скопировано</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -2730,27 +2730,27 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="98"/>
|
||||
<source> connection settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation> настройки подключения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="110"/>
|
||||
<source>Click the "connect" button to create a connection configuration</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Нажмите кнопку «Подключиться», чтобы создать конфигурацию</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="130"/>
|
||||
<source> server settings</source>
|
||||
<translation type="unfinished"> настройки сервера</translation>
|
||||
<translation> настройки сервера</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="172"/>
|
||||
<source>Clear profile</source>
|
||||
<translation type="unfinished">Очистить профиль</translation>
|
||||
<translation>Очистить профиль</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="176"/>
|
||||
<source>The connection configuration will be deleted for this device only</source>
|
||||
<translation type="unfinished">Конфигурация подключения будет удалена только для этого устройства</translation>
|
||||
<translation>Конфигурация подключения будет удалена только на этом устройстве</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="182"/>
|
||||
@@ -3201,17 +3201,17 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardEasy.qml" line="68"/>
|
||||
<source>Choose Installation Type</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Выберите тип установки</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardEasy.qml" line="142"/>
|
||||
<source>Manual</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Ручная</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardEasy.qml" line="143"/>
|
||||
<source>Choose a VPN protocol</source>
|
||||
<translation>Выберите VPN-протокол</translation>
|
||||
<translation>Выбрать VPN-протокол</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardEasy.qml" line="200"/>
|
||||
@@ -3711,7 +3711,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="134"/>
|
||||
<source>Access error!</source>
|
||||
<translation type="unfinished">Ошибка доступа!</translation>
|
||||
<translation>Ошибка доступа!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="140"/>
|
||||
@@ -4009,7 +4009,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="15"/>
|
||||
<source>The selected protocol is not supported on the current platform</source>
|
||||
<translation type="unfinished">Выбранный протокол не поддерживается на данном устройстве</translation>
|
||||
<translation>Выбранный протокол не поддерживается на данном устройстве</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="18"/>
|
||||
@@ -4038,13 +4038,13 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="23"/>
|
||||
<source>The user does not have permission to use sudo</source>
|
||||
<translation>У пользователя нет прав на использование sudo</translation>
|
||||
<source>The user is not a member of the sudo group</source>
|
||||
<translation>Пользователь не входит в группу sudo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="24"/>
|
||||
<source>Server error: Packet manager error</source>
|
||||
<translation>Ошибка сервера: ошибка менеджера пакетов</translation>
|
||||
<source>Server error: Package manager error</source>
|
||||
<translation>Ошибка сервера: Ошибка менеджера пакетов</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="27"/>
|
||||
@@ -4217,7 +4217,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
<location filename="../core/errorstrings.cpp" line="55"/>
|
||||
<source>VPN Protocols is not installed.
|
||||
Please install VPN container at first</source>
|
||||
<translation type="unfinished">VPN-протоколы не установлены.
|
||||
<translation>VPN-протоколы не установлены.
|
||||
Пожалуйста, установите протокол</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -4248,7 +4248,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="67"/>
|
||||
<source>Failed to decrypt response payload</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="68"/>
|
||||
@@ -4258,7 +4258,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="69"/>
|
||||
<source>The limit of allowed configurations per subscription has been exceeded</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Превышен лимит разрешенных конфигураций для одной подписки</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="73"/>
|
||||
@@ -4435,27 +4435,27 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="113"/>
|
||||
<source>Shadowsocks masks VPN traffic, making it resemble normal web traffic, but it may still be detected by certain analysis systems.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Shadowsocks маскирует VPN-трафик, делая его похожим на обычный веб-трафик, но он все равно может быть обнаружен некоторыми системами анализа.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="115"/>
|
||||
<source>OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. It is very resistant to detection, but offers low speed.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>OpenVPN over Cloak — OpenVPN с маскировкой под веб-трафик , а также с защитой от обнаружения и систем анализа трафика. Он очень устойчив к обнаружению, но имеет низкую скорость работы в сравнении с другими похожими протоколами.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="118"/>
|
||||
<source>WireGuard - popular VPN protocol with high performance, high speed and low power consumption.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>WireGuard — популярный VPN-протокол с высокой производительностью, высокой скоростью и низким энергопотреблением.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="121"/>
|
||||
<source>AmneziaWG is a special protocol from Amnezia based on WireGuard. It provides high connection speed and ensures stable operation even in the most challenging network conditions.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>AmneziaWG — специальный протокол от Amnezia, основанный на WireGuard. Он обеспечивает высокую скорость соединения и гарантирует стабильную работу даже в самых сложных условиях.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="124"/>
|
||||
<source>XRay with REALITY masks VPN traffic as web traffic and protects against active probing. It is highly resistant to detection and offers high speed.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>XRay с REALITY маскирует VPN-трафик под веб-трафик. Обладает высокой устойчивостью к обнаружению и обеспечивает высокую скорость соединения.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="143"/>
|
||||
@@ -4467,7 +4467,12 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
|
||||
* Flexible customisation to suit user needs to work with different operating systems and devices
|
||||
* Recognised by DPI systems and therefore susceptible to blocking
|
||||
* Can operate over both TCP and UDP network protocols.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>OpenVPN является одним из самых популярных и проверенных временем VPN-протоколов. Он использует собственный протокол безопасности, и криптографические протоколы SSL/TLS для шифрования и обмена ключами. Более того, поддержка множества методов аутентификации делает OpenVPN универсальным, адаптируемым и подходящим для широкого спектра устройств и операционных систем. Благодаря своему открытому коду, OpenVPN подвергается тщательной проверке со стороны мирового сообщества, что постоянно укрепляет его безопасность. Имея отличный баланс между производительностью, безопасностью и совместимостью OpenVPN остается лучшим выбором для людей и компаний, заботящихся о конфиденциальности, однако OpenVPN легко распознается современными системами анализа трафика.
|
||||
Доступен в AmneziaVPN на всех платформах
|
||||
Нормальное энергопотребление на мобильных устройствах
|
||||
Гибкая настройка полезная при работе с различными операционными системами и устройствами
|
||||
Распознается системами DPI и, следовательно, уязвим к блокировкам
|
||||
Может работать как по TCP, так и по UDP протоколу.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="168"/>
|
||||
@@ -4487,7 +4492,22 @@ Immediately after receiving the first data packet, Cloak authenticates the incom
|
||||
* Not recognised by detection systems
|
||||
* Works over TCP network protocol, 443 port.
|
||||
</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Это связка протокола OpenVPN и плагина Cloak, созданная специально для защиты от обнаружения.
|
||||
|
||||
OpenVPN обеспечивает безопасное VPN-соединение, шифруя весь интернет-трафик между клиентом и сервером.
|
||||
|
||||
Плагин Cloak защищает OpenVPN от обнаружения.
|
||||
|
||||
Cloak может изменять метаданные пакета, чтобы полностью замаскировать VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью метода Active Probing. Это делает его очень устойчивым к обнаружению.
|
||||
|
||||
Сразу после получения первого пакета данных Cloak аутентифицирует входящее соединение, если аутентификация не удалась, плагин маскирует сервер под настоящий веб-сайт, и ваш VPN становится невидимым для систем анализа. Имеет низкую скорость работы в сравнении с другими похожими протоколами.
|
||||
|
||||
* Доступно в AmneziaVPN на всех платформах.
|
||||
* Высокое энергопотребление на мобильных устройствах
|
||||
* Гибкие настройки
|
||||
* Не распознается системами обнаружения.
|
||||
* Работает по сетевому протоколу TCP, порт 443.
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="185"/>
|
||||
@@ -4500,7 +4520,15 @@ WireGuard is very susceptible to detection and blocking due to its distinct pack
|
||||
* Minimum number of settings
|
||||
* Easily recognised by DPI analysis systems, susceptible to blocking
|
||||
* Works over UDP network protocol.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Популярный VPN-протокол с упрощенной архитектурой.
|
||||
WireGuard обеспечивает стабильное VPN-соединение и высокую производительность на всех устройствах. Он использует закодированные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность передачи данных.
|
||||
WireGuard очень чувствителен к обнаружению и блокировке из-за различных сигнатур пакетов. В отличие от некоторых других VPN протоколов, использующих методы запутывания, последовательные шаблоны сигнатур пакетов WireGuard легко идентифицируются системами анализа трафика.
|
||||
|
||||
* Доступно в AmneziaVPN на всех платформах.
|
||||
* Низкое энергопотребление
|
||||
* Минимальное количество настроек
|
||||
* Легко распознается системами анализа DPI, подвержен блокировке.
|
||||
* Работает по сетевому протоколу UDP.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="198"/>
|
||||
@@ -4513,7 +4541,15 @@ This means that AmneziaWG keeps the fast performance of the original while addin
|
||||
* Minimum number of settings
|
||||
* Not recognised by traffic analysis systems
|
||||
* Works over UDP network protocol.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>AmneziaWG — это современная версия популярного VPN протокола, основанная на базе WireGuard, сохранившая упрощенную архитектуру и высокопроизводительные возможности на всех устройствах.
|
||||
Хотя WireGuard известен своей эффективностью, обнаружить его довольно легко из-за различных сигнатур пакетов. AmneziaWG решает эту проблему, используя более совершенные методы работы, смешивая свой трафик с обычным интернет-трафиком.
|
||||
Это означает, что AmneziaWG сохраняет высокую производительность оригинала, добавляя при этом дополнительный уровень скрытности, что делает его отличным выбором для тех, кому нужно быстрое и незаметное VPN-соединение.
|
||||
|
||||
* Доступно в AmneziaVPN на всех платформах.
|
||||
* Низкое энергопотребление
|
||||
* Минимальное количество настроек
|
||||
* Не распознается системами анализа трафика.
|
||||
* Работает по сетевому протоколу UDP.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="214"/>
|
||||
@@ -4521,7 +4557,10 @@ This means that AmneziaWG keeps the fast performance of the original while addin
|
||||
It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, thus presenting an authentic TLS certificate and data.
|
||||
This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations.
|
||||
Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security. This makes REALITY a robust solution for maintaining internet freedom.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Протокол REALITY, современная разработка от создателей XRay. Призван обеспечить высочайший уровень защиты от обнаружения благодаря инновационному подходу к безопасности и конфиденциальности.
|
||||
Он безошибочно идентифицирует злоумышленников на этапе установления связи TLS, беспрепятственно работая в качестве прокси-сервера для оригинального клиента и перенаправляя злоумышленников на подлинные веб-сайты, предоставляя тем самым подлинный сертификат TLS и данные.
|
||||
Эта расширенная возможность отличает REALITY от аналогичных технологий тем, что способна маскироваться под случайный веб-трафик без использования специальных настроек.
|
||||
В отличие от старых протоколов, таких как VMess, VLESS и транспорт XTLS-Vision, REALITY имеет инновационную технологию распознавания «свой-чужой».Это делает REALITY надежным решением для обеспечения доступа к свободному интернету.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship.</source>
|
||||
@@ -4575,12 +4614,12 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
|
||||
* Configurable encryption protocol
|
||||
* Detectable by some DPI systems
|
||||
* Works over TCP network protocol.</source>
|
||||
<translation>Shadowsocks создан на основе протокола SOCKS5, защищает соединение с помощью шифра AEAD. Несмотря на то, что протокол Shadowsocks разработан таким образом, чтобы быть незаметным и сложным для идентификации, он не идентичен стандартному HTTPS-соединению. Поэтому некоторые системы анализа трафика всё же могут обнаружить соединение Shadowsocks. В связи с ограниченной поддержкой в Amnezia рекомендуется использовать протокол AmneziaWG.
|
||||
<translation>Shadowsocks создан на основе протокола SOCKS5, защищает соединение с помощью шифра AEAD. Несмотря на то, что протокол Shadowsocks разработан таким образом, чтобы быть незаметным и сложным для идентификации, он не идентичен стандартному HTTPS-соединению, поэтому некоторые системы анализа трафика всё же могут обнаружить соединение Shadowsocks. В связи с ограниченной поддержкой в Amnezia рекомендуется использовать протокол AmneziaWG.
|
||||
|
||||
* Доступен в AmneziaVPN только для ПК и ноутбуков
|
||||
* Настраиваемый протокол шифрования
|
||||
* Распознается некоторыми системами DPI-анализа
|
||||
* Работает по сетевому протоколу TCP</translation>
|
||||
* Работает по сетевому протоколу TCP.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The REALITY protocol, a pioneering development by the creators of XRay, is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion.
|
||||
@@ -4911,12 +4950,12 @@ This means that AmneziaWG keeps the fast performance of the original while addin
|
||||
<message>
|
||||
<location filename="../ui/qml/Components/RenameServerDrawer.qml" line="30"/>
|
||||
<source>Server name</source>
|
||||
<translation type="unfinished">Имя сервера</translation>
|
||||
<translation>Имя сервера</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Components/RenameServerDrawer.qml" line="41"/>
|
||||
<source>Save</source>
|
||||
<translation type="unfinished">Сохранить</translation>
|
||||
<translation>Сохранить</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -4932,7 +4971,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin
|
||||
<message>
|
||||
<location filename="../ui/qml/Components/ServersListView.qml" line="86"/>
|
||||
<source>Unable change server while there is an active connection</source>
|
||||
<translation type="unfinished">Невозможно изменить сервер во время активного соединения</translation>
|
||||
<translation>Невозможно изменить сервер во время активного соединения</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -5186,12 +5225,12 @@ This means that AmneziaWG keeps the fast performance of the original while addin
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="338"/>
|
||||
<source>Automatic</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Автоматическая</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="346"/>
|
||||
<source>AmneziaWG protocol will be installed. It provides high connection speed and ensures stable operation even in the most challenging network conditions.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Будет установлен протокол AmneziaWG. Он обеспечивает высокую скорость соединения и гарантирует стабильную работу даже в самых сложных условиях.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -3700,13 +3700,13 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="22"/>
|
||||
<source>The user does not have permission to use sudo</source>
|
||||
<translation>The user does not have permission to use sudo</translation>
|
||||
<source>The user is not a member of the sudo group</source>
|
||||
<translation>Користувач не входить до групи sudo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="23"/>
|
||||
<source>Server error: Packet manager error</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<source>Server error: Package manager error</source>
|
||||
<translation>Помилка сервера: Помилка менеджера пакетів</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="26"/>
|
||||
|
||||
@@ -3433,8 +3433,8 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="22"/>
|
||||
<source>The user does not have permission to use sudo</source>
|
||||
<translation>صارف کو sudo استعمال کرنے کی اجازت نہیں ہے</translation>
|
||||
<source>The user is not a member of the sudo group</source>
|
||||
<translation>صارف sudo گروپ کا رکن نہیں ہے</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="26"/>
|
||||
@@ -3498,7 +3498,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="23"/>
|
||||
<source>Server error: Packet manager error</source>
|
||||
<source>Server error: Package manager error</source>
|
||||
<translation>سرور خطا: پیکیج منیجر خطا</translation>
|
||||
</message>
|
||||
<message>
|
||||
|
||||
@@ -3675,13 +3675,13 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="22"/>
|
||||
<source>The user does not have permission to use sudo</source>
|
||||
<translation>用户没有root权限</translation>
|
||||
<source>The user is not a member of the sudo group</source>
|
||||
<translation>用户不是 sudo 组的成员</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="23"/>
|
||||
<source>Server error: Packet manager error</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<source>Server error: Package manager error</source>
|
||||
<translation>服务器错误:包管理器错误</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="26"/>
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
#include "allowedDnsController.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QStandardPaths>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "systemController.h"
|
||||
#include "core/networkUtilities.h"
|
||||
#include "core/defs.h"
|
||||
|
||||
AllowedDnsController::AllowedDnsController(const std::shared_ptr<Settings> &settings,
|
||||
const QSharedPointer<AllowedDnsModel> &allowedDnsModel,
|
||||
QObject *parent)
|
||||
: QObject(parent), m_settings(settings), m_allowedDnsModel(allowedDnsModel)
|
||||
{
|
||||
}
|
||||
|
||||
void AllowedDnsController::addDns(QString ip)
|
||||
{
|
||||
if (ip.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NetworkUtilities::ipAddressRegExp().match(ip).hasMatch()) {
|
||||
emit errorOccurred(tr("The address does not look like a valid IP address"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_allowedDnsModel->addDns(ip)) {
|
||||
emit finished(tr("New DNS server added: %1").arg(ip));
|
||||
} else {
|
||||
emit errorOccurred(tr("DNS server already exists: %1").arg(ip));
|
||||
}
|
||||
}
|
||||
|
||||
void AllowedDnsController::removeDns(int index)
|
||||
{
|
||||
auto modelIndex = m_allowedDnsModel->index(index);
|
||||
auto ip = m_allowedDnsModel->data(modelIndex, AllowedDnsModel::Roles::IpRole).toString();
|
||||
m_allowedDnsModel->removeDns(modelIndex);
|
||||
|
||||
emit finished(tr("DNS server removed: %1").arg(ip));
|
||||
}
|
||||
|
||||
void AllowedDnsController::importDns(const QString &fileName, bool replaceExisting)
|
||||
{
|
||||
QByteArray jsonData;
|
||||
if (!SystemController::readFile(fileName, jsonData)) {
|
||||
emit errorOccurred(tr("Can't open file: %1").arg(fileName));
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData);
|
||||
if (jsonDocument.isNull()) {
|
||||
emit errorOccurred(tr("Failed to parse JSON data from file: %1").arg(fileName));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!jsonDocument.isArray()) {
|
||||
emit errorOccurred(tr("The JSON data is not an array in file: %1").arg(fileName));
|
||||
return;
|
||||
}
|
||||
|
||||
auto jsonArray = jsonDocument.array();
|
||||
QStringList dnsServers;
|
||||
|
||||
for (auto jsonValue : jsonArray) {
|
||||
auto ip = jsonValue.toString();
|
||||
|
||||
if (!NetworkUtilities::ipAddressRegExp().match(ip).hasMatch()) {
|
||||
qDebug() << ip << " is not a valid IP address";
|
||||
continue;
|
||||
}
|
||||
|
||||
dnsServers.append(ip);
|
||||
}
|
||||
|
||||
m_allowedDnsModel->addDnsList(dnsServers, replaceExisting);
|
||||
|
||||
emit finished(tr("Import completed"));
|
||||
}
|
||||
|
||||
void AllowedDnsController::exportDns(const QString &fileName)
|
||||
{
|
||||
auto dnsServers = m_allowedDnsModel->getCurrentDnsServers();
|
||||
|
||||
QJsonArray jsonArray;
|
||||
|
||||
for (const auto &ip : dnsServers) {
|
||||
jsonArray.append(ip);
|
||||
}
|
||||
|
||||
QJsonDocument jsonDocument(jsonArray);
|
||||
QByteArray jsonData = jsonDocument.toJson();
|
||||
|
||||
SystemController::saveFile(fileName, jsonData);
|
||||
|
||||
emit finished(tr("Export completed"));
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#ifndef ALLOWEDDNSCONTROLLER_H
|
||||
#define ALLOWEDDNSCONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "settings.h"
|
||||
#include "ui/models/allowed_dns_model.h"
|
||||
|
||||
class AllowedDnsController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AllowedDnsController(const std::shared_ptr<Settings> &settings,
|
||||
const QSharedPointer<AllowedDnsModel> &allowedDnsModel,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void addDns(QString ip);
|
||||
void removeDns(int index);
|
||||
|
||||
void importDns(const QString &fileName, bool replaceExisting);
|
||||
void exportDns(const QString &fileName);
|
||||
|
||||
signals:
|
||||
void errorOccurred(const QString &errorMessage);
|
||||
void finished(const QString &message);
|
||||
|
||||
void saveFile(const QString &fileName, const QString &data);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
|
||||
};
|
||||
|
||||
#endif // ALLOWEDDNSCONTROLLER_H
|
||||
@@ -31,15 +31,13 @@ namespace PageLoader
|
||||
PageSettingsLogging,
|
||||
PageSettingsSplitTunneling,
|
||||
PageSettingsAppSplitTunneling,
|
||||
PageSettingsKillSwitch,
|
||||
PageSettingsApiServerInfo,
|
||||
PageSettingsApiAvailableCountries,
|
||||
PageSettingsApiSupport,
|
||||
PageSettingsApiInstructions,
|
||||
PageSettingsApiNativeConfigs,
|
||||
PageSettingsApiDevices,
|
||||
PageSettingsKillSwitchExceptions,
|
||||
|
||||
|
||||
PageServiceSftpSettings,
|
||||
PageServiceTorWebsiteSettings,
|
||||
PageServiceDnsSettings,
|
||||
|
||||
@@ -245,24 +245,6 @@ bool SettingsController::isKillSwitchEnabled()
|
||||
void SettingsController::toggleKillSwitch(bool enable)
|
||||
{
|
||||
m_settings->setKillSwitchEnabled(enable);
|
||||
emit killSwitchEnabledChanged();
|
||||
if (enable == false) {
|
||||
emit strictKillSwitchEnabledChanged(false);
|
||||
} else {
|
||||
emit strictKillSwitchEnabledChanged(isStrictKillSwitchEnabled());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool SettingsController::isStrictKillSwitchEnabled()
|
||||
{
|
||||
return m_settings->isStrictKillSwitchEnabled();
|
||||
}
|
||||
|
||||
void SettingsController::toggleStrictKillSwitch(bool enable)
|
||||
{
|
||||
m_settings->setStrictKillSwitchEnabled(enable);
|
||||
emit strictKillSwitchEnabledChanged(enable);
|
||||
}
|
||||
|
||||
bool SettingsController::isNotificationPermissionGranted()
|
||||
|
||||
@@ -24,8 +24,6 @@ public:
|
||||
Q_PROPERTY(QString secondaryDns READ getSecondaryDns WRITE setSecondaryDns NOTIFY secondaryDnsChanged)
|
||||
Q_PROPERTY(bool isLoggingEnabled READ isLoggingEnabled WRITE toggleLogging NOTIFY loggingStateChanged)
|
||||
Q_PROPERTY(bool isNotificationPermissionGranted READ isNotificationPermissionGranted NOTIFY onNotificationStateChanged)
|
||||
Q_PROPERTY(bool isKillSwitchEnabled READ isKillSwitchEnabled WRITE toggleKillSwitch NOTIFY killSwitchEnabledChanged)
|
||||
Q_PROPERTY(bool strictKillSwitchEnabled READ isStrictKillSwitchEnabled WRITE toggleStrictKillSwitch NOTIFY strictKillSwitchEnabledChanged)
|
||||
|
||||
Q_PROPERTY(bool isDevModeEnabled READ isDevModeEnabled NOTIFY devModeEnabled)
|
||||
Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged)
|
||||
@@ -77,9 +75,6 @@ public slots:
|
||||
bool isKillSwitchEnabled();
|
||||
void toggleKillSwitch(bool enable);
|
||||
|
||||
bool isStrictKillSwitchEnabled();
|
||||
void toggleStrictKillSwitch(bool enable);
|
||||
|
||||
bool isNotificationPermissionGranted();
|
||||
void requestNotificationPermission();
|
||||
|
||||
@@ -103,8 +98,6 @@ signals:
|
||||
void primaryDnsChanged();
|
||||
void secondaryDnsChanged();
|
||||
void loggingStateChanged();
|
||||
void killSwitchEnabledChanged();
|
||||
void strictKillSwitchEnabledChanged(bool enabled);
|
||||
|
||||
void restoreBackupFinished();
|
||||
void changeSettingsFinished(const QString &finishedMessage);
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
#include "allowed_dns_model.h"
|
||||
|
||||
AllowedDnsModel::AllowedDnsModel(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: QAbstractListModel(parent), m_settings(settings)
|
||||
{
|
||||
fillDnsServers();
|
||||
}
|
||||
|
||||
int AllowedDnsModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_dnsServers.size();
|
||||
}
|
||||
|
||||
QVariant AllowedDnsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(rowCount()))
|
||||
return QVariant();
|
||||
|
||||
switch (role) {
|
||||
case IpRole:
|
||||
return m_dnsServers.at(index.row());
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
bool AllowedDnsModel::addDns(const QString &ip)
|
||||
{
|
||||
if (m_dnsServers.contains(ip)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||
m_dnsServers.append(ip);
|
||||
m_settings->setAllowedDnsServers(m_dnsServers);
|
||||
endInsertRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
void AllowedDnsModel::addDnsList(const QStringList &dnsServers, bool replaceExisting)
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
if (replaceExisting) {
|
||||
m_dnsServers.clear();
|
||||
}
|
||||
|
||||
for (const QString &ip : dnsServers) {
|
||||
if (!m_dnsServers.contains(ip)) {
|
||||
m_dnsServers.append(ip);
|
||||
}
|
||||
}
|
||||
|
||||
m_settings->setAllowedDnsServers(m_dnsServers);
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void AllowedDnsModel::removeDns(QModelIndex index)
|
||||
{
|
||||
if (!index.isValid() || index.row() >= m_dnsServers.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
beginRemoveRows(QModelIndex(), index.row(), index.row());
|
||||
m_dnsServers.removeAt(index.row());
|
||||
m_settings->setAllowedDnsServers(m_dnsServers);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
QStringList AllowedDnsModel::getCurrentDnsServers()
|
||||
{
|
||||
return m_dnsServers;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> AllowedDnsModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[IpRole] = "ip";
|
||||
return roles;
|
||||
}
|
||||
|
||||
void AllowedDnsModel::fillDnsServers()
|
||||
{
|
||||
m_dnsServers = m_settings->allowedDnsServers();
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#ifndef ALLOWEDDNSMODEL_H
|
||||
#define ALLOWEDDNSMODEL_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include "settings.h"
|
||||
|
||||
class AllowedDnsModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
IpRole = Qt::UserRole + 1
|
||||
};
|
||||
|
||||
explicit AllowedDnsModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
public slots:
|
||||
bool addDns(const QString &ip);
|
||||
void addDnsList(const QStringList &dnsServers, bool replaceExisting);
|
||||
void removeDns(QModelIndex index);
|
||||
QStringList getCurrentDnsServers();
|
||||
|
||||
protected:
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
private:
|
||||
void fillDnsServers();
|
||||
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
QStringList m_dnsServers;
|
||||
};
|
||||
|
||||
#endif // ALLOWEDDNSMODEL_H
|
||||
@@ -1,73 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property bool enabled: true
|
||||
property string placeholderText: ""
|
||||
property alias textField: searchField.textField
|
||||
|
||||
signal addClicked(string text)
|
||||
signal moreClicked()
|
||||
|
||||
implicitWidth: 360
|
||||
implicitHeight: 96
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
anchors.fill: parent
|
||||
color: "#0E0F12"
|
||||
opacity: 0.85
|
||||
z: -1
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: addSiteButton
|
||||
|
||||
enabled: root.enabled
|
||||
spacing: 2
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: 16
|
||||
leftMargin: 16
|
||||
rightMargin: 16
|
||||
bottomMargin: 24
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: searchField
|
||||
|
||||
Layout.fillWidth: true
|
||||
rightButtonClickedOnEnter: true
|
||||
|
||||
textField.placeholderText: root.placeholderText
|
||||
buttonImageSource: "qrc:/images/controls/plus.svg"
|
||||
|
||||
clickedFunc: function() {
|
||||
root.addClicked(textField.text)
|
||||
textField.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: addSiteButtonImage
|
||||
implicitWidth: 56
|
||||
implicitHeight: 56
|
||||
|
||||
image: "qrc:/images/controls/more-vertical.svg"
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
onClicked: root.moreClicked()
|
||||
|
||||
Keys.onReturnPressed: addSiteButtonImage.clicked()
|
||||
Keys.onEnterPressed: addSiteButtonImage.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string headerText
|
||||
property int headerTextMaximumLineCount: 2
|
||||
property int headerTextElide: Qt.ElideRight
|
||||
property string descriptionText
|
||||
property alias headerRow: headerRow
|
||||
|
||||
implicitWidth: content.implicitWidth
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
anchors.fill: parent
|
||||
|
||||
RowLayout {
|
||||
id: headerRow
|
||||
|
||||
Header1TextType {
|
||||
id: header
|
||||
Layout.fillWidth: true
|
||||
text: root.headerText
|
||||
maximumLineCount: root.headerTextMaximumLineCount
|
||||
elide: root.headerTextElide
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
id: description
|
||||
Layout.topMargin: 16
|
||||
Layout.fillWidth: true
|
||||
text: root.descriptionText
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
visible: root.descriptionText !== ""
|
||||
}
|
||||
}
|
||||
}
|
||||
86
client/ui/qml/Controls2/HeaderType.qml
Normal file
86
client/ui/qml/Controls2/HeaderType.qml
Normal file
@@ -0,0 +1,86 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string actionButtonImage
|
||||
property var actionButtonFunction
|
||||
|
||||
property alias actionButton: headerActionButton
|
||||
|
||||
property string headerText
|
||||
property int headerTextMaximumLineCount: 2
|
||||
property int headerTextElide: Qt.ElideRight
|
||||
|
||||
property string descriptionText
|
||||
|
||||
implicitWidth: content.implicitWidth
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
anchors.fill: parent
|
||||
|
||||
RowLayout {
|
||||
Header1TextType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: root.headerText
|
||||
maximumLineCount: root.headerTextMaximumLineCount
|
||||
elide: root.headerTextElide
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: headerActionButton
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
image: root.actionButtonImage
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
visible: image ? true : false
|
||||
|
||||
onClicked: {
|
||||
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
||||
actionButtonFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
id: description
|
||||
|
||||
Layout.topMargin: 16
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: root.descriptionText
|
||||
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
|
||||
visible: root.descriptionText !== ""
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
||||
actionButtonFunction()
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
||||
actionButtonFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
BaseHeaderType {
|
||||
id: root
|
||||
|
||||
property string actionButtonImage
|
||||
property var actionButtonFunction
|
||||
property alias actionButton: headerActionButton
|
||||
|
||||
Component.onCompleted: {
|
||||
headerRow.children.push(headerActionButton)
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: headerActionButton
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
Layout.alignment: Qt.AlignRight
|
||||
image: root.actionButtonImage
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
visible: image ? true : false
|
||||
|
||||
onClicked: {
|
||||
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
||||
actionButtonFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
||||
actionButtonFunction()
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
||||
actionButtonFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
BaseHeaderType {
|
||||
id: root
|
||||
|
||||
property var switcherFunction
|
||||
property bool showSwitcher: false
|
||||
property alias switcher: headerSwitcher
|
||||
|
||||
Component.onCompleted: {
|
||||
headerRow.children.push(headerSwitcher)
|
||||
}
|
||||
|
||||
SwitcherType {
|
||||
id: headerSwitcher
|
||||
Layout.alignment: Qt.AlignRight
|
||||
visible: root.showSwitcher
|
||||
|
||||
onToggled: {
|
||||
if (switcherFunction && typeof switcherFunction === "function") {
|
||||
switcherFunction(checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,11 +20,7 @@ RadioButton {
|
||||
property string selectedColor: AmneziaStyle.color.transparent
|
||||
|
||||
property string textColor: AmneziaStyle.color.paleGray
|
||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
||||
property string selectedTextColor: AmneziaStyle.color.goldenApricot
|
||||
property string selectedTextDisabledColor: AmneziaStyle.color.burntOrange
|
||||
property string descriptionColor: AmneziaStyle.color.mutedGray
|
||||
property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
property int borderFocusedWidth: 1
|
||||
@@ -34,12 +30,6 @@ RadioButton {
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
|
||||
property string radioButtonInnerCirclePressedSource: "qrc:/images/controls/radio-button-inner-circle-pressed.png"
|
||||
property string radioButtonInnerCircleSource: "qrc:/images/controls/radio-button-inner-circle.png"
|
||||
property string radioButtonPressedSource: "qrc:/images/controls/radio-button-pressed.svg"
|
||||
property string radioButtonDefaultSource: "qrc:/images/controls/radio-button.svg"
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
@@ -104,15 +94,14 @@ RadioButton {
|
||||
if (showImage) {
|
||||
return imageSource
|
||||
} else if (root.pressed) {
|
||||
return root.radioButtonInnerCirclePressedSource
|
||||
return "qrc:/images/controls/radio-button-inner-circle-pressed.png"
|
||||
} else if (root.checked) {
|
||||
return root.radioButtonInnerCircleSource
|
||||
return "qrc:/images/controls/radio-button-inner-circle.png"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
opacity: root.enabled ? 1.0 : 0.3
|
||||
anchors.centerIn: parent
|
||||
|
||||
width: 24
|
||||
@@ -124,13 +113,12 @@ RadioButton {
|
||||
if (showImage) {
|
||||
return ""
|
||||
} else if (root.pressed || root.checked) {
|
||||
return root.radioButtonPressedSource
|
||||
return "qrc:/images/controls/radio-button-pressed.svg"
|
||||
} else {
|
||||
return root.radioButtonDefaultSource
|
||||
return "qrc:/images/controls/radio-button.svg"
|
||||
}
|
||||
}
|
||||
|
||||
opacity: root.enabled ? 1.0 : 0.3
|
||||
anchors.centerIn: parent
|
||||
|
||||
width: 24
|
||||
@@ -160,11 +148,10 @@ RadioButton {
|
||||
elide: root.textElide
|
||||
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
return root.checked ? selectedTextColor : textColor
|
||||
} else {
|
||||
return root.checked ? selectedTextDisabledColor : textDisabledColor
|
||||
if (root.checked) {
|
||||
return selectedTextColor
|
||||
}
|
||||
return textColor
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
@@ -177,7 +164,7 @@ RadioButton {
|
||||
CaptionTextType {
|
||||
id: description
|
||||
|
||||
color: root.enabled ? root.descriptionColor : root.descriptionDisabledColor
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
text: root.descriptionText
|
||||
|
||||
visible: root.descriptionText !== ""
|
||||
|
||||
@@ -56,7 +56,7 @@ PageType {
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ PageType {
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -91,7 +91,7 @@ PageType {
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("AmneziaWG settings")
|
||||
|
||||
@@ -91,7 +91,7 @@ PageType {
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("AmneziaWG settings")
|
||||
|
||||
@@ -76,7 +76,7 @@ PageType {
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("Cloak settings")
|
||||
|
||||
@@ -75,7 +75,7 @@ PageType {
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("OpenVPN settings")
|
||||
|
||||
@@ -32,7 +32,7 @@ PageType {
|
||||
id: backButton
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
@@ -78,7 +78,7 @@ PageType {
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("Shadowsocks settings")
|
||||
|
||||
@@ -85,7 +85,7 @@ PageType {
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("WG settings")
|
||||
|
||||
@@ -77,7 +77,7 @@ PageType {
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("WG settings")
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ PageType {
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("XRay settings")
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ PageType {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -85,7 +85,7 @@ PageType {
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
@@ -77,7 +77,7 @@ PageType {
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
@@ -217,7 +217,7 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("SOCKS5 settings")
|
||||
|
||||
@@ -54,7 +54,7 @@ PageType {
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
@@ -29,7 +29,7 @@ PageType {
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
id: header
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
|
||||
@@ -69,7 +69,7 @@ PageType {
|
||||
Layout.topMargin: 20
|
||||
}
|
||||
|
||||
HeaderTypeWithButton {
|
||||
HeaderType {
|
||||
id: headerContent
|
||||
objectName: "headerContent"
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ PageType {
|
||||
id: backButton
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -91,7 +91,7 @@ PageType {
|
||||
id: backButton
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -38,7 +38,7 @@ PageType {
|
||||
id: backButton
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -93,7 +93,7 @@ PageType {
|
||||
Layout.topMargin: 20
|
||||
}
|
||||
|
||||
HeaderTypeWithButton {
|
||||
HeaderType {
|
||||
id: headerContent
|
||||
objectName: "headerContent"
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ PageType {
|
||||
id: backButton
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -79,22 +79,29 @@ PageType {
|
||||
id: backButton
|
||||
}
|
||||
|
||||
HeaderTypeWithSwitcher {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
RowLayout {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
|
||||
headerText: qsTr("App split tunneling")
|
||||
headerText: qsTr("App split tunneling")
|
||||
|
||||
enabled: root.pageEnabled
|
||||
showSwitcher: true
|
||||
switcher {
|
||||
checked: AppSplitTunnelingModel.isTunnelingEnabled
|
||||
enabled: root.pageEnabled
|
||||
}
|
||||
switcherFunction: function(checked) {
|
||||
AppSplitTunnelingModel.toggleSplitTunneling(checked)
|
||||
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
|
||||
|
||||
SwitcherType {
|
||||
id: switcher
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: root.pageEnabled
|
||||
|
||||
checked: AppSplitTunnelingModel.isTunnelingEnabled
|
||||
onToggled: {
|
||||
AppSplitTunnelingModel.toggleSplitTunneling(checked)
|
||||
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ PageType {
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
@@ -60,7 +60,7 @@ PageType {
|
||||
|
||||
spacing: 16
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("Back up your configuration")
|
||||
|
||||
@@ -36,7 +36,7 @@ PageType {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
@@ -94,7 +94,9 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
DividerType {
|
||||
visible: root.isAppSplitTinnelingEnabled
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: splitTunnelingButton2
|
||||
@@ -117,20 +119,29 @@ PageType {
|
||||
visible: root.isAppSplitTinnelingEnabled
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: killSwitchButton
|
||||
SwitcherType {
|
||||
id: killSwitchSwitcher
|
||||
visible: !GC.isMobile()
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
text: qsTr("KillSwitch")
|
||||
descriptionText: qsTr("Blocks network connections without VPN")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
descriptionText: qsTr("Disables your internet if your encrypted VPN connection drops out for any reason.")
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsKillSwitch)
|
||||
checked: SettingsController.isKillSwitchEnabled()
|
||||
checkable: !ConnectionController.isConnected
|
||||
onCheckedChanged: {
|
||||
if (checked !== SettingsController.isKillSwitchEnabled()) {
|
||||
SettingsController.toggleKillSwitch(checked)
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
if (!checkable) {
|
||||
PageController.showNotificationMessage(qsTr("Cannot change KillSwitch settings during active connection"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ PageType {
|
||||
|
||||
spacing: 16
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("DNS servers")
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Config"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.height
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
HeaderTypeWithSwitcher {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Kill Switch")
|
||||
descriptionText: qsTr("Enable to ensure network traffic goes through a secure VPN tunnel, preventing accidental exposure of your IP and DNS queries if the connection drops")
|
||||
|
||||
showSwitcher: true
|
||||
switcher {
|
||||
checked: SettingsController.isKillSwitchEnabled
|
||||
enabled: !ConnectionController.isConnected
|
||||
}
|
||||
switcherFunction: function(checked) {
|
||||
if (!ConnectionController.isConnected) {
|
||||
SettingsController.isKillSwitchEnabled = checked
|
||||
} else {
|
||||
PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection"))
|
||||
switcher.checked = SettingsController.isKillSwitchEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalRadioButton {
|
||||
id: softKillSwitch
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected
|
||||
checked: !SettingsController.strictKillSwitchEnabled
|
||||
|
||||
text: qsTr("Soft Kill Switch")
|
||||
descriptionText: qsTr("Internet connection is blocked if VPN connection drops accidentally")
|
||||
|
||||
onClicked: {
|
||||
SettingsController.strictKillSwitchEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
VerticalRadioButton {
|
||||
id: strictKillSwitch
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected
|
||||
checked: SettingsController.strictKillSwitchEnabled
|
||||
|
||||
text: qsTr("Strict Kill Switch")
|
||||
descriptionText: qsTr("Internet connection is blocked even if VPN was turned off manually or not started")
|
||||
|
||||
onClicked: {
|
||||
SettingsController.strictKillSwitchEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.topMargin: 32
|
||||
Layout.fillWidth: true
|
||||
|
||||
enabled: true
|
||||
text: qsTr("DNS Exceptions")
|
||||
descriptionText: qsTr("DNS servers from the list will remain accessible when Kill Switch is triggered")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsKillSwitchExceptions)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,296 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import QtCore
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
import PageEnum 1.0
|
||||
import ProtocolEnum 1.0
|
||||
import ContainerProps 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
property bool pageEnabled: true
|
||||
|
||||
ColumnLayout {
|
||||
id: header
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
enabled: root.pageEnabled
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
|
||||
headerText: qsTr("DNS Exceptions")
|
||||
descriptionText: qsTr("DNS servers from the list will remain accessible when Kill Switch is triggered")
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
|
||||
anchors.top: header.bottom
|
||||
anchors.topMargin: 16
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
width: parent.width
|
||||
|
||||
enabled: root.pageEnabled
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
cacheBuffer: 200
|
||||
displayMarginBeginning: 40
|
||||
displayMarginEnd: 40
|
||||
|
||||
ScrollBar.vertical: ScrollBarType { }
|
||||
|
||||
footer: AddSitePanel {
|
||||
id: addSitePanel
|
||||
|
||||
width: listView.width
|
||||
z: 10
|
||||
|
||||
enabled: root.pageEnabled
|
||||
placeholderText: qsTr("IPv4 address")
|
||||
|
||||
onAddClicked: function(text) {
|
||||
PageController.showBusyIndicator(true)
|
||||
AllowedDnsController.addDns(text)
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
|
||||
onMoreClicked: {
|
||||
moreActionsDrawer.openTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
footerPositioning: ListView.OverlayFooter
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
id: dnsFilterModel
|
||||
sourceModel: AllowedDnsModel
|
||||
filters: [
|
||||
RegExpFilter {
|
||||
roleName: "ip"
|
||||
pattern: ".*" + addSitePanel.textField.text + ".*"
|
||||
caseSensitivity: Qt.CaseInsensitive
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
clip: true
|
||||
|
||||
reuseItems: true
|
||||
|
||||
delegate: ColumnLayout {
|
||||
id: delegateContent
|
||||
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
id: site
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: ip
|
||||
rightImageSource: "qrc:/images/controls/trash.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
var headerText = qsTr("Delete ") + ip + "?"
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
AllowedDnsController.removeDns(dnsFilterModel.mapToSource(index))
|
||||
if (!GC.isMobile()) {
|
||||
site.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
site.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
|
||||
DrawerType2 {
|
||||
id: moreActionsDrawer
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: parent.height * 0.4375
|
||||
|
||||
expandedStateContent: ColumnLayout {
|
||||
id: moreActionsDrawerContent
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
headerText: qsTr("Import / Export addresses")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: importSitesButton
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Import")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
importSitesDrawer.openTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: exportSitesButton
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Save address list")
|
||||
|
||||
clickedFunction: function() {
|
||||
var fileName = ""
|
||||
if (GC.isMobile()) {
|
||||
fileName = "amnezia_killswitch_exceptions.json"
|
||||
} else {
|
||||
fileName = SystemController.getFileName(qsTr("Save addresses"),
|
||||
qsTr("Address files (*.json)"),
|
||||
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_killswitch_exceptions",
|
||||
true,
|
||||
".json")
|
||||
}
|
||||
if (fileName !== "") {
|
||||
PageController.showBusyIndicator(true)
|
||||
AllowedDnsController.exportDns(fileName)
|
||||
moreActionsDrawer.closeTriggered()
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
|
||||
DrawerType2 {
|
||||
id: importSitesDrawer
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: parent.height * 0.4375
|
||||
|
||||
expandedStateContent: Item {
|
||||
implicitHeight: importSitesDrawer.expandedHeight
|
||||
|
||||
BackButtonType {
|
||||
id: importSitesDrawerBackButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 16
|
||||
|
||||
backButtonFunction: function() {
|
||||
importSitesDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
anchors.top: importSitesDrawerBackButton.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
contentHeight: importSitesDrawerContent.height
|
||||
|
||||
ColumnLayout {
|
||||
id: importSitesDrawerContent
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
headerText: qsTr("Import address list")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: importSitesButton2
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Replace address list")
|
||||
|
||||
clickedFunction: function() {
|
||||
var fileName = SystemController.getFileName(qsTr("Open address file"),
|
||||
qsTr("Address files (*.json)"))
|
||||
if (fileName !== "") {
|
||||
importSitesDrawerContent.importSites(fileName, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: importSitesButton3
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Add imported addresses to existing ones")
|
||||
|
||||
clickedFunction: function() {
|
||||
var fileName = SystemController.getFileName(qsTr("Open address file"),
|
||||
qsTr("Address files (*.json)"))
|
||||
if (fileName !== "") {
|
||||
importSitesDrawerContent.importSites(fileName, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function importSites(fileName, replaceExistingSites) {
|
||||
PageController.showBusyIndicator(true)
|
||||
AllowedDnsController.importDns(fileName, replaceExistingSites)
|
||||
PageController.showBusyIndicator(false)
|
||||
importSitesDrawer.closeTriggered()
|
||||
moreActionsDrawer.closeTriggered()
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ PageType {
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
@@ -71,7 +71,7 @@ PageType {
|
||||
objectName: "backButton"
|
||||
}
|
||||
|
||||
HeaderTypeWithButton {
|
||||
HeaderType {
|
||||
id: headerContent
|
||||
objectName: "headerContent"
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ PageType {
|
||||
id: backButton
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
@@ -31,7 +31,7 @@ PageType {
|
||||
id: backButton
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
@@ -94,22 +94,33 @@ PageType {
|
||||
id: backButton
|
||||
}
|
||||
|
||||
HeaderTypeWithSwitcher {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Split tunneling")
|
||||
|
||||
enabled: root.pageEnabled
|
||||
showSwitcher: true
|
||||
switcher {
|
||||
checked: SitesModel.isTunnelingEnabled
|
||||
RowLayout {
|
||||
HeaderType {
|
||||
enabled: root.pageEnabled
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
|
||||
headerText: qsTr("Split tunneling")
|
||||
}
|
||||
switcherFunction: function(checked) {
|
||||
SitesModel.toggleSplitTunneling(checked)
|
||||
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
|
||||
|
||||
SwitcherType {
|
||||
id: switcher
|
||||
|
||||
enabled: root.pageEnabled
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
|
||||
function onToggledFunc() {
|
||||
SitesModel.toggleSplitTunneling(this.checked)
|
||||
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
|
||||
}
|
||||
|
||||
checked: SitesModel.isTunnelingEnabled
|
||||
onToggled: { onToggledFunc() }
|
||||
Keys.onEnterPressed: { onToggledFunc() }
|
||||
Keys.onReturnPressed: { onToggledFunc() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ PageType {
|
||||
Layout.topMargin: 20
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.rightMargin: 16
|
||||
|
||||
@@ -28,7 +28,7 @@ PageType {
|
||||
Layout.topMargin: 20
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.rightMargin: 16
|
||||
|
||||
@@ -43,7 +43,7 @@ PageType {
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
HeaderTypeWithButton {
|
||||
HeaderType {
|
||||
id: moreButton
|
||||
|
||||
property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible()
|
||||
@@ -74,7 +74,7 @@ PageType {
|
||||
anchors.right: parent.right
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
|
||||
@@ -66,7 +66,7 @@ PageType {
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
@@ -59,7 +59,7 @@ PageType {
|
||||
|
||||
spacing: 16
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
id: header
|
||||
|
||||
implicitWidth: parent.width
|
||||
|
||||
@@ -118,7 +118,7 @@ PageType {
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ PageType {
|
||||
Layout.leftMargin: -16
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -58,7 +58,7 @@ PageType {
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -33,7 +33,7 @@ PageType {
|
||||
Layout.topMargin: 20
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
@@ -61,7 +61,7 @@ PageType {
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
headerText: qsTr("New connection")
|
||||
}
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ PageType {
|
||||
|
||||
spacing: 0
|
||||
|
||||
HeaderTypeWithButton {
|
||||
HeaderType {
|
||||
id: header
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
|
||||
@@ -44,7 +44,7 @@ PageType {
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
|
||||
|
||||
@@ -52,28 +52,6 @@ void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes)
|
||||
emit bytesChanged(receivedBytes, sentBytes);
|
||||
}
|
||||
|
||||
void VpnConnection::onKillSwitchModeChanged(bool enabled)
|
||||
{
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
if (!m_IpcClient) {
|
||||
m_IpcClient = new IpcClient(this);
|
||||
}
|
||||
|
||||
if (!m_IpcClient->isSocketConnected()) {
|
||||
if (!IpcClient::init(m_IpcClient)) {
|
||||
qWarning() << "Error occurred when init IPC client";
|
||||
emit serviceIsNotReady();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (IpcClient::Interface()) {
|
||||
qDebug() << "Set KillSwitch Strict mode enabled " << enabled;
|
||||
IpcClient::Interface()->refreshKillSwitch(enabled);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
||||
{
|
||||
|
||||
@@ -308,7 +286,6 @@ void VpnConnection::createProtocolConnections()
|
||||
void VpnConnection::appendKillSwitchConfig()
|
||||
{
|
||||
m_vpnConfiguration.insert(config_key::killSwitchOption, QVariant(m_settings->isKillSwitchEnabled()).toString());
|
||||
m_vpnConfiguration.insert(config_key::allowedDnsServers, QVariant(m_settings->allowedDnsServers()).toJsonValue());
|
||||
}
|
||||
|
||||
void VpnConnection::appendSplitTunnelingConfig()
|
||||
|
||||
@@ -48,14 +48,14 @@ public:
|
||||
|
||||
public slots:
|
||||
void connectToVpn(int serverIndex,
|
||||
const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration);
|
||||
const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration);
|
||||
|
||||
void disconnectFromVpn();
|
||||
|
||||
|
||||
void addRoutes(const QStringList &ips);
|
||||
void deleteRoutes(const QStringList &ips);
|
||||
void flushDns();
|
||||
void onKillSwitchModeChanged(bool enabled);
|
||||
|
||||
signals:
|
||||
void bytesChanged(quint64 receivedBytes, quint64 sentBytes);
|
||||
|
||||
@@ -29,9 +29,6 @@ class IpcInterface
|
||||
SLOT( void StopRoutingIpv6() );
|
||||
|
||||
SLOT( bool disableKillSwitch() );
|
||||
SLOT( bool disableAllTraffic() );
|
||||
SLOT( bool refreshKillSwitch( bool enabled ) );
|
||||
SLOT( bool allowTrafficTo( const QStringList ranges ) );
|
||||
SLOT( bool enablePeerTraffic( const QJsonObject &configStr) );
|
||||
SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) );
|
||||
SLOT( bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) );
|
||||
|
||||
@@ -8,12 +8,21 @@
|
||||
#include "logger.h"
|
||||
#include "router.h"
|
||||
|
||||
#include "killswitch.h"
|
||||
|
||||
#include "../core/networkUtilities.h"
|
||||
#include "../client/protocols/protocols_defs.h"
|
||||
#ifdef Q_OS_WIN
|
||||
#include "../client/platforms/windows/daemon/windowsdaemon.h"
|
||||
#include "../client/platforms/windows/daemon/windowsfirewall.h"
|
||||
#include "tapcontroller_win.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#include "../client/platforms/linux/daemon/linuxfirewall.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
#include "../client/platforms/macos/daemon/macosfirewall.h"
|
||||
#endif
|
||||
|
||||
IpcServer::IpcServer(QObject *parent) : IpcInterfaceSource(parent)
|
||||
|
||||
@@ -179,32 +188,174 @@ void IpcServer::setLogsEnabled(bool enabled)
|
||||
}
|
||||
}
|
||||
|
||||
bool IpcServer::allowTrafficTo(QStringList ranges)
|
||||
{
|
||||
return KillSwitch::instance()->allowTrafficTo(ranges);
|
||||
}
|
||||
|
||||
bool IpcServer::disableAllTraffic()
|
||||
{
|
||||
return KillSwitch::instance()->disableAllTraffic();
|
||||
}
|
||||
|
||||
bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex)
|
||||
{
|
||||
return KillSwitch::instance()->enableKillSwitch(configStr, vpnAdapterIndex);
|
||||
#ifdef Q_OS_WIN
|
||||
auto firewallManager = WindowsFirewall::create(this);
|
||||
Q_ASSERT(firewallManager != nullptr);
|
||||
return firewallManager->enableInterface(vpnAdapterIndex);
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
|
||||
int splitTunnelType = configStr.value("splitTunnelType").toInt();
|
||||
QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray();
|
||||
bool blockAll = 0;
|
||||
bool allowNets = 0;
|
||||
bool blockNets = 0;
|
||||
QStringList allownets;
|
||||
QStringList blocknets;
|
||||
|
||||
if (splitTunnelType == 0) {
|
||||
blockAll = true;
|
||||
allowNets = true;
|
||||
allownets.append(configStr.value("vpnServer").toString());
|
||||
} else if (splitTunnelType == 1) {
|
||||
blockNets = true;
|
||||
for (auto v : splitTunnelSites) {
|
||||
blocknets.append(v.toString());
|
||||
}
|
||||
} else if (splitTunnelType == 2) {
|
||||
blockAll = true;
|
||||
allowNets = true;
|
||||
allownets.append(configStr.value("vpnServer").toString());
|
||||
for (auto v : splitTunnelSites) {
|
||||
allownets.append(v.toString());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
// double-check + ensure our firewall is installed and enabled
|
||||
if (!LinuxFirewall::isInstalled())
|
||||
LinuxFirewall::install();
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), blockAll);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), allowNets);
|
||||
LinuxFirewall::updateAllowNets(allownets);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), blockAll);
|
||||
LinuxFirewall::updateBlockNets(blocknets);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), true);
|
||||
QStringList dnsServers;
|
||||
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
|
||||
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
|
||||
dnsServers.append("127.0.0.1");
|
||||
dnsServers.append("127.0.0.53");
|
||||
LinuxFirewall::updateDNSServers(dnsServers);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true);
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
|
||||
// double-check + ensure our firewall is installed and enabled. This is necessary as
|
||||
// other software may disable pfctl before re-enabling with their own rules (e.g other VPNs)
|
||||
if (!MacOSFirewall::isInstalled())
|
||||
MacOSFirewall::install();
|
||||
|
||||
MacOSFirewall::ensureRootAnchorPriority();
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), blockAll);
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), allowNets);
|
||||
MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), allowNets, QStringLiteral("allownets"), allownets);
|
||||
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), blockNets);
|
||||
MacOSFirewall::setAnchorTable(QStringLiteral("120.blockNets"), blockNets, QStringLiteral("blocknets"), blocknets);
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), true);
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true);
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), true);
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), true);
|
||||
|
||||
QStringList dnsServers;
|
||||
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
|
||||
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
|
||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true);
|
||||
MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IpcServer::disableKillSwitch()
|
||||
{
|
||||
return KillSwitch::instance()->disableKillSwitch();
|
||||
#ifdef Q_OS_WIN
|
||||
auto firewallManager = WindowsFirewall::create(this);
|
||||
Q_ASSERT(firewallManager != nullptr);
|
||||
return firewallManager->disableKillSwitch();
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
LinuxFirewall::uninstall();
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
MacOSFirewall::uninstall();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IpcServer::enablePeerTraffic(const QJsonObject &configStr)
|
||||
{
|
||||
return KillSwitch::instance()->enablePeerTraffic(configStr);
|
||||
}
|
||||
#ifdef Q_OS_WIN
|
||||
InterfaceConfig config;
|
||||
config.m_dnsServer = configStr.value(amnezia::config_key::dns1).toString();
|
||||
config.m_serverPublicKey = "openvpn";
|
||||
config.m_serverIpv4Gateway = configStr.value("vpnGateway").toString();
|
||||
config.m_serverIpv4AddrIn = configStr.value("vpnServer").toString();
|
||||
int vpnAdapterIndex = configStr.value("vpnAdapterIndex").toInt();
|
||||
int inetAdapterIndex = configStr.value("inetAdapterIndex").toInt();
|
||||
|
||||
bool IpcServer::refreshKillSwitch(bool enabled)
|
||||
{
|
||||
return KillSwitch::instance()->refresh(enabled);
|
||||
int splitTunnelType = configStr.value("splitTunnelType").toInt();
|
||||
QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray();
|
||||
|
||||
QStringList AllowedIPAddesses;
|
||||
|
||||
// Use APP split tunnel
|
||||
if (splitTunnelType == 0 || splitTunnelType == 2) {
|
||||
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("0.0.0.0"), 0));
|
||||
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("::"), 0));
|
||||
}
|
||||
|
||||
if (splitTunnelType == 1) {
|
||||
for (auto v : splitTunnelSites) {
|
||||
QString ipRange = v.toString();
|
||||
if (ipRange.split('/').size() > 1) {
|
||||
config.m_allowedIPAddressRanges.append(
|
||||
IPAddress(QHostAddress(ipRange.split('/')[0]), atoi(ipRange.split('/')[1].toLocal8Bit())));
|
||||
} else {
|
||||
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress(ipRange), 32));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config.m_excludedAddresses.append(configStr.value("vpnServer").toString());
|
||||
if (splitTunnelType == 2) {
|
||||
for (auto v : splitTunnelSites) {
|
||||
QString ipRange = v.toString();
|
||||
config.m_excludedAddresses.append(ipRange);
|
||||
}
|
||||
}
|
||||
|
||||
for (const QJsonValue &i : configStr.value(amnezia::config_key::splitTunnelApps).toArray()) {
|
||||
if (!i.isString()) {
|
||||
break;
|
||||
}
|
||||
config.m_vpnDisabledApps.append(i.toString());
|
||||
}
|
||||
|
||||
// killSwitch toggle
|
||||
if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) {
|
||||
auto firewallManager = WindowsFirewall::create(this);
|
||||
Q_ASSERT(firewallManager != nullptr);
|
||||
firewallManager->enablePeerTraffic(config);
|
||||
}
|
||||
|
||||
WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex);
|
||||
WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -34,12 +34,9 @@ public:
|
||||
virtual bool deleteTun(const QString &dev) override;
|
||||
virtual void StartRoutingIpv6() override;
|
||||
virtual void StopRoutingIpv6() override;
|
||||
virtual bool disableAllTraffic() override;
|
||||
virtual bool allowTrafficTo(QStringList ranges) override;
|
||||
virtual bool enablePeerTraffic(const QJsonObject &configStr) override;
|
||||
virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override;
|
||||
virtual bool disableKillSwitch() override;
|
||||
virtual bool refreshKillSwitch( bool enabled ) override;
|
||||
virtual bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) override;
|
||||
|
||||
private:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user