Compare commits

..

1 Commits

Author SHA1 Message Date
shiroow a2450f916c Update PageSettingsApiDevices.qml
title case Active Devices
2025-03-04 20:33:44 +03:00
37 changed files with 351 additions and 434 deletions
+3 -3
View File
@@ -10,7 +10,7 @@ env:
jobs: jobs:
Build-Linux-Ubuntu: Build-Linux-Ubuntu:
runs-on: ubuntu-24.04 runs-on: ubuntu-20.04
env: env:
QT_VERSION: 6.6.2 QT_VERSION: 6.6.2
@@ -47,7 +47,7 @@ jobs:
- name: 'Build project' - name: 'Build project'
run: | run: |
sudo apt-get install libxkbcommon-x11-0 libsecret-1-dev sudo apt-get install libxkbcommon-x11-0
export QT_BIN_DIR=${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64/bin export QT_BIN_DIR=${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64/bin
export QIF_BIN_DIR=${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin export QIF_BIN_DIR=${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin
bash deploy/build_linux.sh bash deploy/build_linux.sh
@@ -190,7 +190,7 @@ jobs:
- name: 'Install go' - name: 'Install go'
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: '1.24' go-version: '1.22.1'
cache: false cache: false
- name: 'Setup gomobile' - name: 'Setup gomobile'
+2 -2
View File
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN) set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.8.5.0 project(${PROJECT} VERSION 4.8.4.3
DESCRIPTION "AmneziaVPN" DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/" HOMEPAGE_URL "https://amnezia.org/"
) )
@@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}") set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 2082) set(APP_ANDROID_VERSION_CODE 2080)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux") set(MZ_PLATFORM_NAME "linux")
+10 -10
View File
@@ -6,11 +6,11 @@
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client) [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский ### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
[AmneziaVPN](https://amnezia.org) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере. [AmneziaVPN](https://amnezia.org) — это open sourse VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org) [![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](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] > [!TIP]
> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org). > Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org).
@@ -30,7 +30,7 @@
- Классические VPN-протоколы: OpenVPN, WireGuard и IKEv2. - Классические 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. - Протоколы с маскировкой трафика (обфускацией): OpenVPN с плагином [Cloak](https://github.com/cbeuw/Cloak), Shadowsocks (OpenVPN over Shadowsocks), [AmneziaWG](https://docs.amnezia.org/documentation/amnezia-wg/) and XRay.
- Поддержка Split Tunneling — добавляйте любые сайты или приложения в список, чтобы включить VPN только для них. - Поддержка 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). - Поддержка конфигурации протокола 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://amnezia.org](https://amnezia.org) - Веб-сайт проекта | [Альтернативная ссылка (зеркало)](https://storage.googleapis.com/kldscp/amnezia.org)
- [https://docs.amnezia.org](https://docs.amnezia.org) - Документация - [https://docs.amnezia.org](https://docs.amnezia.org) - Документация
- [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit - [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_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_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_mm](https://t.me/amnezia_vpn_mm) - Канал поддржки в Telegram (Мьянма)
- [https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Канал поддержки в 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\) - [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 для скриптов сборки. Проверьте папку deploy для скриптов сборки.
### Как собрать iOS-приложение из исходного кода на MacOS ### Как собрать iOS-приложение из исходного кода на MacOS
1. Убедитесь, что у вас установлен Xcode версии 14 или выше. 1. Убедитесь, что у вас установлен XCode версии 14 или выше.
2. Для генерации проекта Xcode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули: 2. Для генерации проекта XCode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули:
- MacOS - MacOS
- iOS - iOS
- Модуль совместимости с Qt 5 - Модуль совместимости с 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 export PATH=$(PATH):/path/to/GOPATH/bin
``` ```
6. Откройте проект в Xcode. Теперь вы можете тестировать, архивировать или публиковать приложение. 6. Откройте проект в XCode. Теперь вы можете тестировать, архивировать или публиковать приложение.
Если сборка завершится с ошибкой: Если сборка завершится с ошибкой:
``` ```
+8
View File
@@ -31,6 +31,10 @@ add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}") add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_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)) if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
set(PACKAGES ${PACKAGES} Widgets) set(PACKAGES ${PACKAGES} Widgets)
endif() endif()
@@ -44,6 +48,10 @@ set(LIBS ${LIBS}
Qt6::Core5Compat Qt6::Concurrent Qt6::Core5Compat Qt6::Concurrent
) )
if(IOS)
set(LIBS ${LIBS} Qt6::Multimedia)
endif()
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
set(LIBS ${LIBS} Qt6::Widgets) set(LIBS ${LIBS} Qt6::Widgets)
endif() endif()
+65 -74
View File
@@ -3,7 +3,6 @@
#include <QDebug> #include <QDebug>
#include <QJsonDocument> #include <QJsonDocument>
#include <QProcess> #include <QProcess>
#include <QRegularExpression>
#include <QString> #include <QString>
#include <QTemporaryDir> #include <QTemporaryDir>
#include <QTemporaryFile> #include <QTemporaryFile>
@@ -20,17 +19,13 @@
#include "settings.h" #include "settings.h"
#include "utilities.h" #include "utilities.h"
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
const QSharedPointer<ServerController> &serverController, bool isAwg, bool isAwg, QObject *parent)
QObject *parent)
: ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg) : ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg)
{ {
m_serverConfigPath = m_serverConfigPath = m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath; m_serverPublicKeyPath = m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath;
m_serverPublicKeyPath = m_serverPskKeyPath = m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
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_configTemplate = m_isAwg ? ProtocolScriptType::awg_template : ProtocolScriptType::wireguard_template;
m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard; m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard;
@@ -68,31 +63,9 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
return connData; 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, WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
DockerContainer container, DockerContainer container,
const QJsonObject &containerConfig, const QJsonObject &containerConfig, ErrorCode &errorCode)
ErrorCode &errorCode)
{ {
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys(); WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
connData.host = credentials.hostName; connData.host = credentials.hostName;
@@ -103,45 +76,65 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
return connData; return connData;
} }
QString getIpsScript = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath); // Get list of already created clients (only IP addresses)
QString stdOut; QString nextIpNumber;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) { {
stdOut += data + "\n"; QString script = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
return ErrorCode::NoError; QString stdOut;
}; auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
errorCode = m_serverController->runContainerScript(credentials, container, getIpsScript, cbReadStdOut); errorCode = m_serverController->runContainerScript(credentials, container, script, cbReadStdOut);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return connData; return connData;
} }
auto ips = getIpsFromConf(stdOut);
QHostAddress nextIp = [&] { stdOut.replace("AllowedIPs = ", "");
QHostAddress result; stdOut.replace("/32", "");
QHostAddress lastIp; QStringList ips = stdOut.split("\n", Qt::SkipEmptyParts);
if (ips.empty()) {
lastIp.setAddress(containerConfig.value(m_protocolName) // remove extra IPs from each line for case when user manually edited the wg0.conf
.toObject() // and added there more IPs for route his itnernal networks, like:
.value(config_key::subnet_address) // ...
.toString(protocols::wireguard::defaultSubnetAddress)); // 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";
} else { } else {
lastIp = ips.last(); int next = ips.last().split(".").last().toInt() + 1;
if (next > 254) {
errorCode = ErrorCode::AddressPoolError;
return connData;
}
nextIpNumber = QString::number(next);
} }
quint8 lastOctet = static_cast<quint8>(lastIp.toIPv4Address()); }
switch (lastOctet) {
case 254: result.setAddress(lastIp.toIPv4Address() + 3); break; QString subnetIp = containerConfig.value(m_protocolName).toObject().value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
case 255: result.setAddress(lastIp.toIPv4Address() + 2); break; {
default: result.setAddress(lastIp.toIPv4Address() + 1); break; QStringList l = subnetIp.split(".", Qt::SkipEmptyParts);
if (l.isEmpty()) {
errorCode = ErrorCode::AddressPoolError;
return connData;
} }
l.removeLast();
l.append(nextIpNumber);
return result; connData.clientIP = l.join(".");
}(); }
connData.clientIP = nextIp.toString();
// Get keys // Get keys
connData.serverPubKey = connData.serverPubKey = m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
connData.serverPubKey.replace("\n", ""); connData.serverPubKey.replace("\n", "");
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return connData; return connData;
@@ -168,12 +161,10 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
return connData; return connData;
} }
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'") QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'").arg(m_serverConfigPath);
.arg(m_serverConfigPath);
errorCode = m_serverController->runScript( errorCode = m_serverController->runScript(
credentials, credentials, m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
return connData; return connData;
} }
@@ -182,8 +173,8 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
const QJsonObject &containerConfig, ErrorCode &errorCode) const QJsonObject &containerConfig, ErrorCode &errorCode)
{ {
QString scriptData = amnezia::scriptData(m_configTemplate, container); QString scriptData = amnezia::scriptData(m_configTemplate, container);
QString config = m_serverController->replaceVars( QString config =
scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig)); m_serverController->replaceVars(scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode); ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
@@ -217,16 +208,16 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
return QJsonDocument(jConfig).toJson(); return QJsonDocument(jConfig).toJson();
} }
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
const bool isApiConfig, QString &protocolConfigString) QString &protocolConfigString)
{ {
processConfigWithDnsSettings(dns, protocolConfigString); processConfigWithDnsSettings(dns, protocolConfigString);
return protocolConfigString; return protocolConfigString;
} }
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
const bool isApiConfig, QString &protocolConfigString) QString &protocolConfigString)
{ {
processConfigWithDnsSettings(dns, protocolConfigString); processConfigWithDnsSettings(dns, protocolConfigString);
+6 -10
View File
@@ -1,7 +1,6 @@
#ifndef WIREGUARD_CONFIGURATOR_H #ifndef WIREGUARD_CONFIGURATOR_H
#define WIREGUARD_CONFIGURATOR_H #define WIREGUARD_CONFIGURATOR_H
#include <QHostAddress>
#include <QObject> #include <QObject>
#include <QProcessEnvironment> #include <QProcessEnvironment>
@@ -13,8 +12,8 @@ class WireguardConfigurator : public ConfiguratorBase
{ {
Q_OBJECT Q_OBJECT
public: public:
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, bool isAwg,
bool isAwg, QObject *parent = nullptr); QObject *parent = nullptr);
struct ConnectionData struct ConnectionData
{ {
@@ -27,18 +26,15 @@ public:
QString port; QString port;
}; };
QString createConfig(const ServerCredentials &credentials, DockerContainer container, QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
const QJsonObject &containerConfig, ErrorCode &errorCode); ErrorCode &errorCode);
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
QString &protocolConfigString); QString processConfigWithExportSettings(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(); static ConnectionData genClientKeys();
private: private:
QList<QHostAddress> getIpsFromConf(const QString &input);
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container, ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode); const QJsonObject &containerConfig, ErrorCode &errorCode);
+1 -18
View File
@@ -1,6 +1,5 @@
#include "coreController.h" #include "coreController.h"
#include <QDirIterator>
#include <QTranslator> #include <QTranslator>
#if defined(Q_OS_ANDROID) #if defined(Q_OS_ANDROID)
@@ -239,23 +238,7 @@ void CoreController::updateTranslator(const QLocale &locale)
QCoreApplication::removeTranslator(m_translator.get()); QCoreApplication::removeTranslator(m_translator.get());
} }
QStringList availableTranslations; QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm";
QDirIterator it(":/translations", QStringList("amneziavpn_*.qm"), QDir::Files);
while (it.hasNext()) {
availableTranslations << it.next();
}
// This code allow to load translation for the language only, without country code
const QString lang = locale.name().split("_").first();
const QString translationFilePrefix = QString(":/translations/amneziavpn_") + lang;
QString strFileName = QString(":/translations/amneziavpn_%1.qm").arg(locale.name());
for (const QString &translation : availableTranslations) {
if (translation.contains(translationFilePrefix)) {
strFileName = translation;
break;
}
}
if (m_translator->load(strFileName)) { if (m_translator->load(strFileName)) {
if (QCoreApplication::installTranslator(m_translator.get())) { if (QCoreApplication::installTranslator(m_translator.get())) {
m_settings->setAppLanguage(locale); m_settings->setAppLanguage(locale);
+5 -18
View File
@@ -26,10 +26,6 @@ namespace
constexpr char apiPayload[] = "api_payload"; constexpr char apiPayload[] = "api_payload";
constexpr char keyPayload[] = "key_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) GatewayController::GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent)
@@ -198,16 +194,16 @@ QStringList GatewayController::getProxyUrls()
QList<QSslError> sslErrors; QList<QSslError> sslErrors;
QNetworkReply *reply; QNetworkReply *reply;
QStringList proxyStorageUrls; QStringList proxyStorageUrl;
if (m_isDevEnvironment) { if (m_isDevEnvironment) {
proxyStorageUrls = QString(DEV_S3_ENDPOINT).split(", "); proxyStorageUrl = QStringList { DEV_S3_ENDPOINT };
} else { } else {
proxyStorageUrls = QString(PROD_S3_ENDPOINT).split(", "); proxyStorageUrl = QStringList { PROD_S3_ENDPOINT };
} }
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY; QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
for (const auto &proxyStorageUrl : proxyStorageUrls) { for (const auto &proxyStorageUrl : proxyStorageUrl) {
request.setUrl(proxyStorageUrl); request.setUrl(proxyStorageUrl);
reply = amnApp->networkManager()->get(request); reply = amnApp->networkManager()->get(request);
@@ -266,16 +262,7 @@ bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray
} else if (responseBody.contains("html")) { } else if (responseBody.contains("html")) {
qDebug() << "The response contains an html tag"; qDebug() << "The response contains an html tag";
return true; return true;
} else if (reply->error() == QNetworkReply::NetworkError::ContentNotFoundError) { } else if (reply->error() == QNetworkReply::NetworkError::NoError && checkEncryption) {
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 { try {
QSimpleCrypto::QBlockCipher blockCipher; QSimpleCrypto::QBlockCipher blockCipher;
static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt)); static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
+6 -10
View File
@@ -709,7 +709,7 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto); QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto);
// TODO reimplement with netstat // TODO reimplement with netstat
QString script = QString("which lsof > /dev/null 2>&1 || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port); QString script = QString("which lsof &>/dev/null || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
for (auto &port : fixedPorts) { for (auto &port : fixedPorts) {
script = script.append("|:%1").arg(port); script = script.append("|:%1").arg(port);
} }
@@ -757,6 +757,10 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, DockerContainer container) ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, DockerContainer container)
{ {
if (credentials.userName == "root") {
return ErrorCode::NoError;
}
QString stdOut; QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) { auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n"; stdOut += data + "\n";
@@ -770,16 +774,8 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo); const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo);
ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr); ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
if (credentials.userName != "root" && stdOut.contains("sudo:") && !stdOut.contains("uname:") && stdOut.contains("not found")) if (!stdOut.contains("sudo"))
return ErrorCode::ServerSudoPackageIsNotPreinstalled;
if (credentials.userName != "root" && !stdOut.contains("sudo") && !stdOut.contains("wheel"))
return ErrorCode::ServerUserNotInSudo; 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; return error;
} }
-4
View File
@@ -54,10 +54,6 @@ namespace amnezia
ServerCancelInstallation = 204, ServerCancelInstallation = 204,
ServerUserNotInSudo = 205, ServerUserNotInSudo = 205,
ServerPacketManagerError = 206, ServerPacketManagerError = 206,
ServerSudoPackageIsNotPreinstalled = 207,
ServerUserDirectoryNotAccessible = 208,
ServerUserNotAllowedInSudoers = 209,
ServerUserPasswordRequired = 210,
// Ssh connection errors // Ssh connection errors
SshRequestDeniedError = 300, SshRequestDeniedError = 300,
+2 -6
View File
@@ -20,12 +20,8 @@ QString errorString(ErrorCode code) {
case(ErrorCode::ServerContainerMissingError): errorMessage = QObject::tr("Server error: Docker container missing"); break; 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::ServerDockerFailedError): errorMessage = QObject::tr("Server error: Docker failed"); break;
case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break; case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break;
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user is not a member of the sudo group"); 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: Package manager error"); break; case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break;
case(ErrorCode::ServerSudoPackageIsNotPreinstalled): errorMessage = QObject::tr("The sudo package is not pre-installed on the server"); 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 // Libssh errors
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break; case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
+2 -13
View File
@@ -1,13 +1,2 @@
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); opt="--version";\ CUR_USER=$(whoami);\
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); opt="--version";\ groups $CUR_USER
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;\
cd ~;\
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 -1
View File
@@ -1,4 +1,4 @@
CUR_USER=$(whoami 2>/dev/null || echo $HOME | sed 's/.*\///');\ CUR_USER=$(whoami);\
sudo mkdir -p $DOCKERFILE_FOLDER;\ sudo mkdir -p $DOCKERFILE_FOLDER;\
sudo chown $CUR_USER $DOCKERFILE_FOLDER;\ sudo chown $CUR_USER $DOCKERFILE_FOLDER;\
if ! sudo docker network ls | grep -q amnezia-dns-net; then sudo docker network create \ if ! sudo docker network ls | grep -q amnezia-dns-net; then sudo docker network create \
+3 -3
View File
@@ -3334,8 +3334,8 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="22"/> <location filename="../core/errorstrings.cpp" line="22"/>
<source>The user is not a member of the sudo group</source> <source>The user does not have permission to use sudo</source>
<translation>المستخدم ليس عضوًا في مجموعة sudo</translation> <translation>ليس لدي المستخدم الصلحيات لأستخدام sudo</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="26"/> <location filename="../core/errorstrings.cpp" line="26"/>
@@ -3399,7 +3399,7 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="23"/> <location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Package manager error</source> <source>Server error: Packet manager error</source>
<translation>خطأ في الخادم: خطأ في مدير الحزم</translation> <translation>خطأ في الخادم: خطأ في مدير الحزم</translation>
</message> </message>
<message> <message>
+4 -4
View File
@@ -3468,8 +3468,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="22"/> <location filename="../core/errorstrings.cpp" line="22"/>
<source>The user is not a member of the sudo group</source> <source>The user does not have permission to use sudo</source>
<translation>کاربر عضو گروه sudo نیست</translation> <translation>The user does not have permission to use sudo</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="26"/> <location filename="../core/errorstrings.cpp" line="26"/>
@@ -3590,8 +3590,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="23"/> <location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Package manager error</source> <source>Server error: Packet manager error</source>
<translation>خطای سرور: خطای مدیر بسته</translation> <translation>Server error: Packet manager error</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="34"/> <location filename="../core/errorstrings.cpp" line="34"/>
+4 -4
View File
@@ -3434,13 +3434,13 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="22"/> <location filename="../core/errorstrings.cpp" line="22"/>
<source>The user is not a member of the sudo group</source> <source>The user does not have permission to use sudo</source>
<translation> sudo </translation> <translation> sudo ि </translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="23"/> <location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Package manager error</source> <source>Server error: Packet manager error</source>
<translation> ि: ि</translation> <translation> ि: ि</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="26"/> <location filename="../core/errorstrings.cpp" line="26"/>
+4 -4
View File
@@ -3330,8 +3330,8 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="22"/> <location filename="../core/errorstrings.cpp" line="22"/>
<source>The user is not a member of the sudo group</source> <source>The user does not have permission to use sudo</source>
<translation> sudo </translation> <translation> sudo ကက</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="26"/> <location filename="../core/errorstrings.cpp" line="26"/>
@@ -3395,8 +3395,8 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="23"/> <location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Package manager error</source> <source>Server error: Packet manager error</source>
<translation> - Package manager </translation> <translation> မှု: Packet Manager </translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="34"/> <location filename="../core/errorstrings.cpp" line="34"/>
+134 -173
View File
@@ -6,7 +6,7 @@
<message> <message>
<location filename="../ui/qml/Components/AdLabel.qml" line="57"/> <location filename="../ui/qml/Components/AdLabel.qml" line="57"/>
<source>Amnezia Premium - for access to any website</source> <source>Amnezia Premium - for access to any website</source>
<translation>Amnezia Premium - для доступа к любым сайтам</translation> <translation>Amnezia Premium - для доступа к любым сайтам </translation>
</message> </message>
</context> </context>
<context> <context>
@@ -15,37 +15,37 @@
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="31"/> <location filename="../ui/models/api/apiAccountInfoModel.cpp" line="31"/>
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="34"/> <location filename="../ui/models/api/apiAccountInfoModel.cpp" line="34"/>
<source>Active</source> <source>Active</source>
<translation>Активна</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="34"/> <location filename="../ui/models/api/apiAccountInfoModel.cpp" line="34"/>
<source>Inactive</source> <source>Inactive</source>
<translation>Не активна</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="47"/> <location filename="../ui/models/api/apiAccountInfoModel.cpp" line="47"/>
<source>%1 out of %2</source> <source>%1 out of %2</source>
<translation>%1 из %2</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="51"/> <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> <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>Классический VPN для комфортной работы, загрузки больших файлов и просмотра видео. Доступ ко всем сайтам и онлайн-ресурсам. Скорость до 200 Мбит/с</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="54"/> <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> <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>Бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включен в бесплатный тариф.</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="125"/> <location filename="../ui/models/api/apiAccountInfoModel.cpp" line="125"/>
<source>amnezia_free_support_bot</source> <source>amnezia_free_support_bot</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="127"/> <location filename="../ui/models/api/apiAccountInfoModel.cpp" line="127"/>
<source>amnezia_premium_support_bot</source> <source>amnezia_premium_support_bot</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context> <context>
@@ -53,17 +53,17 @@
<message> <message>
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="203"/> <location filename="../ui/controllers/api/apiConfigsController.cpp" line="203"/>
<source>%1 installed successfully.</source> <source>%1 installed successfully.</source>
<translation>%1 успешно установлен.</translation> <translation type="unfinished">%1 успешно установлен.</translation>
</message> </message>
<message> <message>
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="258"/> <location filename="../ui/controllers/api/apiConfigsController.cpp" line="258"/>
<source>API config reloaded</source> <source>API config reloaded</source>
<translation>Конфигурация API перезагружена</translation> <translation type="unfinished">Конфигурация API перезагружена</translation>
</message> </message>
<message> <message>
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="262"/> <location filename="../ui/controllers/api/apiConfigsController.cpp" line="262"/>
<source>Successfully changed the country of connection to %1</source> <source>Successfully changed the country of connection to %1</source>
<translation>Страна подключения изменена на %1</translation> <translation type="unfinished">Изменение страны подключения на %1</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -92,18 +92,18 @@
<message> <message>
<location filename="../ui/models/api/apiServicesModel.cpp" line="68"/> <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> <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>Amnezia Premium VPN для комфортной работы, скачивания больших файлов и просмотра видео в высоком разрешении. Скорость до %1 Мбит/с. Безлимитный трафик.</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/models/api/apiServicesModel.cpp" line="72"/> <location filename="../ui/models/api/apiServicesModel.cpp" line="72"/>
<location filename="../ui/models/api/apiServicesModel.cpp" line="85"/> <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> <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>AmneziaFree предоставляет бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включен в бесплатный тариф.</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/models/api/apiServicesModel.cpp" line="82"/> <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> <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>Amnezia Premium VPN для комфортной работы, скачивания больших файлов и просмотра видео в высоком разрешении. Работает для любых сайтов без ограничений.</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/models/api/apiServicesModel.cpp" line="97"/> <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> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="97"/> <location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="97"/>
<source>AmneziaWG settings</source> <source>AmneziaWG settings</source>
<translation>Настройки AmneziaWG</translation> <translation type="unfinished">Настройки AmneziaWG</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="105"/> <location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="105"/>
<source>MTU</source> <source>MTU</source>
<translation>MTU</translation> <translation type="unfinished">MTU</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="181"/> <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> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="191"/> <location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="191"/>
<source>Port</source> <source>Port</source>
<translation>Порт</translation> <translation type="unfinished">Порт</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="278"/> <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> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="289"/> <location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="289"/>
<source>Only the settings for this device will be changed</source> <source>Only the settings for this device will be changed</source>
<translation>Будут изменены настройки только для этого устройства</translation> <translation>Будут изменены настройки только для этого устройства.</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="290"/> <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> <message>
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="91"/> <location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="91"/>
<source>WG settings</source> <source>WG settings</source>
<translation>Настройки WG</translation> <translation type="unfinished">Настройки WG</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="99"/> <location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="99"/>
<source>MTU</source> <source>MTU</source>
<translation>MTU</translation> <translation type="unfinished">MTU</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="116"/> <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> <message>
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="126"/> <location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="126"/>
<source>Port</source> <source>Port</source>
<translation>Порт</translation> <translation type="unfinished">Порт</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="149"/> <location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="149"/>
<source>Save</source> <source>Save</source>
<translation>Сохранить</translation> <translation type="unfinished">Сохранить</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="153"/> <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> <message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="46"/> <location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="46"/>
<source>support@amnezia.org</source> <source>support@amnezia.org</source>
<translation>support@amnezia.org</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Mail</source> <source>Mail</source>
@@ -1526,7 +1526,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="50"/> <location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="50"/>
<source>mailto:support@amnezia.org</source> <source>mailto:support@amnezia.org</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="57"/> <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> <message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="58"/> <location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="58"/>
<source>Discover the source code</source> <source>Discover the source code</source>
<translation>Посмотреть исходный код</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="61"/> <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> <message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="69"/> <location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="69"/>
<source>Visit official website</source> <source>Visit official website</source>
<translation>Посетить официальный сайт</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>https://amnezia.org</source> <source>https://amnezia.org</source>
@@ -1578,12 +1578,12 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiAvailableCountries.qml" line="84"/> <location filename="../ui/qml/Pages2/PageSettingsApiAvailableCountries.qml" line="84"/>
<source>Location for connection</source> <source>Location for connection</source>
<translation>Страны для подключения</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiAvailableCountries.qml" line="123"/> <location filename="../ui/qml/Pages2/PageSettingsApiAvailableCountries.qml" line="123"/>
<source>Unable change server location while there is an active connection</source> <source>Unable change server location while there is an active connection</source>
<translation>Невозможно изменить локацию во время активного соединения</translation> <translation type="unfinished">Невозможно изменить локацию во время активного соединения</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -1591,57 +1591,57 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="45"/> <location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="45"/>
<source>Active devices</source> <source>Active devices</source>
<translation>Активные устройства</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="46"/> <location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="46"/>
<source>Manage currently connected devices</source> <source>Manage currently connected devices</source>
<translation>Управление подключенными устройствами</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="55"/> <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 &apos;+&apos; and then the three dots at the top of the page.</source> <source>You can find the identifier on the Support tab or, for older versions of the app, by tapping &apos;+&apos; and then the three dots at the top of the page.</source>
<translation>Вы можете найти support tag во вкладке «Поддержка» или, в более ранних версиях приложения, нажав «+» на нижней панели, а затем три точки вверху страницы.</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="69"/> <location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="69"/>
<source> (current device)</source> <source> (current device)</source>
<translation> (текущее устройство)</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="70"/> <location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="70"/>
<source>Support tag: </source> <source>Support tag: </source>
<translation>Support tag: </translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="70"/> <location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="70"/>
<source>Last updated: </source> <source>Last updated: </source>
<translation>Последнее обновление: </translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="75"/> <location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="75"/>
<source>Cannot unlink device during active connection</source> <source>Cannot unlink device during active connection</source>
<translation>Невозможно отвязать устройство во время активного соединения</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="79"/> <location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="79"/>
<source>Are you sure you want to unlink this device?</source> <source>Are you sure you want to unlink this device?</source>
<translation>Вы уверены, что хотите отвязать это устройство?</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="80"/> <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&#xa0;Connect.</source> <source>This will unlink the device from your subscription. You can reconnect it anytime by pressing&#xa0;Connect.</source>
<translation>Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку &quot;Подключиться&quot;.</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="81"/> <location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="81"/>
<source>Continue</source> <source>Continue</source>
<translation>Продолжить</translation> <translation type="unfinished">Продолжить</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="82"/> <location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="82"/>
<source>Cancel</source> <source>Cancel</source>
<translation>Отменить</translation> <translation type="unfinished">Отменить</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -1649,82 +1649,82 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="22"/> <location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="22"/>
<source>Windows</source> <source>Windows</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="23"/> <location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="23"/>
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#windows</source> <source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#windows</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="29"/> <location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="29"/>
<source>macOS</source> <source>macOS</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="30"/> <location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="30"/>
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#macos</source> <source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#macos</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="36"/> <location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="36"/>
<source>Android</source> <source>Android</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="37"/> <location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="37"/>
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#android</source> <source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#android</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="43"/> <location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="43"/>
<source>AndroidTV</source> <source>AndroidTV</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="44"/> <location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="44"/>
<source>https://docs.amnezia.org/ru/documentation/instructions/android_tv_connect/</source> <source>https://docs.amnezia.org/ru/documentation/instructions/android_tv_connect/</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="50"/> <location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="50"/>
<source>iOS</source> <source>iOS</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="51"/> <location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="51"/>
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#ios</source> <source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#ios</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="57"/> <location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="57"/>
<source>Linux</source> <source>Linux</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="58"/> <location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="58"/>
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#linux</source> <source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#linux</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="64"/> <location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="64"/>
<source>Routers</source> <source>Routers</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="65"/> <location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="65"/>
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#routers</source> <source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#routers</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="101"/> <location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="101"/>
<source>How to connect on another device</source> <source>How to connect on another device</source>
<translation>Как подключить другие устройства</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="102"/> <location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="102"/>
<source>Setup guides on the Amnezia website</source> <source>Setup guides on the Amnezia website</source>
<translation>Инструкции по настройке</translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context> <context>
@@ -1739,82 +1739,82 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="23"/> <location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="23"/>
<source>Save AmneziaVPN config</source> <source>Save AmneziaVPN config</source>
<translation>Сохранить конфигурацию AmneziaVPN</translation> <translation type="unfinished">Сохранить конфигурацию AmneziaVPN</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="48"/> <location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="48"/>
<source>Configuration files</source> <source>Configuration files</source>
<translation>Файл конфигурации</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="49"/> <location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="49"/>
<source>For router setup or the AmneziaWG app</source> <source>For router setup or the AmneziaWG app</source>
<translation>Для настройки роутера или приложения AmneziaWG</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="61"/> <location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="61"/>
<source>The configuration needs to be reissued</source> <source>The configuration needs to be reissued</source>
<translation>Необходимо заново скачать конфигурацию и добавить ее в приложение</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="126"/> <location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="126"/>
<source> configuration file</source> <source> configuration file</source>
<translation> файл конфигурации</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="132"/> <location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="132"/>
<source>Generate a new configuration file</source> <source>Generate a new configuration file</source>
<translation>Создать новый файл конфигурации</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="133"/> <location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="133"/>
<source>The previously created one will stop working</source> <source>The previously created one will stop working</source>
<translation>Ранее созданный файл перестанет работать</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="144"/> <location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="144"/>
<source>Revoke the current configuration file</source> <source>Revoke the current configuration file</source>
<translation>Отозвать текущий файл конфигурации</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="177"/> <location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="177"/>
<source>Config file saved</source> <source>Config file saved</source>
<translation>Файл конфигурации сохранен</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="191"/> <location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="191"/>
<source>The config has been revoked</source> <source>The config has been revoked</source>
<translation>Конфигурация была отозвана</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="198"/> <location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="198"/>
<source>Generate a new %1 configuration file?</source> <source>Generate a new %1 configuration file?</source>
<translation>Создать новый %1 файл конфигурации?</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="200"/> <location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="200"/>
<source>Revoke the current %1 configuration file?</source> <source>Revoke the current %1 configuration file?</source>
<translation>Отозвать текущий %1 файл конфигурации?</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="203"/> <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> <source>Your previous configuration file will no longer work, and it will not be possible to connect using it</source>
<translation>Ваш предыдущий файл конфигурации не будет работать, и вы больше не сможете использовать его для подключения</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="204"/> <location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="204"/>
<source>Download</source> <source>Download</source>
<translation>Скачать</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="204"/> <location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="204"/>
<source>Continue</source> <source>Continue</source>
<translation>Продолжить</translation> <translation type="unfinished">Продолжить</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="205"/> <location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="205"/>
<source>Cancel</source> <source>Cancel</source>
<translation>Отменить</translation> <translation type="unfinished">Отменить</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -1834,7 +1834,7 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="37"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="37"/>
<source>Valid until</source> <source>Valid until</source>
<translation>Действует до</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Speed</source> <source>Speed</source>
@@ -1847,67 +1847,67 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="29"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="29"/>
<source>Subscription status</source> <source>Subscription status</source>
<translation>Статус подписки</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="45"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="45"/>
<source>Active connections</source> <source>Active connections</source>
<translation>Активные соединения</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="171"/> <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> <source>Configurations have been updated for some countries. Download and install the updated configuration files</source>
<translation>Сетевые адреса одного или нескольких серверов были обновлены. Пожалуйста, удалите старые конфигурацию и загрузите новые файлы</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/>
<source>Subscription key</source> <source>Subscription key</source>
<translation>Ключ для подключения</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/>
<source>Amnezia Premium subscription key</source> <source>Amnezia Premium subscription key</source>
<translation>Ключ подписки Amnezia Premium</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="194"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="194"/>
<source>Save VPN key to file</source> <source>Save VPN key to file</source>
<translation>Сохранить VPN-ключ в файле</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="195"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="195"/>
<source>Copy VPN key</source> <source>Copy VPN key</source>
<translation>Скопировать VPN ключ</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="216"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="216"/>
<source>Configuration files</source> <source>Configuration files</source>
<translation>Файл конфигурации</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="218"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="218"/>
<source>Manage configuration files</source> <source>Manage configuration files</source>
<translation>Управление файлами конфигурации</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="236"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="236"/>
<source>Active devices</source> <source>Active devices</source>
<translation>Активные устройства</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="238"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="238"/>
<source>Manage currently connected devices</source> <source>Manage currently connected devices</source>
<translation>Управление подключенными устройствами</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="255"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="255"/>
<source>Support</source> <source>Support</source>
<translation>Поддержка</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="270"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="270"/>
<source>How to connect on another device</source> <source>How to connect on another device</source>
<translation>Как подключить другие устройства</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="295"/> <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> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="332"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="332"/>
<source>Unlink this device</source> <source>Unlink this device</source>
<translation>Отвязать это устройство</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="335"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="335"/>
<source>Are you sure you want to unlink this device?</source> <source>Are you sure you want to unlink this device?</source>
<translation>Вы уверены, что хотите отвязать это устройство?</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="336"/> <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&#xa0;Connect.</source> <source>This will unlink the device from your subscription. You can reconnect it anytime by pressing&#xa0;Connect.</source>
<translation>Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку Подключиться.</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="342"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="342"/>
<source>Cannot unlink device during active connection</source> <source>Cannot unlink device during active connection</source>
<translation>Невозможно отвязать устройство во время активного соединения</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="370"/> <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> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="22"/> <location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="22"/>
<source>Telegram</source> <source>Telegram</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="30"/> <location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="30"/>
<source>Email Support</source> <source>Email Support</source>
<translation>Email</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="31"/> <location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="31"/>
<source>support@amnezia.org</source> <source>support@amnezia.org</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="38"/> <location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="38"/>
<source>Email Billing &amp; Orders</source> <source>Email Billing &amp; Orders</source>
<translation>По вопросам оплаты</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="39"/> <location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="39"/>
<source>help@vpnpay.io</source> <source>help@vpnpay.io</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="46"/> <location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="46"/>
<source>Website</source> <source>Website</source>
<translation>Сайт</translation> <translation type="unfinished">Веб-сайт</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="47"/> <location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="47"/>
<source>amnezia.org</source> <source>amnezia.org</source>
<translation>amnezia.org</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="81"/> <location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="81"/>
<source>Support</source> <source>Support</source>
<translation>Поддержка</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="82"/> <location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="82"/>
<source>Our technical support specialists are available to assist you at any time</source> <source>Our technical support specialists are available to assist you at any time</source>
<translation>Наши специалисты технической поддержки всегда готовы помочь вам.</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="109"/> <location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="109"/>
<source>Support tag</source> <source>Support tag</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="119"/> <location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="119"/>
<source>Copied</source> <source>Copied</source>
<translation>Скопировано</translation> <translation type="unfinished">Скопировано</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -2730,27 +2730,27 @@ Already installed containers were found on the server. All installed containers
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="98"/> <location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="98"/>
<source> connection settings</source> <source> connection settings</source>
<translation> настройки подключения</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="110"/> <location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="110"/>
<source>Click the &quot;connect&quot; button to create a connection configuration</source> <source>Click the &quot;connect&quot; button to create a connection configuration</source>
<translation>Нажмите кнопку «Подключиться», чтобы создать конфигурацию</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="130"/> <location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="130"/>
<source> server settings</source> <source> server settings</source>
<translation> настройки сервера</translation> <translation type="unfinished"> настройки сервера</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="172"/> <location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="172"/>
<source>Clear profile</source> <source>Clear profile</source>
<translation>Очистить профиль</translation> <translation type="unfinished">Очистить профиль</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="176"/> <location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="176"/>
<source>The connection configuration will be deleted for this device only</source> <source>The connection configuration will be deleted for this device only</source>
<translation>Конфигурация подключения будет удалена только на этом устройстве</translation> <translation type="unfinished">Конфигурация подключения будет удалена только для этого устройства</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="182"/> <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> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardEasy.qml" line="68"/> <location filename="../ui/qml/Pages2/PageSetupWizardEasy.qml" line="68"/>
<source>Choose Installation Type</source> <source>Choose Installation Type</source>
<translation>Выберите тип установки</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardEasy.qml" line="142"/> <location filename="../ui/qml/Pages2/PageSetupWizardEasy.qml" line="142"/>
<source>Manual</source> <source>Manual</source>
<translation>Ручная</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardEasy.qml" line="143"/> <location filename="../ui/qml/Pages2/PageSetupWizardEasy.qml" line="143"/>
<source>Choose a VPN protocol</source> <source>Choose a VPN protocol</source>
<translation>Выбрать VPN-протокол</translation> <translation>Выберите VPN-протокол</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardEasy.qml" line="200"/> <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> <message>
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="134"/> <location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="134"/>
<source>Access error!</source> <source>Access error!</source>
<translation>Ошибка доступа!</translation> <translation type="unfinished">Ошибка доступа!</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="140"/> <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> <message>
<location filename="../core/errorstrings.cpp" line="15"/> <location filename="../core/errorstrings.cpp" line="15"/>
<source>The selected protocol is not supported on the current platform</source> <source>The selected protocol is not supported on the current platform</source>
<translation>Выбранный протокол не поддерживается на данном устройстве</translation> <translation type="unfinished">Выбранный протокол не поддерживается на данном устройстве</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="18"/> <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>
<message> <message>
<location filename="../core/errorstrings.cpp" line="23"/> <location filename="../core/errorstrings.cpp" line="23"/>
<source>The user is not a member of the sudo group</source> <source>The user does not have permission to use sudo</source>
<translation>Пользователь не входит в группу sudo</translation> <translation>У пользователя нет прав на использование sudo</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="24"/> <location filename="../core/errorstrings.cpp" line="24"/>
<source>Server error: Package manager error</source> <source>Server error: Packet manager error</source>
<translation>Ошибка сервера: Ошибка менеджера пакетов</translation> <translation>Ошибка сервера: ошибка менеджера пакетов</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="27"/> <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"/> <location filename="../core/errorstrings.cpp" line="55"/>
<source>VPN Protocols is not installed. <source>VPN Protocols is not installed.
Please install VPN container at first</source> Please install VPN container at first</source>
<translation>VPN-протоколы не установлены. <translation type="unfinished">VPN-протоколы не установлены.
Пожалуйста, установите протокол</translation> Пожалуйста, установите протокол</translation>
</message> </message>
<message> <message>
@@ -4248,7 +4248,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<message> <message>
<location filename="../core/errorstrings.cpp" line="67"/> <location filename="../core/errorstrings.cpp" line="67"/>
<source>Failed to decrypt response payload</source> <source>Failed to decrypt response payload</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="68"/> <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> <message>
<location filename="../core/errorstrings.cpp" line="69"/> <location filename="../core/errorstrings.cpp" line="69"/>
<source>The limit of allowed configurations per subscription has been exceeded</source> <source>The limit of allowed configurations per subscription has been exceeded</source>
<translation>Превышен лимит разрешенных конфигураций для одной подписки</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="73"/> <location filename="../core/errorstrings.cpp" line="73"/>
@@ -4435,27 +4435,27 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
<message> <message>
<location filename="../containers/containers_defs.cpp" line="113"/> <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> <source>Shadowsocks masks VPN traffic, making it resemble normal web traffic, but it may still be detected by certain analysis systems.</source>
<translation>Shadowsocks маскирует VPN-трафик, делая его похожим на обычный веб-трафик, но он все равно может быть обнаружен некоторыми системами анализа.</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="115"/> <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> <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>OpenVPN over Cloak OpenVPN с маскировкой под веб-трафик , а также с защитой от обнаружения и систем анализа трафика. Он очень устойчив к обнаружению, но имеет низкую скорость работы в сравнении с другими похожими протоколами.</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="118"/> <location filename="../containers/containers_defs.cpp" line="118"/>
<source>WireGuard - popular VPN protocol with high performance, high speed and low power consumption.</source> <source>WireGuard - popular VPN protocol with high performance, high speed and low power consumption.</source>
<translation>WireGuard популярный VPN-протокол с высокой производительностью, высокой скоростью и низким энергопотреблением.</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="121"/> <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> <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>AmneziaWG специальный протокол от Amnezia, основанный на WireGuard. Он обеспечивает высокую скорость соединения и гарантирует стабильную работу даже в самых сложных условиях.</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="124"/> <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> <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>XRay с REALITY маскирует VPN-трафик под веб-трафик. Обладает высокой устойчивостью к обнаружению и обеспечивает высокую скорость соединения.</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="143"/> <location filename="../containers/containers_defs.cpp" line="143"/>
@@ -4467,12 +4467,7 @@ 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 * Flexible customisation to suit user needs to work with different operating systems and devices
* Recognised by DPI systems and therefore susceptible to blocking * Recognised by DPI systems and therefore susceptible to blocking
* Can operate over both TCP and UDP network protocols.</source> * Can operate over both TCP and UDP network protocols.</source>
<translation>OpenVPN является одним из самых популярных и проверенных временем VPN-протоколов. Он использует собственный протокол безопасности, и криптографические протоколы SSL/TLS для шифрования и обмена ключами. Более того, поддержка множества методов аутентификации делает OpenVPN универсальным, адаптируемым и подходящим для широкого спектра устройств и операционных систем. Благодаря своему открытому коду, OpenVPN подвергается тщательной проверке со стороны мирового сообщества, что постоянно укрепляет его безопасность. Имея отличный баланс между производительностью, безопасностью и совместимостью OpenVPN остается лучшим выбором для людей и компаний, заботящихся о конфиденциальности, однако OpenVPN легко распознается современными системами анализа трафика. <translation type="unfinished"></translation>
Доступен в AmneziaVPN на всех платформах
Нормальное энергопотребление на мобильных устройствах
Гибкая настройка полезная при работе с различными операционными системами и устройствами
Распознается системами DPI и, следовательно, уязвим к блокировкам
Может работать как по TCP, так и по UDP протоколу.</translation>
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="168"/> <location filename="../containers/containers_defs.cpp" line="168"/>
@@ -4492,22 +4487,7 @@ Immediately after receiving the first data packet, Cloak authenticates the incom
* Not recognised by detection systems * Not recognised by detection systems
* Works over TCP network protocol, 443 port. * Works over TCP network protocol, 443 port.
</source> </source>
<translation>Это связка протокола OpenVPN и плагина Cloak, созданная специально для защиты от обнаружения. <translation type="unfinished"></translation>
OpenVPN обеспечивает безопасное VPN-соединение, шифруя весь интернет-трафик между клиентом и сервером.
Плагин Cloak защищает OpenVPN от обнаружения.
Cloak может изменять метаданные пакета, чтобы полностью замаскировать VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью метода Active Probing. Это делает его очень устойчивым к обнаружению.
Сразу после получения первого пакета данных Cloak аутентифицирует входящее соединение, если аутентификация не удалась, плагин маскирует сервер под настоящий веб-сайт, и ваш VPN становится невидимым для систем анализа. Имеет низкую скорость работы в сравнении с другими похожими протоколами.
* Доступно в AmneziaVPN на всех платформах.
* Высокое энергопотребление на мобильных устройствах
* Гибкие настройки
* Не распознается системами обнаружения.
* Работает по сетевому протоколу TCP, порт 443.
</translation>
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="185"/> <location filename="../containers/containers_defs.cpp" line="185"/>
@@ -4520,15 +4500,7 @@ WireGuard is very susceptible to detection and blocking due to its distinct pack
* Minimum number of settings * Minimum number of settings
* Easily recognised by DPI analysis systems, susceptible to blocking * Easily recognised by DPI analysis systems, susceptible to blocking
* Works over UDP network protocol.</source> * Works over UDP network protocol.</source>
<translation>Популярный VPN-протокол с упрощенной архитектурой. <translation type="unfinished"></translation>
WireGuard обеспечивает стабильное VPN-соединение и высокую производительность на всех устройствах. Он использует закодированные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность передачи данных.
WireGuard очень чувствителен к обнаружению и блокировке из-за различных сигнатур пакетов. В отличие от некоторых других VPN протоколов, использующих методы запутывания, последовательные шаблоны сигнатур пакетов WireGuard легко идентифицируются системами анализа трафика.
* Доступно в AmneziaVPN на всех платформах.
* Низкое энергопотребление
* Минимальное количество настроек
* Легко распознается системами анализа DPI, подвержен блокировке.
* Работает по сетевому протоколу UDP.</translation>
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="198"/> <location filename="../containers/containers_defs.cpp" line="198"/>
@@ -4541,15 +4513,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin
* Minimum number of settings * Minimum number of settings
* Not recognised by traffic analysis systems * Not recognised by traffic analysis systems
* Works over UDP network protocol.</source> * Works over UDP network protocol.</source>
<translation>AmneziaWG это современная версия популярного VPN протокола, основанная на базе WireGuard, сохранившая упрощенную архитектуру и высокопроизводительные возможности на всех устройствах. <translation type="unfinished"></translation>
Хотя WireGuard известен своей эффективностью, обнаружить его довольно легко из-за различных сигнатур пакетов. AmneziaWG решает эту проблему, используя более совершенные методы работы, смешивая свой трафик с обычным интернет-трафиком.
Это означает, что AmneziaWG сохраняет высокую производительность оригинала, добавляя при этом дополнительный уровень скрытности, что делает его отличным выбором для тех, кому нужно быстрое и незаметное VPN-соединение.
* Доступно в AmneziaVPN на всех платформах.
* Низкое энергопотребление
* Минимальное количество настроек
* Не распознается системами анализа трафика.
* Работает по сетевому протоколу UDP.</translation>
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="214"/> <location filename="../containers/containers_defs.cpp" line="214"/>
@@ -4557,10 +4521,7 @@ 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. 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. 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&apos;s innovative &quot;friend or foe&quot; recognition at the TLS handshake enhances security. This makes REALITY a robust solution for maintaining internet freedom.</source> Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY&apos;s innovative &quot;friend or foe&quot; recognition at the TLS handshake enhances security. This makes REALITY a robust solution for maintaining internet freedom.</source>
<translation>Протокол REALITY, современная разработка от создателей XRay. Призван обеспечить высочайший уровень защиты от обнаружения благодаря инновационному подходу к безопасности и конфиденциальности. <translation type="unfinished"></translation>
Он безошибочно идентифицирует злоумышленников на этапе установления связи TLS, беспрепятственно работая в качестве прокси-сервера для оригинального клиента и перенаправляя злоумышленников на подлинные веб-сайты, предоставляя тем самым подлинный сертификат TLS и данные.
Эта расширенная возможность отличает REALITY от аналогичных технологий тем, что способна маскироваться под случайный веб-трафик без использования специальных настроек.
В отличие от старых протоколов, таких как VMess, VLESS и транспорт XTLS-Vision, REALITY имеет инновационную технологию распознавания «свой-чужой».Это делает REALITY надежным решением для обеспечения доступа к свободному интернету.</translation>
</message> </message>
<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> <source>WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship.</source>
@@ -4614,12 +4575,12 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
* Configurable encryption protocol * Configurable encryption protocol
* Detectable by some DPI systems * Detectable by some DPI systems
* Works over TCP network protocol.</source> * 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 только для ПК и ноутбуков * Доступен в AmneziaVPN только для ПК и ноутбуков
* Настраиваемый протокол шифрования * Настраиваемый протокол шифрования
* Распознается некоторыми системами DPI-анализа * Распознается некоторыми системами DPI-анализа
* Работает по сетевому протоколу TCP.</translation> * Работает по сетевому протоколу TCP</translation>
</message> </message>
<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. <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.
@@ -4950,12 +4911,12 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<message> <message>
<location filename="../ui/qml/Components/RenameServerDrawer.qml" line="30"/> <location filename="../ui/qml/Components/RenameServerDrawer.qml" line="30"/>
<source>Server name</source> <source>Server name</source>
<translation>Имя сервера</translation> <translation type="unfinished">Имя сервера</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Components/RenameServerDrawer.qml" line="41"/> <location filename="../ui/qml/Components/RenameServerDrawer.qml" line="41"/>
<source>Save</source> <source>Save</source>
<translation>Сохранить</translation> <translation type="unfinished">Сохранить</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -4971,7 +4932,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<message> <message>
<location filename="../ui/qml/Components/ServersListView.qml" line="86"/> <location filename="../ui/qml/Components/ServersListView.qml" line="86"/>
<source>Unable change server while there is an active connection</source> <source>Unable change server while there is an active connection</source>
<translation>Невозможно изменить сервер во время активного соединения</translation> <translation type="unfinished">Невозможно изменить сервер во время активного соединения</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -5225,12 +5186,12 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<message> <message>
<location filename="../containers/containers_defs.cpp" line="338"/> <location filename="../containers/containers_defs.cpp" line="338"/>
<source>Automatic</source> <source>Automatic</source>
<translation>Автоматическая</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="346"/> <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> <source>AmneziaWG protocol will be installed. It provides high connection speed and ensures stable operation even in the most challenging network conditions.</source>
<translation>Будет установлен протокол AmneziaWG. Он обеспечивает высокую скорость соединения и гарантирует стабильную работу даже в самых сложных условиях.</translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context> <context>
+4 -4
View File
@@ -3700,13 +3700,13 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="22"/> <location filename="../core/errorstrings.cpp" line="22"/>
<source>The user is not a member of the sudo group</source> <source>The user does not have permission to use sudo</source>
<translation>Користувач не входить до групи sudo</translation> <translation>The user does not have permission to use sudo</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="23"/> <location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Package manager error</source> <source>Server error: Packet manager error</source>
<translation>Помилка сервера: Помилка менеджера пакетів</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="26"/> <location filename="../core/errorstrings.cpp" line="26"/>
+3 -3
View File
@@ -3433,8 +3433,8 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="22"/> <location filename="../core/errorstrings.cpp" line="22"/>
<source>The user is not a member of the sudo group</source> <source>The user does not have permission to use sudo</source>
<translation>صارف sudo گروپ کا رکن نہیں ہے</translation> <translation>صارف کو sudo استعمال کرنے کی اجازت نہیں ہے</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="26"/> <location filename="../core/errorstrings.cpp" line="26"/>
@@ -3498,7 +3498,7 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="23"/> <location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Package manager error</source> <source>Server error: Packet manager error</source>
<translation>سرور خطا: پیکیج منیجر خطا</translation> <translation>سرور خطا: پیکیج منیجر خطا</translation>
</message> </message>
<message> <message>
+4 -4
View File
@@ -3675,13 +3675,13 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="22"/> <location filename="../core/errorstrings.cpp" line="22"/>
<source>The user is not a member of the sudo group</source> <source>The user does not have permission to use sudo</source>
<translation> sudo </translation> <translation>root权限</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="23"/> <location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Package manager error</source> <source>Server error: Packet manager error</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="26"/> <location filename="../core/errorstrings.cpp" line="26"/>
@@ -19,7 +19,7 @@ namespace
constexpr char cloak[] = "cloak"; constexpr char cloak[] = "cloak";
constexpr char awg[] = "awg"; constexpr char awg[] = "awg";
constexpr char apiEndpoint[] = "api_endpoint"; constexpr char apiEdnpoint[] = "api_endpoint";
constexpr char accessToken[] = "api_key"; constexpr char accessToken[] = "api_key";
constexpr char certificate[] = "certificate"; constexpr char certificate[] = "certificate";
constexpr char publicKey[] = "public_key"; constexpr char publicKey[] = "public_key";
@@ -251,6 +251,7 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
newServerConfig.insert(configKey::apiConfig, newApiConfig); newServerConfig.insert(configKey::apiConfig, newApiConfig);
newServerConfig.insert(configKey::authData, authData); newServerConfig.insert(configKey::authData, authData);
// newServerConfig.insert(
m_serversModel->editServer(newServerConfig, serverIndex); m_serversModel->editServer(newServerConfig, serverIndex);
if (reloadServiceConfig) { if (reloadServiceConfig) {
@@ -269,37 +270,54 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex) bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex)
{ {
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
auto installationUuid = m_settings->getInstallationUuid(true);
#ifdef Q_OS_IOS #ifdef Q_OS_IOS
IosController::Instance()->requestInetAccess(); IosController::Instance()->requestInetAccess();
QThread::msleep(10); QThread::msleep(10);
#endif #endif
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); if (serverConfig.value(config_key::configVersion).toInt()) {
QNetworkRequest request;
request.setTransferTimeout(apiDefs::requestTimeoutMsecs);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Authorization", "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8());
QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString();
request.setUrl(endpoint);
auto serverConfig = m_serversModel->getServerConfig(serverIndex); QString protocol = serverConfig.value(configKey::protocol).toString();
auto installationUuid = m_settings->getInstallationUuid(true);
QString serviceProtocol = serverConfig.value(configKey::protocol).toString(); ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol);
QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData); QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
apiPayload[configKey::uuid] = installationUuid; apiPayload[configKey::uuid] = installationUuid;
apiPayload[configKey::accessToken] = serverConfig.value(configKey::accessToken).toString();
apiPayload[configKey::apiEndpoint] = serverConfig.value(configKey::apiEndpoint).toString();
QByteArray responseBody; QByteArray requestBody = QJsonDocument(apiPayload).toJson();
ErrorCode errorCode = gatewayController.post(QString("%1v1/proxy_config"), apiPayload, responseBody);
if (errorCode == ErrorCode::NoError) { QNetworkReply *reply = amnApp->networkManager()->post(request, requestBody);
fillServerConfig(serviceProtocol, apiPayloadData, responseBody, serverConfig);
QEventLoop wait;
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
QList<QSslError> sslErrors;
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec();
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
if (errorCode != ErrorCode::NoError) {
reply->deleteLater();
emit errorOccurred(errorCode);
return false;
}
auto apiResponseBody = reply->readAll();
reply->deleteLater();
fillServerConfig(protocol, apiPayloadData, apiResponseBody, serverConfig);
m_serversModel->editServer(serverConfig, serverIndex); m_serversModel->editServer(serverConfig, serverIndex);
emit updateServerFromApiFinished(); emit updateServerFromApiFinished();
return true;
} else {
emit errorOccurred(errorCode);
return false;
} }
return true;
} }
bool ApiConfigsController::deactivateDevice() bool ApiConfigsController::deactivateDevice()
+8 -11
View File
@@ -27,6 +27,8 @@ namespace
ConfigTypes checkConfigFormat(const QString &config) ConfigTypes checkConfigFormat(const QString &config)
{ {
const QString openVpnConfigPatternCli = "client"; const QString openVpnConfigPatternCli = "client";
const QString openVpnConfigPatternProto1 = "proto tcp";
const QString openVpnConfigPatternProto2 = "proto udp";
const QString openVpnConfigPatternDriver1 = "dev tun"; const QString openVpnConfigPatternDriver1 = "dev tun";
const QString openVpnConfigPatternDriver2 = "dev tap"; const QString openVpnConfigPatternDriver2 = "dev tap";
@@ -51,13 +53,14 @@ namespace
|| (config.contains(amneziaConfigPatternHostName) && config.contains(amneziaConfigPatternUserName) || (config.contains(amneziaConfigPatternHostName) && config.contains(amneziaConfigPatternUserName)
&& config.contains(amneziaConfigPatternPassword))) { && config.contains(amneziaConfigPatternPassword))) {
return ConfigTypes::Amnezia; return ConfigTypes::Amnezia;
} else if (config.contains(openVpnConfigPatternCli)
&& (config.contains(openVpnConfigPatternProto1) || config.contains(openVpnConfigPatternProto2))
&& (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) {
return ConfigTypes::OpenVpn;
} else if (config.contains(wireguardConfigPatternSectionInterface) && config.contains(wireguardConfigPatternSectionPeer)) { } else if (config.contains(wireguardConfigPatternSectionInterface) && config.contains(wireguardConfigPatternSectionPeer)) {
return ConfigTypes::WireGuard; return ConfigTypes::WireGuard;
} else if ((config.contains(xrayConfigPatternInbound)) && (config.contains(xrayConfigPatternOutbound))) { } else if ((config.contains(xrayConfigPatternInbound)) && (config.contains(xrayConfigPatternOutbound))) {
return ConfigTypes::Xray; return ConfigTypes::Xray;
} else if (config.contains(openVpnConfigPatternCli)
&& (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) {
return ConfigTypes::OpenVpn;
} }
return ConfigTypes::Invalid; return ConfigTypes::Invalid;
} }
@@ -94,8 +97,6 @@ bool ImportController::extractConfigFromFile(const QString &fileName)
bool ImportController::extractConfigFromData(QString data) bool ImportController::extractConfigFromData(QString data)
{ {
m_maliciousWarningText.clear();
QString config = data; QString config = data;
QString prefix; QString prefix;
QString errormsg; QString errormsg;
@@ -344,7 +345,7 @@ QJsonObject ImportController::extractOpenVpnConfig(const QString &data)
arr.push_back(containers); arr.push_back(containers);
QString hostName; QString hostName;
const static QRegularExpression hostNameRegExp("remote\\s+([^\\s]+)"); const static QRegularExpression hostNameRegExp("remote (.*) [0-9]*");
QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data); QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data);
if (hostNameMatch.hasMatch()) { if (hostNameMatch.hasMatch()) {
hostName = hostNameMatch.captured(1); hostName = hostNameMatch.captured(1);
@@ -660,7 +661,6 @@ void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig)
if ((containerName == ContainerProps::containerToString(DockerContainer::OpenVpn)) if ((containerName == ContainerProps::containerToString(DockerContainer::OpenVpn))
|| (containerName == ContainerProps::containerToString(DockerContainer::Cloak)) || (containerName == ContainerProps::containerToString(DockerContainer::Cloak))
|| (containerName == ContainerProps::containerToString(DockerContainer::ShadowSocks))) { || (containerName == ContainerProps::containerToString(DockerContainer::ShadowSocks))) {
QString protocolConfig = QString protocolConfig =
containerConfig[ProtocolProps::protoToString(Proto::OpenVpn)].toObject()[config_key::last_config].toString(); containerConfig[ProtocolProps::protoToString(Proto::OpenVpn)].toObject()[config_key::last_config].toString();
QString protocolConfigJson = QJsonDocument::fromJson(protocolConfig.toUtf8()).object()[config_key::config].toString(); QString protocolConfigJson = QJsonDocument::fromJson(protocolConfig.toUtf8()).object()[config_key::config].toString();
@@ -682,11 +682,8 @@ void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig)
} }
} }
m_maliciousWarningText = tr("This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious "
"scripts, so only add it if you fully trust the provider of this config. ");
if (maliciousStrings.size() >= dangerousTagsMaxCount) { if (maliciousStrings.size() >= dangerousTagsMaxCount) {
m_maliciousWarningText.push_back(tr("<br>In the imported configuration, potentially dangerous lines were found:")); m_maliciousWarningText = tr("In the imported configuration, potentially dangerous lines were found:");
for (const auto &string : maliciousStrings) { for (const auto &string : maliciousStrings) {
m_maliciousWarningText.push_back(QString("<br><i>%1</i>").arg(string)); m_maliciousWarningText.push_back(QString("<br><i>%1</i>").arg(string));
} }
@@ -44,6 +44,7 @@ void SitesController::addSite(QString hostname)
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
Q_ARG(QStringList, QStringList() << hostname)); Q_ARG(QStringList, QStringList() << hostname));
} }
QMetaObject::invokeMethod(m_vpnConnection.get(), "flushDns", Qt::QueuedConnection);
}; };
const auto &resolveCallback = [this, processSite](const QHostInfo &hostInfo) { const auto &resolveCallback = [this, processSite](const QHostInfo &hostInfo) {
@@ -74,6 +75,7 @@ void SitesController::removeSite(int index)
QMetaObject::invokeMethod(m_vpnConnection.get(), "deleteRoutes", Qt::QueuedConnection, QMetaObject::invokeMethod(m_vpnConnection.get(), "deleteRoutes", Qt::QueuedConnection,
Q_ARG(QStringList, QStringList() << hostname)); Q_ARG(QStringList, QStringList() << hostname));
QMetaObject::invokeMethod(m_vpnConnection.get(), "flushDns", Qt::QueuedConnection);
emit finished(tr("Site removed: %1").arg(hostname)); emit finished(tr("Site removed: %1").arg(hostname));
} }
@@ -122,6 +124,7 @@ void SitesController::importSites(const QString &fileName, bool replaceExisting)
m_sitesModel->addSites(sites, replaceExisting); m_sitesModel->addSites(sites, replaceExisting);
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, Q_ARG(QStringList, ips)); QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, Q_ARG(QStringList, ips));
QMetaObject::invokeMethod(m_vpnConnection.get(), "flushDns", Qt::QueuedConnection);
emit finished(tr("Import completed")); emit finished(tr("Import completed"));
} }
+4 -4
View File
@@ -65,8 +65,8 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
case CardDescriptionRole: { case CardDescriptionRole: {
auto speed = apiServiceData.serviceInfo.speed; auto speed = apiServiceData.serviceInfo.speed;
if (serviceType == serviceType::amneziaPremium) { if (serviceType == serviceType::amneziaPremium) {
return tr("Amnezia Premium is classic VPN for seamless work, downloading large files, and watching videos. " return tr("Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. "
"Access all websites and online resources. Speeds up to %1 Mbps.") "Works for any sites with no restrictions. Speed up to %1 MBit/s. Unlimited traffic.")
.arg(speed); .arg(speed);
} else if (serviceType == serviceType::amneziaFree) { } else if (serviceType == serviceType::amneziaFree) {
QString description = tr("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."); QString description = tr("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.");
@@ -79,8 +79,8 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
} }
case ServiceDescriptionRole: { case ServiceDescriptionRole: {
if (serviceType == serviceType::amneziaPremium) { if (serviceType == serviceType::amneziaPremium) {
return tr("Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. " return tr("Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. "
"Access all websites and online resources."); "Works for any sites with no restrictions.");
} else { } else {
return tr("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."); return tr("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.");
} }
+10 -8
View File
@@ -1,11 +1,13 @@
#include "languageModel.h" #include "languageModel.h"
LanguageModel::LanguageModel(std::shared_ptr<Settings> settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent) LanguageModel::LanguageModel(std::shared_ptr<Settings> settings, QObject *parent)
: m_settings(settings), QAbstractListModel(parent)
{ {
QMetaEnum metaEnum = QMetaEnum::fromType<LanguageSettings::AvailableLanguageEnum>(); QMetaEnum metaEnum = QMetaEnum::fromType<LanguageSettings::AvailableLanguageEnum>();
for (int i = 0; i < metaEnum.keyCount(); i++) { for (int i = 0; i < metaEnum.keyCount(); i++) {
m_availableLanguages.push_back(LanguageModelData { getLocalLanguageName(static_cast<LanguageSettings::AvailableLanguageEnum>(i)), m_availableLanguages.push_back(
static_cast<LanguageSettings::AvailableLanguageEnum>(i) }); LanguageModelData {getLocalLanguageName(static_cast<LanguageSettings::AvailableLanguageEnum>(i)),
static_cast<LanguageSettings::AvailableLanguageEnum>(i) });
} }
} }
@@ -48,7 +50,8 @@ QString LanguageModel::getLocalLanguageName(const LanguageSettings::AvailableLan
case LanguageSettings::AvailableLanguageEnum::Burmese: strLanguage = "မြန်မာဘာသာ"; break; case LanguageSettings::AvailableLanguageEnum::Burmese: strLanguage = "မြန်မာဘာသာ"; break;
case LanguageSettings::AvailableLanguageEnum::Urdu: strLanguage = "اُرْدُوْ"; break; case LanguageSettings::AvailableLanguageEnum::Urdu: strLanguage = "اُرْدُوْ"; break;
case LanguageSettings::AvailableLanguageEnum::Hindi: strLanguage = "हिन्दी"; break; case LanguageSettings::AvailableLanguageEnum::Hindi: strLanguage = "हिन्दी"; break;
default: break; default:
break;
} }
return strLanguage; return strLanguage;
@@ -101,12 +104,11 @@ QString LanguageModel::getCurrentLanguageName()
return m_availableLanguages[getCurrentLanguageIndex()].name; return m_availableLanguages[getCurrentLanguageIndex()].name;
} }
QString LanguageModel::getCurrentSiteUrl(const QString &path) QString LanguageModel::getCurrentSiteUrl()
{ {
auto language = static_cast<LanguageSettings::AvailableLanguageEnum>(getCurrentLanguageIndex()); auto language = static_cast<LanguageSettings::AvailableLanguageEnum>(getCurrentLanguageIndex());
switch (language) { switch (language) {
case LanguageSettings::AvailableLanguageEnum::Russian: case LanguageSettings::AvailableLanguageEnum::Russian: return "https://storage.googleapis.com/amnezia/amnezia.org";
return "https://storage.googleapis.com/amnezia/amnezia.org" + (path.isEmpty() ? "" : (QString("?m-path=/%1").arg(path))); default: return "https://amnezia.org";
default: return QString("https://amnezia.org") + (path.isEmpty() ? "" : (QString("/%1").arg(path)));
} }
} }
+1 -1
View File
@@ -59,7 +59,7 @@ public slots:
int getCurrentLanguageIndex(); int getCurrentLanguageIndex();
int getLineHeightAppend(); int getLineHeightAppend();
QString getCurrentLanguageName(); QString getCurrentLanguageName();
QString getCurrentSiteUrl(const QString &path = ""); QString getCurrentSiteUrl();
signals: signals:
void updateTranslations(const QLocale &locale); void updateTranslations(const QLocale &locale);
+2 -2
View File
@@ -29,7 +29,7 @@ Rectangle {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: function() { onClicked: function() {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("premium")) Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/premium")
} }
} }
@@ -54,7 +54,7 @@ Rectangle {
Layout.rightMargin: 10 Layout.rightMargin: 10
Layout.leftMargin: 10 Layout.leftMargin: 10
text: qsTr("Amnezia Premium - for access to all websites and online resources") text: qsTr("Amnezia Premium - for access to any website")
color: AmneziaStyle.color.pearlGray color: AmneziaStyle.color.pearlGray
lineHeight: 18 lineHeight: 18
+1 -1
View File
@@ -252,7 +252,7 @@ PageType {
text: qsTr("Privacy Policy") text: qsTr("Privacy Policy")
clickedFunc: function() { clickedFunc: function() {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("policy")) Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/policy")
} }
} }
} }
@@ -45,7 +45,7 @@ PageType {
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
headerText: qsTr("Configuration Files") headerText: qsTr("Configuration files")
descriptionText: qsTr("For router setup or the AmneziaWG app") descriptionText: qsTr("For router setup or the AmneziaWG app")
} }
} }
@@ -26,7 +26,7 @@ PageType {
QtObject { QtObject {
id: statusObject id: statusObject
readonly property string title: qsTr("Subscription Status") readonly property string title: qsTr("Subscription status")
readonly property string contentKey: "subscriptionStatus" readonly property string contentKey: "subscriptionStatus"
readonly property string objectImageSource: "qrc:/images/controls/info.svg" readonly property string objectImageSource: "qrc:/images/controls/info.svg"
} }
@@ -34,7 +34,7 @@ PageType {
QtObject { QtObject {
id: endDateObject id: endDateObject
readonly property string title: qsTr("Valid Until") readonly property string title: qsTr("Valid until")
readonly property string contentKey: "endDate" readonly property string contentKey: "endDate"
readonly property string objectImageSource: "qrc:/images/controls/history.svg" readonly property string objectImageSource: "qrc:/images/controls/history.svg"
} }
@@ -42,7 +42,7 @@ PageType {
QtObject { QtObject {
id: deviceCountObject id: deviceCountObject
readonly property string title: qsTr("Active Connections") readonly property string title: qsTr("Active connections")
readonly property string contentKey: "connectedDevices" readonly property string contentKey: "connectedDevices"
readonly property string objectImageSource: "qrc:/images/controls/monitor.svg" readonly property string objectImageSource: "qrc:/images/controls/monitor.svg"
} }
@@ -183,7 +183,7 @@ PageType {
visible: false //footer.isVisibleForAmneziaFree visible: false //footer.isVisibleForAmneziaFree
text: qsTr("Subscription Key") text: qsTr("Subscription key")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
@@ -191,7 +191,7 @@ PageType {
shareConnectionDrawer.openTriggered() shareConnectionDrawer.openTriggered()
shareConnectionDrawer.isSelfHostedConfig = false; shareConnectionDrawer.isSelfHostedConfig = false;
shareConnectionDrawer.shareButtonText = qsTr("Save VPN key as a file") shareConnectionDrawer.shareButtonText = qsTr("Save VPN key to file")
shareConnectionDrawer.copyButtonText = qsTr("Copy VPN key") shareConnectionDrawer.copyButtonText = qsTr("Copy VPN key")
@@ -213,7 +213,7 @@ PageType {
visible: footer.isVisibleForAmneziaFree visible: footer.isVisibleForAmneziaFree
text: qsTr("Configuration Files") text: qsTr("Configuration files")
descriptionText: qsTr("Manage configuration files") descriptionText: qsTr("Manage configuration files")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
@@ -233,7 +233,7 @@ PageType {
visible: footer.isVisibleForAmneziaFree visible: footer.isVisibleForAmneziaFree
text: qsTr("Active Devices") text: qsTr("Active devices")
descriptionText: qsTr("Manage currently connected devices") descriptionText: qsTr("Manage currently connected devices")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
@@ -27,7 +27,7 @@ PageType {
QtObject { QtObject {
id: techSupport id: techSupport
readonly property string title: qsTr("Email") readonly property string title: qsTr("Email Support")
readonly property string description: qsTr("support@amnezia.org") readonly property string description: qsTr("support@amnezia.org")
readonly property string link: "mailto:support@amnezia.org" readonly property string link: "mailto:support@amnezia.org"
} }
@@ -140,7 +140,7 @@ PageType {
} }
onClicked: { onClicked: {
if (!checkable) { if (!checkable) {
PageController.showNotificationMessage(qsTr("Cannot change KillSwitch settings during active connection")) PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection"))
} }
} }
} }
@@ -175,7 +175,7 @@ PageType {
leftImageSource: "qrc:/images/controls/help-circle.svg" leftImageSource: "qrc:/images/controls/help-circle.svg"
onClicked: { onClicked: {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("starter-guide")) Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/starter-guide")
} }
} }
} }
+1 -1
View File
@@ -78,7 +78,7 @@ PageType {
height: containers.contentItem.height height: containers.contentItem.height
spacing: 16 spacing: 16
currentIndex: 0 currentIndex: 1
clip: true clip: true
interactive: false interactive: false
model: proxyContainersModel model: proxyContainersModel
+2 -8
View File
@@ -351,10 +351,8 @@ void VpnConnection::appendSplitTunnelingConfig()
sitesJsonArray.append(site); sitesJsonArray.append(site);
} }
if (sitesJsonArray.isEmpty()) { // Allow traffic to Amnezia DNS
sitesRouteMode = Settings::RouteMode::VpnAllSites; if (sitesRouteMode == Settings::VpnOnlyForwardSites) {
} else if (sitesRouteMode == Settings::VpnOnlyForwardSites) {
// Allow traffic to Amnezia DNS
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns1).toString()); sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns1).toString());
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString()); sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString());
} }
@@ -373,10 +371,6 @@ void VpnConnection::appendSplitTunnelingConfig()
for (const auto &app : apps) { for (const auto &app : apps) {
appsJsonArray.append(app.appPath.isEmpty() ? app.packageName : app.appPath); appsJsonArray.append(app.appPath.isEmpty() ? app.packageName : app.appPath);
} }
if (appsJsonArray.isEmpty()) {
appsRouteMode = Settings::AppsRouteMode::VpnAllApps;
}
} }
m_vpnConfiguration.insert(config_key::appSplitTunnelType, appsRouteMode); m_vpnConfiguration.insert(config_key::appSplitTunnelType, appsRouteMode);