mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-22 02:01:08 +07:00
Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 393926c78e | |||
| ecf5b7c68f | |||
| 0b6822d47b | |||
| ae32b26b83 | |||
| 0c42563389 | |||
| b14431662f | |||
| 58a803335d | |||
| 8a3183f101 | |||
| 9fbea76b74 | |||
| b3ff120bcf | |||
| 9b695df78d | |||
| 9dea98f020 | |||
| c4701d4e7a | |||
| 48903ca3a1 | |||
| 0c9fd4aef4 | |||
| b2af2e46ac | |||
| efc76a0683 | |||
| c4a553c166 | |||
| 69a00b0252 | |||
| 4257c08b43 | |||
| c9e5b92f79 | |||
| 99818c2ad8 | |||
| 99e3afabad | |||
| d3339a7f3a | |||
| 678bfffe49 | |||
| 728b48044c | |||
| 7ccbfa48bc | |||
| edee3fec10 | |||
| 92e33f62c5 | |||
| 76b01bc09a | |||
| 343a5fdc1a | |||
| f42fe9dde4 | |||
| 3676027ae1 | |||
| 3c239ec28d | |||
| 65b3015078 | |||
| 0f0b644630 | |||
| a9bf2dfcb9 | |||
| c34c7f21b0 | |||
| 88b166443f | |||
| 284019f954 | |||
| 4f77d7ba70 | |||
| 3fc1a51a4c | |||
| ef902e6ddb | |||
| df10271be0 | |||
| df743b0b39 | |||
| d1b3c05e02 | |||
| fa5e254d83 | |||
| 467459e6f3 | |||
| 40e113023e | |||
| 37cddd73ce | |||
| 2f91240766 | |||
| 5c9ab510cb | |||
| 886ca3f6ac | |||
| 95b330bd56 | |||
| 91bb378806 | |||
| d33b84f637 | |||
| 388fd6ac46 | |||
| 2717d9f5c2 | |||
| a480c17f38 | |||
| 0a3f69a477 | |||
| 832a525065 | |||
| 520094d430 | |||
| 8599f8f0a9 | |||
| 40bb2e8ee4 | |||
| fe5ffec7c3 | |||
| d502f1c4d2 | |||
| c3e59bbaea | |||
| 5f98d388ef | |||
| 2fc62cfa24 | |||
| b819d87245 | |||
| f39b4ecdc4 | |||
| 762603f0ad | |||
| a3601d4dea |
+2
-2
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
|||||||
|
|
||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
|
|
||||||
project(${PROJECT} VERSION 4.8.4.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 2077)
|
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
@@ -6,11 +6,11 @@
|
|||||||
[](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
|
[](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 sourse VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
|
[AmneziaVPN](https://amnezia.org) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
|
||||||
|
|
||||||
[](https://amnezia.org)
|
[](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. Теперь вы можете тестировать, архивировать или публиковать приложение.
|
||||||
|
|
||||||
Если сборка завершится с ошибкой:
|
Если сборка завершится с ошибкой:
|
||||||
```
|
```
|
||||||
|
|||||||
+1
-1
Submodule client/3rd-prebuilt updated: e555c78bcf...efad1a5b5c
@@ -31,10 +31,6 @@ add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
|
|||||||
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
|
add_definitions(-DDEV_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()
|
||||||
@@ -48,10 +44,6 @@ 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()
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#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>
|
||||||
@@ -19,13 +20,17 @@
|
|||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings,
|
||||||
bool isAwg, QObject *parent)
|
const QSharedPointer<ServerController> &serverController, bool isAwg,
|
||||||
|
QObject *parent)
|
||||||
: ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg)
|
: ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg)
|
||||||
{
|
{
|
||||||
m_serverConfigPath = m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
|
m_serverConfigPath =
|
||||||
m_serverPublicKeyPath = m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath;
|
m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
|
||||||
m_serverPskKeyPath = m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
|
m_serverPublicKeyPath =
|
||||||
|
m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath;
|
||||||
|
m_serverPskKeyPath =
|
||||||
|
m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
|
||||||
m_configTemplate = m_isAwg ? ProtocolScriptType::awg_template : ProtocolScriptType::wireguard_template;
|
m_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;
|
||||||
@@ -63,9 +68,31 @@ 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, ErrorCode &errorCode)
|
const QJsonObject &containerConfig,
|
||||||
|
ErrorCode &errorCode)
|
||||||
{
|
{
|
||||||
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
|
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
|
||||||
connData.host = credentials.hostName;
|
connData.host = credentials.hostName;
|
||||||
@@ -76,65 +103,45 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
|||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get list of already created clients (only IP addresses)
|
QString getIpsScript = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
|
||||||
QString nextIpNumber;
|
|
||||||
{
|
|
||||||
QString script = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
|
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
|
|
||||||
errorCode = m_serverController->runContainerScript(credentials, container, script, cbReadStdOut);
|
errorCode = m_serverController->runContainerScript(credentials, container, getIpsScript, cbReadStdOut);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
|
auto ips = getIpsFromConf(stdOut);
|
||||||
|
|
||||||
stdOut.replace("AllowedIPs = ", "");
|
QHostAddress nextIp = [&] {
|
||||||
stdOut.replace("/32", "");
|
QHostAddress result;
|
||||||
QStringList ips = stdOut.split("\n", Qt::SkipEmptyParts);
|
QHostAddress lastIp;
|
||||||
|
if (ips.empty()) {
|
||||||
// remove extra IPs from each line for case when user manually edited the wg0.conf
|
lastIp.setAddress(containerConfig.value(m_protocolName)
|
||||||
// and added there more IPs for route his itnernal networks, like:
|
.toObject()
|
||||||
// ...
|
.value(config_key::subnet_address)
|
||||||
// AllowedIPs = 10.8.1.6/32, 192.168.1.0/24, 192.168.2.0/24, ...
|
.toString(protocols::wireguard::defaultSubnetAddress));
|
||||||
// ...
|
|
||||||
// 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 {
|
||||||
int next = ips.last().split(".").last().toInt() + 1;
|
lastIp = ips.last();
|
||||||
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;
|
||||||
|
case 255: result.setAddress(lastIp.toIPv4Address() + 2); break;
|
||||||
|
default: result.setAddress(lastIp.toIPv4Address() + 1); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString subnetIp = containerConfig.value(m_protocolName).toObject().value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
|
return result;
|
||||||
{
|
}();
|
||||||
QStringList l = subnetIp.split(".", Qt::SkipEmptyParts);
|
|
||||||
if (l.isEmpty()) {
|
|
||||||
errorCode = ErrorCode::AddressPoolError;
|
|
||||||
return connData;
|
|
||||||
}
|
|
||||||
l.removeLast();
|
|
||||||
l.append(nextIpNumber);
|
|
||||||
|
|
||||||
connData.clientIP = l.join(".");
|
connData.clientIP = nextIp.toString();
|
||||||
}
|
|
||||||
|
|
||||||
// Get keys
|
// Get keys
|
||||||
connData.serverPubKey = m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
|
connData.serverPubKey =
|
||||||
|
m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
|
||||||
connData.serverPubKey.replace("\n", "");
|
connData.serverPubKey.replace("\n", "");
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return connData;
|
return connData;
|
||||||
@@ -161,10 +168,12 @@ 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)'").arg(m_serverConfigPath);
|
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'")
|
||||||
|
.arg(m_serverConfigPath);
|
||||||
|
|
||||||
errorCode = m_serverController->runScript(
|
errorCode = m_serverController->runScript(
|
||||||
credentials, m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
|
credentials,
|
||||||
|
m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
|
||||||
|
|
||||||
return connData;
|
return connData;
|
||||||
}
|
}
|
||||||
@@ -173,8 +182,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 =
|
QString config = m_serverController->replaceVars(
|
||||||
m_serverController->replaceVars(scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
|
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) {
|
||||||
@@ -208,16 +217,16 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
|
|||||||
return QJsonDocument(jConfig).toJson();
|
return QJsonDocument(jConfig).toJson();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns,
|
||||||
QString &protocolConfigString)
|
const bool isApiConfig, QString &protocolConfigString)
|
||||||
{
|
{
|
||||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||||
|
|
||||||
return protocolConfigString;
|
return protocolConfigString;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns,
|
||||||
QString &protocolConfigString)
|
const bool isApiConfig, QString &protocolConfigString)
|
||||||
{
|
{
|
||||||
processConfigWithDnsSettings(dns, protocolConfigString);
|
processConfigWithDnsSettings(dns, protocolConfigString);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#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>
|
||||||
|
|
||||||
@@ -12,8 +13,8 @@ class WireguardConfigurator : public ConfiguratorBase
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, bool isAwg,
|
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
|
||||||
QObject *parent = nullptr);
|
bool isAwg, QObject *parent = nullptr);
|
||||||
|
|
||||||
struct ConnectionData
|
struct ConnectionData
|
||||||
{
|
{
|
||||||
@@ -26,15 +27,18 @@ public:
|
|||||||
QString port;
|
QString port;
|
||||||
};
|
};
|
||||||
|
|
||||||
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
|
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
ErrorCode &errorCode);
|
const QJsonObject &containerConfig, ErrorCode &errorCode);
|
||||||
|
|
||||||
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
|
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
|
||||||
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
|
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);
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ CoreController::CoreController(const QSharedPointer<VpnConnection> &vpnConnectio
|
|||||||
initControllers();
|
initControllers();
|
||||||
initSignalHandlers();
|
initSignalHandlers();
|
||||||
|
|
||||||
|
initAndroidController();
|
||||||
|
initAppleController();
|
||||||
|
|
||||||
initNotificationHandler();
|
initNotificationHandler();
|
||||||
|
|
||||||
auto locale = m_settings->getAppLanguage();
|
auto locale = m_settings->getAppLanguage();
|
||||||
@@ -90,6 +93,9 @@ void CoreController::initModels()
|
|||||||
|
|
||||||
m_apiAccountInfoModel.reset(new ApiAccountInfoModel(this));
|
m_apiAccountInfoModel.reset(new ApiAccountInfoModel(this));
|
||||||
m_engine->rootContext()->setContextProperty("ApiAccountInfoModel", m_apiAccountInfoModel.get());
|
m_engine->rootContext()->setContextProperty("ApiAccountInfoModel", m_apiAccountInfoModel.get());
|
||||||
|
|
||||||
|
m_apiDevicesModel.reset(new ApiDevicesModel(m_settings, this));
|
||||||
|
m_engine->rootContext()->setContextProperty("ApiDevicesModel", m_apiDevicesModel.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initControllers()
|
void CoreController::initControllers()
|
||||||
@@ -129,7 +135,8 @@ void CoreController::initControllers()
|
|||||||
m_systemController.reset(new SystemController(m_settings));
|
m_systemController.reset(new SystemController(m_settings));
|
||||||
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
|
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
|
||||||
|
|
||||||
m_apiSettingsController.reset(new ApiSettingsController(m_serversModel, m_apiAccountInfoModel, m_apiCountryModel, m_settings));
|
m_apiSettingsController.reset(
|
||||||
|
new ApiSettingsController(m_serversModel, m_apiAccountInfoModel, m_apiCountryModel, m_apiDevicesModel, m_settings));
|
||||||
m_engine->rootContext()->setContextProperty("ApiSettingsController", m_apiSettingsController.get());
|
m_engine->rootContext()->setContextProperty("ApiSettingsController", m_apiSettingsController.get());
|
||||||
|
|
||||||
m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings));
|
m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings));
|
||||||
|
|||||||
@@ -25,8 +25,9 @@
|
|||||||
#include "ui/models/protocols/ikev2ConfigModel.h"
|
#include "ui/models/protocols/ikev2ConfigModel.h"
|
||||||
#endif
|
#endif
|
||||||
#include "ui/models/api/apiAccountInfoModel.h"
|
#include "ui/models/api/apiAccountInfoModel.h"
|
||||||
#include "ui/models/api/apiServicesModel.h"
|
|
||||||
#include "ui/models/api/apiCountryModel.h"
|
#include "ui/models/api/apiCountryModel.h"
|
||||||
|
#include "ui/models/api/apiDevicesModel.h"
|
||||||
|
#include "ui/models/api/apiServicesModel.h"
|
||||||
#include "ui/models/appSplitTunnelingModel.h"
|
#include "ui/models/appSplitTunnelingModel.h"
|
||||||
#include "ui/models/clientManagementModel.h"
|
#include "ui/models/clientManagementModel.h"
|
||||||
#include "ui/models/protocols/awgConfigModel.h"
|
#include "ui/models/protocols/awgConfigModel.h"
|
||||||
@@ -117,6 +118,7 @@ private:
|
|||||||
QSharedPointer<ApiServicesModel> m_apiServicesModel;
|
QSharedPointer<ApiServicesModel> m_apiServicesModel;
|
||||||
QSharedPointer<ApiCountryModel> m_apiCountryModel;
|
QSharedPointer<ApiCountryModel> m_apiCountryModel;
|
||||||
QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel;
|
QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel;
|
||||||
|
QSharedPointer<ApiDevicesModel> m_apiDevicesModel;
|
||||||
|
|
||||||
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
|
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
|
||||||
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
|
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ 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)
|
||||||
@@ -148,7 +152,7 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
|||||||
|
|
||||||
QByteArray encryptedResponseBody = reply->readAll();
|
QByteArray encryptedResponseBody = reply->readAll();
|
||||||
|
|
||||||
if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, false)) {
|
if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
|
||||||
auto requestFunction = [&request, &encryptedResponseBody, &requestBody](const QString &url) {
|
auto requestFunction = [&request, &encryptedResponseBody, &requestBody](const QString &url) {
|
||||||
request.setUrl(url);
|
request.setUrl(url);
|
||||||
return amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
|
return amnApp->networkManager()->post(request, QJsonDocument(requestBody).toJson());
|
||||||
@@ -157,12 +161,12 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
|||||||
auto replyProcessingFunction = [&encryptedResponseBody, &reply, &sslErrors, &key, &iv, &salt,
|
auto replyProcessingFunction = [&encryptedResponseBody, &reply, &sslErrors, &key, &iv, &salt,
|
||||||
this](QNetworkReply *nestedReply, const QList<QSslError> &nestedSslErrors) {
|
this](QNetworkReply *nestedReply, const QList<QSslError> &nestedSslErrors) {
|
||||||
encryptedResponseBody = nestedReply->readAll();
|
encryptedResponseBody = nestedReply->readAll();
|
||||||
if (!sslErrors.isEmpty() || !shouldBypassProxy(nestedReply, encryptedResponseBody, true, key, iv, salt)) {
|
|
||||||
sslErrors = nestedSslErrors;
|
|
||||||
reply = nestedReply;
|
reply = nestedReply;
|
||||||
return true;
|
if (!sslErrors.isEmpty() || shouldBypassProxy(nestedReply, encryptedResponseBody, true, key, iv, salt)) {
|
||||||
}
|
sslErrors = nestedSslErrors;
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
|
bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
|
||||||
@@ -194,16 +198,16 @@ QStringList GatewayController::getProxyUrls()
|
|||||||
QList<QSslError> sslErrors;
|
QList<QSslError> sslErrors;
|
||||||
QNetworkReply *reply;
|
QNetworkReply *reply;
|
||||||
|
|
||||||
QStringList proxyStorageUrl;
|
QStringList proxyStorageUrls;
|
||||||
if (m_isDevEnvironment) {
|
if (m_isDevEnvironment) {
|
||||||
proxyStorageUrl = QStringList { DEV_S3_ENDPOINT };
|
proxyStorageUrls = QString(DEV_S3_ENDPOINT).split(", ");
|
||||||
} else {
|
} else {
|
||||||
proxyStorageUrl = QStringList { PROD_S3_ENDPOINT };
|
proxyStorageUrls = QString(PROD_S3_ENDPOINT).split(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
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 : proxyStorageUrl) {
|
for (const auto &proxyStorageUrl : proxyStorageUrls) {
|
||||||
request.setUrl(proxyStorageUrl);
|
request.setUrl(proxyStorageUrl);
|
||||||
reply = amnApp->networkManager()->get(request);
|
reply = amnApp->networkManager()->get(request);
|
||||||
|
|
||||||
@@ -212,11 +216,6 @@ QStringList GatewayController::getProxyUrls()
|
|||||||
wait.exec();
|
wait.exec();
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||||
break;
|
|
||||||
}
|
|
||||||
reply->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto encryptedResponseBody = reply->readAll();
|
auto encryptedResponseBody = reply->readAll();
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
|
||||||
@@ -241,7 +240,7 @@ QStringList GatewayController::getProxyUrls()
|
|||||||
} catch (...) {
|
} catch (...) {
|
||||||
Utils::logException();
|
Utils::logException();
|
||||||
qCritical() << "error loading private key from environment variables or decrypting payload" << encryptedResponseBody;
|
qCritical() << "error loading private key from environment variables or decrypting payload" << encryptedResponseBody;
|
||||||
return {};
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto endpointsArray = QJsonDocument::fromJson(responseBody).array();
|
auto endpointsArray = QJsonDocument::fromJson(responseBody).array();
|
||||||
@@ -251,6 +250,11 @@ QStringList GatewayController::getProxyUrls()
|
|||||||
endpoints.push_back(endpoint.toString());
|
endpoints.push_back(endpoint.toString());
|
||||||
}
|
}
|
||||||
return endpoints;
|
return endpoints;
|
||||||
|
} else {
|
||||||
|
reply->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key,
|
bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key,
|
||||||
@@ -262,6 +266,15 @@ 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) {
|
||||||
|
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) {
|
} else if (checkEncryption) {
|
||||||
try {
|
try {
|
||||||
QSimpleCrypto::QBlockCipher blockCipher;
|
QSimpleCrypto::QBlockCipher blockCipher;
|
||||||
@@ -296,7 +309,7 @@ void GatewayController::bypassProxy(const QString &endpoint, QNetworkReply *repl
|
|||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
wait.exec();
|
wait.exec();
|
||||||
|
|
||||||
if (!replyProcessingFunction(reply, sslErrors)) {
|
if (replyProcessingFunction(reply, sslErrors)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -409,8 +409,10 @@ ErrorCode ServerController::installDockerWorker(const ServerCredentials &credent
|
|||||||
qDebug().noquote() << "ServerController::installDockerWorker" << stdOut;
|
qDebug().noquote() << "ServerController::installDockerWorker" << stdOut;
|
||||||
if (stdOut.contains("lock"))
|
if (stdOut.contains("lock"))
|
||||||
return ErrorCode::ServerPacketManagerError;
|
return ErrorCode::ServerPacketManagerError;
|
||||||
if (stdOut.contains("command not found"))
|
if (stdOut.contains("sudo:") && stdOut.contains("not found"))
|
||||||
return ErrorCode::ServerDockerFailedError;
|
return ErrorCode::ServerDockerFailedError;
|
||||||
|
if (stdOut.contains("Failed docker status"))
|
||||||
|
return ErrorCode::ServerDockerStatusNotActive;
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
@@ -757,10 +759,6 @@ 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";
|
||||||
@@ -774,8 +772,16 @@ 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 (!stdOut.contains("sudo"))
|
if (credentials.userName != "root" && stdOut.contains("sudo:") && !stdOut.contains("uname:") && stdOut.contains("not found"))
|
||||||
|
return ErrorCode::SudoPackageIsNotPreinstalled;
|
||||||
|
if (credentials.userName != "root" && !stdOut.contains("sudo") && !stdOut.contains("wheel"))
|
||||||
return ErrorCode::ServerUserNotInSudo;
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ namespace amnezia
|
|||||||
ServerCancelInstallation = 204,
|
ServerCancelInstallation = 204,
|
||||||
ServerUserNotInSudo = 205,
|
ServerUserNotInSudo = 205,
|
||||||
ServerPacketManagerError = 206,
|
ServerPacketManagerError = 206,
|
||||||
|
SudoPackageIsNotPreinstalled = 207,
|
||||||
|
ServerUserDirectoryNotAccessible = 208,
|
||||||
|
ServerUserNotAllowedInSudoers = 209,
|
||||||
|
ServerUserPasswordRequired = 210,
|
||||||
|
ServerDockerStatusNotActive = 211,
|
||||||
|
|
||||||
// Ssh connection errors
|
// Ssh connection errors
|
||||||
SshRequestDeniedError = 300,
|
SshRequestDeniedError = 300,
|
||||||
|
|||||||
@@ -20,8 +20,13 @@ 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 does not have permission to use sudo"); break;
|
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user is not a member of the sudo group"); break;
|
||||||
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break;
|
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Package manager error"); break;
|
||||||
|
case(ErrorCode::SudoPackageIsNotPreinstalled): errorMessage = QObject::tr("The sudo package is not pre-installed"); break;
|
||||||
|
case(ErrorCode::ServerUserDirectoryNotAccessible): errorMessage = QObject::tr("The server user's home directory is not accessible"); break;
|
||||||
|
case(ErrorCode::ServerUserNotAllowedInSudoers): errorMessage = QObject::tr("Action not allowed in sudoers"); break;
|
||||||
|
case(ErrorCode::ServerUserPasswordRequired): errorMessage = QObject::tr("The user's password is required"); break;
|
||||||
|
case(ErrorCode::ServerDockerStatusNotActive): errorMessage = QObject::tr("Docker service status is not active"); 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;
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M20 3H4C2.89543 3 2 3.89543 2 5V15C2 16.1046 2.89543 17 4 17H20C21.1046 17 22 16.1046 22 15V5C22 3.89543 21.1046 3 20 3Z" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M8 21H16" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12 17V21" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 522 B |
@@ -14,10 +14,15 @@ extension UIApplication {
|
|||||||
var keyWindows: [UIWindow] {
|
var keyWindows: [UIWindow] {
|
||||||
connectedScenes
|
connectedScenes
|
||||||
.compactMap {
|
.compactMap {
|
||||||
|
guard let windowScene = $0 as? UIWindowScene else { return nil }
|
||||||
if #available(iOS 15.0, *) {
|
if #available(iOS 15.0, *) {
|
||||||
($0 as? UIWindowScene)?.keyWindow
|
guard let keywindow = windowScene.keyWindow else {
|
||||||
|
windowScene.windows.first?.makeKey()
|
||||||
|
return windowScene.windows.first
|
||||||
|
}
|
||||||
|
return keywindow
|
||||||
} else {
|
} else {
|
||||||
($0 as? UIWindowScene)?.windows.first { $0.isKeyWindow }
|
return windowScene.windows.first { $0.isKeyWindow }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,6 +229,8 @@
|
|||||||
<file>ui/qml/Pages2/PageSettingsApiSupport.qml</file>
|
<file>ui/qml/Pages2/PageSettingsApiSupport.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSettingsApiInstructions.qml</file>
|
<file>ui/qml/Pages2/PageSettingsApiInstructions.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSettingsApiNativeConfigs.qml</file>
|
<file>ui/qml/Pages2/PageSettingsApiNativeConfigs.qml</file>
|
||||||
|
<file>ui/qml/Pages2/PageSettingsApiDevices.qml</file>
|
||||||
|
<file>images/controls/monitor.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/countriesFlags">
|
<qresource prefix="/countriesFlags">
|
||||||
<file>images/flagKit/ZW.svg</file>
|
<file>images/flagKit/ZW.svg</file>
|
||||||
|
|||||||
@@ -11,8 +11,13 @@ sudo docker run -d \
|
|||||||
--name $CONTAINER_NAME \
|
--name $CONTAINER_NAME \
|
||||||
$CONTAINER_NAME
|
$CONTAINER_NAME
|
||||||
|
|
||||||
|
# Create service for podman
|
||||||
|
if [ -n "$(sudo docker --version 2>/dev/null | grep podman)" ]; then \
|
||||||
|
sudo sh -c "podman generate systemd --restart-policy=always -t 1 --name $CONTAINER_NAME 2>/dev/null > $DOCKERFILE_FOLDER/container-$CONTAINER_NAME.service";\
|
||||||
|
sudo sh -c "systemctl enable --now $DOCKERFILE_FOLDER/container-$CONTAINER_NAME.service && docker update --restart no $CONTAINER_NAME > /dev/null";\
|
||||||
|
fi
|
||||||
|
|
||||||
sudo docker network connect amnezia-dns-net $CONTAINER_NAME
|
sudo docker network connect amnezia-dns-net $CONTAINER_NAME
|
||||||
|
|
||||||
# Prevent to route packets outside of the container in case if server behind of the NAT
|
# Prevent to route packets outside of the container in case if server behind of the NAT
|
||||||
#sudo docker exec -i $CONTAINER_NAME sh -c "ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up"
|
#sudo docker exec -i $CONTAINER_NAME sh -c "ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up"
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,18 @@
|
|||||||
|
if [ -n "$(sudo docker --version 2>/dev/null | grep podman)" ]; then sudo sh -c "\
|
||||||
|
test -d /var/cache/containers || mkdir -m 700 -p /var/cache/containers;\
|
||||||
|
test -f /var/cache/containers/short-name-aliases.conf || chmod 600 /var/cache/containers/short-name-aliases.conf>>/var/cache/containers/short-name-aliases.conf;\
|
||||||
|
grep -q '\[aliases\]' /var/cache/containers/short-name-aliases.conf || echo '[aliases]' >> /var/cache/containers/short-name-aliases.conf;\
|
||||||
|
grep -q ' # Amnezia start' /var/cache/containers/short-name-aliases.conf || printf '%s\n' \
|
||||||
|
' # Amnezia start' \
|
||||||
|
' \"3proxy/3proxy\" = \"docker.io/3proxy/3proxy\"' \
|
||||||
|
' \"amneziavpn/amnezia-wg\" = \"docker.io/amneziavpn/amnezia-wg\"' \
|
||||||
|
' \"amneziavpn/amneziawg-go\" = \"docker.io/amneziavpn/amneziawg-go\"' \
|
||||||
|
' \"amneziavpn/ipsec-server\" = \"docker.io/amneziavpn/ipsec-server\"' \
|
||||||
|
' \"amneziavpn/torpress\" = \"docker.io/amneziavpn/torpress\"' \
|
||||||
|
' \"atmoz/sftp\" = \"docker.io/atmoz/sftp\"' \
|
||||||
|
' \"mvance/unbound\" = \"docker.io/mvance/unbound\"' \
|
||||||
|
' \"alpine\" = \"docker.io/library/alpine\"' \
|
||||||
|
' # Amnezia finish' \
|
||||||
|
>> /var/cache/containers/short-name-aliases.conf";\
|
||||||
|
fi;\
|
||||||
sudo docker build --no-cache --pull -t $CONTAINER_NAME $DOCKERFILE_FOLDER
|
sudo docker build --no-cache --pull -t $CONTAINER_NAME $DOCKERFILE_FOLDER
|
||||||
|
|||||||
@@ -1,2 +1,13 @@
|
|||||||
CUR_USER=$(whoami);\
|
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); opt="--version";\
|
||||||
groups $CUR_USER
|
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); opt="--version";\
|
||||||
|
elif which yum > /dev/null 2>&1; then pm=$(which yum); opt="--version";\
|
||||||
|
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); opt="--version";\
|
||||||
|
else pm="uname"; opt="-a";\
|
||||||
|
fi;\
|
||||||
|
CUR_USER=$(whoami 2>/dev/null || echo ~ | 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,23 +1,37 @@
|
|||||||
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\
|
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install"; check_pkgs="-yq update"; wh_pkg="-s install"; docker_pkg="docker.io"; check_srv="docker"; dist="debian";\
|
||||||
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\
|
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; wh_pkg="--assumeno install --setopt=tsflags=test"; docker_pkg="docker"; check_srv="docker"; dist="fedora";\
|
||||||
elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\
|
elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; wh_pkg="--assumeno install --setopt=tsflags=test"; docker_pkg="docker"; check_srv="docker"; dist="centos";\
|
||||||
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); silent_inst="-S --noconfirm --noprogressbar --quiet"; check_pkgs="-Sup"; docker_pkg="docker"; dist="archlinux";\
|
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); silent_inst="-S --noconfirm --noprogressbar --quiet"; check_pkgs="-Sup"; wh_pkg="-Sp"; docker_pkg="docker"; check_srv="docker"; dist="archlinux";\
|
||||||
else echo "Packet manager not found"; exit 1; fi;\
|
else echo "Packet manager not found"; exit 1; fi;\
|
||||||
echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg";\
|
echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, Check pkgs command: $check_pkgs, What pkg command: $wh_pkg, Docker pkg: $docker_pkg, Check service: $check_srv";\
|
||||||
if [ "$dist" = "debian" ]; then export DEBIAN_FRONTEND=noninteractive; fi;\
|
if [ "$dist" = "debian" ]; then export DEBIAN_FRONTEND=noninteractive; fi;\
|
||||||
|
echo $LANG | grep -qE '^(en_US.UTF-8|C.UTF-8|C)$' || export LC_ALL=C;\
|
||||||
if ! command -v sudo > /dev/null 2>&1; then $pm $check_pkgs; $pm $silent_inst sudo; fi;\
|
if ! command -v sudo > /dev/null 2>&1; then $pm $check_pkgs; $pm $silent_inst sudo; fi;\
|
||||||
if ! command -v fuser > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst psmisc; fi;\
|
if ! command -v fuser > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst psmisc; fi;\
|
||||||
if ! command -v lsof > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst lsof; fi;\
|
if ! command -v lsof > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst lsof; fi;\
|
||||||
if ! command -v docker > /dev/null 2>&1; then \
|
if ! command -v docker > /dev/null 2>&1; then \
|
||||||
sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\
|
sudo $pm $check_pkgs;\
|
||||||
sleep 5; sudo systemctl enable --now docker; sleep 5;\
|
if [ -n "$(sudo $pm $wh_pkg $docker_pkg 2>/dev/null | grep moby-engine)" ]; \
|
||||||
|
then echo "Docker is not supported"; exit 1;\
|
||||||
|
else sudo $pm $silent_inst $docker_pkg;\
|
||||||
|
fi;\
|
||||||
|
if [ -n "$(sudo docker --version 2>/dev/null | grep podman)" ]; then docker_pkg="podman-docker"; check_srv="podman.socket podman";\
|
||||||
|
if [ -n "$(sudo docker --version 2>&1 | grep /etc/containers/nodocker)" ]; then sudo touch /etc/containers/nodocker; fi;\
|
||||||
|
fi;\
|
||||||
|
sleep 5; sudo systemctl enable --now $check_srv 2>/dev/null; sleep 5;\
|
||||||
|
fi;\
|
||||||
|
if [ -n "$(sudo docker --version 2>&1 | grep moby-engine)" ]; then echo "Docker is not supported"; exit 1;\
|
||||||
|
elif [ -n "$(sudo docker --version 2>&1 | grep podman)" ]; then check_srv="podman.socket podman"; docker_pkg="podman-docker";\
|
||||||
|
if [ -n "$(sudo docker --version 2>&1 | grep /etc/containers/nodocker)" ]; then sudo touch /etc/containers/nodocker; fi;\
|
||||||
fi;\
|
fi;\
|
||||||
if [ "$(cat /sys/module/apparmor/parameters/enabled 2>/dev/null)" = "Y" ]; then \
|
if [ "$(cat /sys/module/apparmor/parameters/enabled 2>/dev/null)" = "Y" ]; then \
|
||||||
if ! command -v apparmor_parser > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst apparmor; fi;\
|
if ! command -v apparmor_parser > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst apparmor; fi;\
|
||||||
fi;\
|
fi;\
|
||||||
if [ "$(systemctl is-active docker)" != "active" ]; then \
|
if [ "$(systemctl is-active $check_srv | head -n1)" != "active" ]; then \
|
||||||
sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\
|
sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\
|
||||||
sleep 5; sudo systemctl start docker; sleep 5;\
|
sleep 5; sudo systemctl start $check_srv; sleep 5;\
|
||||||
|
if [ "$(systemctl is-active $check_srv | head -n1)" != "active" ]; then echo "Failed docker status"; fi;\
|
||||||
fi;\
|
fi;\
|
||||||
if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install sudo, command not found"; exit 1; fi;\
|
sudo docker --version
|
||||||
docker --version
|
|
||||||
|
# To allow autoinstallation of podman-docker, remove ' || [ -n "$(sudo $pm $wh_pkg $docker_pkg 2>/dev/null | grep podman-docker)" ]' and ' || [ -n "$(sudo docker --version 2>&1 | grep podman)" ]'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
CUR_USER=$(whoami);\
|
CUR_USER=$(whoami 2>/dev/null || echo ~ | sed 's/.*\///');\
|
||||||
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 \
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
sudo docker ps -a | grep amnezia | awk '{print $1}' | xargs sudo docker stop;\
|
sudo docker ps -a | grep amnezia | awk '{print $1}' | xargs sudo docker stop;\
|
||||||
|
sudo docker --version 2>/dev/null | grep -q podman && \
|
||||||
|
sudo systemctl list-units | grep amnezia | awk '{print $1}' | xargs sudo systemctl disable --now && \
|
||||||
|
sudo systemctl daemon-reload && sudo systemctl reset-failed && \
|
||||||
|
sudo sed -i '/^ # Amnezia start/,/^ # Amnezia finish$/d' /var/cache/containers/short-name-aliases.conf;\
|
||||||
sudo docker ps -a | grep amnezia | awk '{print $1}' | xargs sudo docker rm -fv;\
|
sudo docker ps -a | grep amnezia | awk '{print $1}' | xargs sudo docker rm -fv;\
|
||||||
sudo docker images -a | grep amnezia | awk '{print $3}' | xargs sudo docker rmi;\
|
sudo docker images -a | grep amnezia | awk '{print $3}' | xargs sudo docker rmi;\
|
||||||
sudo docker network ls | grep amnezia-dns-net | awk '{print $1}' | xargs sudo docker network rm;\
|
sudo docker network ls | grep amnezia-dns-net | awk '{print $1}' | xargs sudo docker network rm;\
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
sudo docker stop $CONTAINER_NAME;\
|
sudo docker stop $CONTAINER_NAME;\
|
||||||
|
sudo docker --version 2>/dev/null | grep -q podman && \
|
||||||
|
sudo systemctl disable --now container-$CONTAINER_NAME.service && \
|
||||||
|
sudo systemctl daemon-reload && sudo systemctl reset-failed && \
|
||||||
|
sudo rm -f $DOCKERFILE_FOLDER/container-$CONTAINER_NAME.service;\
|
||||||
sudo docker rm -fv $CONTAINER_NAME;\
|
sudo docker rm -fv $CONTAINER_NAME;\
|
||||||
sudo docker rmi $CONTAINER_NAME
|
sudo docker rmi $CONTAINER_NAME
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ sudo docker network connect amnezia-dns-net $CONTAINER_NAME
|
|||||||
# Create tun device if not exist
|
# Create tun device if not exist
|
||||||
sudo docker exec -i $CONTAINER_NAME bash -c 'mkdir -p /dev/net; if [ ! -c /dev/net/tun ]; then mknod /dev/net/tun c 10 200; fi'
|
sudo docker exec -i $CONTAINER_NAME bash -c 'mkdir -p /dev/net; if [ ! -c /dev/net/tun ]; then mknod /dev/net/tun c 10 200; fi'
|
||||||
|
|
||||||
|
# Create service for podman
|
||||||
|
if [ -n "$(sudo docker --version 2>/dev/null | grep podman)" ]; then \
|
||||||
|
sudo sh -c "podman generate systemd --new --name $CONTAINER_NAME 2>/dev/null > $DOCKERFILE_FOLDER/container-$CONTAINER_NAME.service";\
|
||||||
|
sudo mv $DOCKERFILE_FOLDER/container-$CONTAINER_NAME.service /etc/systemd/system/
|
||||||
|
sudo systemctl daemon-reload && sudo systemctl enable --now container-$CONTAINER_NAME.service;\
|
||||||
|
fi
|
||||||
|
|
||||||
# Prevent to route packets outside of the container in case if server behind of the NAT
|
# Prevent to route packets outside of the container in case if server behind of the NAT
|
||||||
#sudo docker exec -i $CONTAINER_NAME sh -c "ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up"
|
#sudo docker exec -i $CONTAINER_NAME sh -c "ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up"
|
||||||
|
|
||||||
|
|||||||
@@ -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 does not have permission to use sudo</source>
|
<source>The user is not a member of the sudo group</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: Packet manager error</source>
|
<source>Server error: Package manager error</source>
|
||||||
<translation>خطأ في الخادم: خطأ في مدير الحزم</translation>
|
<translation>خطأ في الخادم: خطأ في مدير الحزم</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
|
|||||||
@@ -3468,8 +3468,8 @@ It's okay as long as it'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 does not have permission to use sudo</source>
|
<source>The user is not a member of the sudo group</source>
|
||||||
<translation>The user does not have permission to use sudo</translation>
|
<translation>کاربر عضو گروه 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's okay as long as it'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: Packet manager error</source>
|
<source>Server error: Package manager error</source>
|
||||||
<translation>Server error: Packet manager error</translation>
|
<translation>خطای سرور: خطای مدیر بسته</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="34"/>
|
<location filename="../core/errorstrings.cpp" line="34"/>
|
||||||
|
|||||||
@@ -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 does not have permission to use sudo</source>
|
<source>The user is not a member of the sudo group</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: Packet manager error</source>
|
<source>Server error: Package 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"/>
|
||||||
|
|||||||
@@ -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 does not have permission to use sudo</source>
|
<source>The user is not a member of the sudo group</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: Packet manager error</source>
|
<source>Server error: Package manager error</source>
|
||||||
<translation>ဆာဗာ မှားယွင်းမှု: Packet Manager မှားယွင်းမှု</translation>
|
<translation>ဆာဗာ အမှား- Package manager အမှား</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="34"/>
|
<location filename="../core/errorstrings.cpp" line="34"/>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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 does not have permission to use sudo</source>
|
<source>The user is not a member of the sudo group</source>
|
||||||
<translation>The user does not have permission to use 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: Packet manager error</source>
|
<source>Server error: Package manager error</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Помилка сервера: Помилка менеджера пакетів</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="26"/>
|
<location filename="../core/errorstrings.cpp" line="26"/>
|
||||||
|
|||||||
@@ -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 does not have permission to use sudo</source>
|
<source>The user is not a member of the sudo group</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: Packet manager error</source>
|
<source>Server error: Package manager error</source>
|
||||||
<translation>سرور خطا: پیکیج منیجر خطا</translation>
|
<translation>سرور خطا: پیکیج منیجر خطا</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
|
|||||||
@@ -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 does not have permission to use sudo</source>
|
<source>The user is not a member of the sudo group</source>
|
||||||
<translation>用户没有root权限</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: Packet manager error</source>
|
<source>Server error: Package manager error</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>服务器错误:包管理器错误</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../core/errorstrings.cpp" line="26"/>
|
<location filename="../core/errorstrings.cpp" line="26"/>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "apiConfigsController.h"
|
#include "apiConfigsController.h"
|
||||||
|
|
||||||
#include <QEventLoop>
|
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
|
#include <QEventLoop>
|
||||||
|
|
||||||
#include "amnezia_application.h"
|
#include "amnezia_application.h"
|
||||||
#include "configurators/wireguard_configurator.h"
|
#include "configurators/wireguard_configurator.h"
|
||||||
@@ -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) {
|
||||||
@@ -354,6 +355,43 @@ bool ApiConfigsController::deactivateDevice()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode)
|
||||||
|
{
|
||||||
|
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
||||||
|
|
||||||
|
auto serverIndex = m_serversModel->getProcessedServerIndex();
|
||||||
|
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
|
||||||
|
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
|
||||||
|
|
||||||
|
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
|
||||||
|
ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
|
||||||
|
|
||||||
|
QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
|
||||||
|
apiPayload[configKey::userCountryCode] = apiConfigObject.value(configKey::userCountryCode);
|
||||||
|
apiPayload[configKey::serverCountryCode] = serverCountryCode;
|
||||||
|
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
|
||||||
|
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
|
||||||
|
apiPayload[configKey::uuid] = uuid;
|
||||||
|
|
||||||
|
QByteArray responseBody;
|
||||||
|
ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody);
|
||||||
|
if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) {
|
||||||
|
emit errorOccurred(errorCode);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uuid == m_settings->getInstallationUuid(true)) {
|
||||||
|
serverConfigObject.remove(config_key::containers);
|
||||||
|
m_serversModel->editServer(serverConfigObject, serverIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool ApiConfigsController::isConfigValid()
|
bool ApiConfigsController::isConfigValid()
|
||||||
{
|
{
|
||||||
int serverIndex = m_serversModel->getDefaultServerIndex();
|
int serverIndex = m_serversModel->getDefaultServerIndex();
|
||||||
@@ -369,7 +407,7 @@ bool ApiConfigsController::isConfigValid()
|
|||||||
return updateServiceFromGateway(serverIndex, "", "");
|
return updateServiceFromGateway(serverIndex, "", "");
|
||||||
} else if (configSource && m_serversModel->isApiKeyExpired(serverIndex)) {
|
} else if (configSource && m_serversModel->isApiKeyExpired(serverIndex)) {
|
||||||
qDebug() << "attempt to update api config by expires_at event";
|
qDebug() << "attempt to update api config by expires_at event";
|
||||||
if (configSource == apiDefs::ConfigSource::Telegram) {
|
if (configSource == apiDefs::ConfigSource::AmneziaGateway) {
|
||||||
return updateServiceFromGateway(serverIndex, "", "");
|
return updateServiceFromGateway(serverIndex, "", "");
|
||||||
} else {
|
} else {
|
||||||
m_serversModel->removeApiConfig(serverIndex);
|
m_serversModel->removeApiConfig(serverIndex);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public slots:
|
|||||||
bool reloadServiceConfig = false);
|
bool reloadServiceConfig = false);
|
||||||
bool updateServiceFromTelegram(const int serverIndex);
|
bool updateServiceFromTelegram(const int serverIndex);
|
||||||
bool deactivateDevice();
|
bool deactivateDevice();
|
||||||
|
bool deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode);
|
||||||
|
|
||||||
bool isConfigValid();
|
bool isConfigValid();
|
||||||
|
|
||||||
|
|||||||
@@ -25,11 +25,13 @@ namespace
|
|||||||
ApiSettingsController::ApiSettingsController(const QSharedPointer<ServersModel> &serversModel,
|
ApiSettingsController::ApiSettingsController(const QSharedPointer<ServersModel> &serversModel,
|
||||||
const QSharedPointer<ApiAccountInfoModel> &apiAccountInfoModel,
|
const QSharedPointer<ApiAccountInfoModel> &apiAccountInfoModel,
|
||||||
const QSharedPointer<ApiCountryModel> &apiCountryModel,
|
const QSharedPointer<ApiCountryModel> &apiCountryModel,
|
||||||
|
const QSharedPointer<ApiDevicesModel> &apiDevicesModel,
|
||||||
const std::shared_ptr<Settings> &settings, QObject *parent)
|
const std::shared_ptr<Settings> &settings, QObject *parent)
|
||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
m_serversModel(serversModel),
|
m_serversModel(serversModel),
|
||||||
m_apiAccountInfoModel(apiAccountInfoModel),
|
m_apiAccountInfoModel(apiAccountInfoModel),
|
||||||
m_apiCountryModel(apiCountryModel),
|
m_apiCountryModel(apiCountryModel),
|
||||||
|
m_apiDevicesModel(apiDevicesModel),
|
||||||
m_settings(settings)
|
m_settings(settings)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -73,6 +75,7 @@ bool ApiSettingsController::getAccountInfo(bool reload)
|
|||||||
|
|
||||||
if (reload) {
|
if (reload) {
|
||||||
updateApiCountryModel();
|
updateApiCountryModel();
|
||||||
|
updateApiDevicesModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -83,3 +86,8 @@ void ApiSettingsController::updateApiCountryModel()
|
|||||||
m_apiCountryModel->updateModel(m_apiAccountInfoModel->getAvailableCountries(), "");
|
m_apiCountryModel->updateModel(m_apiAccountInfoModel->getAvailableCountries(), "");
|
||||||
m_apiCountryModel->updateIssuedConfigsInfo(m_apiAccountInfoModel->getIssuedConfigsInfo());
|
m_apiCountryModel->updateIssuedConfigsInfo(m_apiAccountInfoModel->getIssuedConfigsInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApiSettingsController::updateApiDevicesModel()
|
||||||
|
{
|
||||||
|
m_apiDevicesModel->updateModel(m_apiAccountInfoModel->getIssuedConfigsInfo());
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "ui/models/api/apiAccountInfoModel.h"
|
#include "ui/models/api/apiAccountInfoModel.h"
|
||||||
#include "ui/models/api/apiCountryModel.h"
|
#include "ui/models/api/apiCountryModel.h"
|
||||||
|
#include "ui/models/api/apiDevicesModel.h"
|
||||||
#include "ui/models/servers_model.h"
|
#include "ui/models/servers_model.h"
|
||||||
|
|
||||||
class ApiSettingsController : public QObject
|
class ApiSettingsController : public QObject
|
||||||
@@ -12,13 +13,14 @@ class ApiSettingsController : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ApiSettingsController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ApiAccountInfoModel> &apiAccountInfoModel,
|
ApiSettingsController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ApiAccountInfoModel> &apiAccountInfoModel,
|
||||||
const QSharedPointer<ApiCountryModel> &apiCountryModel, const std::shared_ptr<Settings> &settings,
|
const QSharedPointer<ApiCountryModel> &apiCountryModel, const QSharedPointer<ApiDevicesModel> &apiDevicesModel,
|
||||||
QObject *parent = nullptr);
|
const std::shared_ptr<Settings> &settings, QObject *parent = nullptr);
|
||||||
~ApiSettingsController();
|
~ApiSettingsController();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
bool getAccountInfo(bool reload);
|
bool getAccountInfo(bool reload);
|
||||||
void updateApiCountryModel();
|
void updateApiCountryModel();
|
||||||
|
void updateApiDevicesModel();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void errorOccurred(ErrorCode errorCode);
|
void errorOccurred(ErrorCode errorCode);
|
||||||
@@ -27,6 +29,7 @@ private:
|
|||||||
QSharedPointer<ServersModel> m_serversModel;
|
QSharedPointer<ServersModel> m_serversModel;
|
||||||
QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel;
|
QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel;
|
||||||
QSharedPointer<ApiCountryModel> m_apiCountryModel;
|
QSharedPointer<ApiCountryModel> m_apiCountryModel;
|
||||||
|
QSharedPointer<ApiDevicesModel> m_apiDevicesModel;
|
||||||
|
|
||||||
std::shared_ptr<Settings> m_settings;
|
std::shared_ptr<Settings> m_settings;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ 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";
|
||||||
|
|
||||||
@@ -53,14 +51,13 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -345,7 +342,7 @@ QJsonObject ImportController::extractOpenVpnConfig(const QString &data)
|
|||||||
arr.push_back(containers);
|
arr.push_back(containers);
|
||||||
|
|
||||||
QString hostName;
|
QString hostName;
|
||||||
const static QRegularExpression hostNameRegExp("remote (.*) [0-9]*");
|
const static QRegularExpression hostNameRegExp("remote\\s+([^\\s]+)");
|
||||||
QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data);
|
QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data);
|
||||||
if (hostNameMatch.hasMatch()) {
|
if (hostNameMatch.hasMatch()) {
|
||||||
hostName = hostNameMatch.captured(1);
|
hostName = hostNameMatch.captured(1);
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ namespace PageLoader
|
|||||||
PageSettingsApiSupport,
|
PageSettingsApiSupport,
|
||||||
PageSettingsApiInstructions,
|
PageSettingsApiInstructions,
|
||||||
PageSettingsApiNativeConfigs,
|
PageSettingsApiNativeConfigs,
|
||||||
|
PageSettingsApiDevices,
|
||||||
|
|
||||||
PageServiceSftpSettings,
|
PageServiceSftpSettings,
|
||||||
PageServiceTorWebsiteSettings,
|
PageServiceTorWebsiteSettings,
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ 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) {
|
||||||
@@ -75,7 +74,6 @@ 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));
|
||||||
}
|
}
|
||||||
@@ -124,7 +122,6 @@ 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"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
|
|||||||
}
|
}
|
||||||
case ServiceDescriptionRole: {
|
case ServiceDescriptionRole: {
|
||||||
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2) {
|
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2) {
|
||||||
return tr("Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to 200 "
|
return tr("Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. "
|
||||||
"Mb/s");
|
"Speeds up to 200 Mbps");
|
||||||
} else if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) {
|
} else if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) {
|
||||||
return tr("Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and "
|
return tr("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.");
|
"more. YouTube is not included in the free plan.");
|
||||||
@@ -58,6 +58,19 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
|
|||||||
case IsComponentVisibleRole: {
|
case IsComponentVisibleRole: {
|
||||||
return m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2;
|
return m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2;
|
||||||
}
|
}
|
||||||
|
case HasExpiredWorkerRole: {
|
||||||
|
for (int i = 0; i < m_issuedConfigsInfo.size(); i++) {
|
||||||
|
QJsonObject issuedConfigObject = m_issuedConfigsInfo.at(i).toObject();
|
||||||
|
|
||||||
|
auto lastDownloaded = QDateTime::fromString(issuedConfigObject.value(apiDefs::key::lastDownloaded).toString());
|
||||||
|
auto workerLastUpdated = QDateTime::fromString(issuedConfigObject.value(apiDefs::key::workerLastUpdated).toString());
|
||||||
|
|
||||||
|
if (lastDownloaded < workerLastUpdated) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
@@ -124,6 +137,7 @@ QHash<int, QByteArray> ApiAccountInfoModel::roleNames() const
|
|||||||
roles[ConnectedDevicesRole] = "connectedDevices";
|
roles[ConnectedDevicesRole] = "connectedDevices";
|
||||||
roles[ServiceDescriptionRole] = "serviceDescription";
|
roles[ServiceDescriptionRole] = "serviceDescription";
|
||||||
roles[IsComponentVisibleRole] = "isComponentVisible";
|
roles[IsComponentVisibleRole] = "isComponentVisible";
|
||||||
|
roles[HasExpiredWorkerRole] = "hasExpiredWorker";
|
||||||
|
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ public:
|
|||||||
ConnectedDevicesRole,
|
ConnectedDevicesRole,
|
||||||
ServiceDescriptionRole,
|
ServiceDescriptionRole,
|
||||||
EndDateRole,
|
EndDateRole,
|
||||||
IsComponentVisibleRole
|
IsComponentVisibleRole,
|
||||||
|
HasExpiredWorkerRole
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit ApiAccountInfoModel(QObject *parent = nullptr);
|
explicit ApiAccountInfoModel(QObject *parent = nullptr);
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ QVariant ApiCountryModel::data(const QModelIndex &index, int role) const
|
|||||||
case IsIssuedRole: {
|
case IsIssuedRole: {
|
||||||
return isIssued;
|
return isIssued;
|
||||||
}
|
}
|
||||||
|
case IsWorkerExpiredRole: {
|
||||||
|
return issuedConfigInfo.lastDownloaded < issuedConfigInfo.workerLastUpdated;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
@@ -114,5 +117,6 @@ QHash<int, QByteArray> ApiCountryModel::roleNames() const
|
|||||||
roles[CountryCodeRole] = "countryCode";
|
roles[CountryCodeRole] = "countryCode";
|
||||||
roles[CountryImageCodeRole] = "countryImageCode";
|
roles[CountryImageCodeRole] = "countryImageCode";
|
||||||
roles[IsIssuedRole] = "isIssued";
|
roles[IsIssuedRole] = "isIssued";
|
||||||
|
roles[IsWorkerExpiredRole] = "isWorkerExpired";
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ public:
|
|||||||
CountryNameRole = Qt::UserRole + 1,
|
CountryNameRole = Qt::UserRole + 1,
|
||||||
CountryCodeRole,
|
CountryCodeRole,
|
||||||
CountryImageCodeRole,
|
CountryImageCodeRole,
|
||||||
IsIssuedRole
|
IsIssuedRole,
|
||||||
|
IsWorkerExpiredRole
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit ApiCountryModel(QObject *parent = nullptr);
|
explicit ApiCountryModel(QObject *parent = nullptr);
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
#include "apiDevicesModel.h"
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
#include "core/api/apiDefs.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
Logger logger("ApiDevicesModel");
|
||||||
|
|
||||||
|
constexpr QLatin1String gatewayAccount("gateway_account");
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiDevicesModel::ApiDevicesModel(std::shared_ptr<Settings> settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int ApiDevicesModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent)
|
||||||
|
return m_issuedConfigs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ApiDevicesModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(rowCount()))
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
IssuedConfigInfo issuedConfigInfo = m_issuedConfigs.at(index.row());
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case OsVersionRole: {
|
||||||
|
return issuedConfigInfo.osVersion;
|
||||||
|
}
|
||||||
|
case SupportTagRole: {
|
||||||
|
return issuedConfigInfo.installationUuid;
|
||||||
|
}
|
||||||
|
case CountryCodeRole: {
|
||||||
|
return issuedConfigInfo.countryCode;
|
||||||
|
}
|
||||||
|
case LastUpdateRole: {
|
||||||
|
return QDateTime::fromString(issuedConfigInfo.lastDownloaded, Qt::ISODate).toLocalTime().toString("d MMM yyyy");
|
||||||
|
}
|
||||||
|
case IsCurrentDeviceRole: {
|
||||||
|
return issuedConfigInfo.installationUuid == m_settings->getInstallationUuid(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiDevicesModel::updateModel(const QJsonArray &issuedConfigs)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
|
||||||
|
m_issuedConfigs.clear();
|
||||||
|
for (int i = 0; i < issuedConfigs.size(); i++) {
|
||||||
|
IssuedConfigInfo issuedConfigInfo;
|
||||||
|
QJsonObject issuedConfigObject = issuedConfigs.at(i).toObject();
|
||||||
|
|
||||||
|
if (issuedConfigObject.value(apiDefs::key::sourceType).toString() != gatewayAccount) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
issuedConfigInfo.installationUuid = issuedConfigObject.value(apiDefs::key::installationUuid).toString();
|
||||||
|
issuedConfigInfo.workerLastUpdated = issuedConfigObject.value(apiDefs::key::workerLastUpdated).toString();
|
||||||
|
issuedConfigInfo.lastDownloaded = issuedConfigObject.value(apiDefs::key::lastDownloaded).toString();
|
||||||
|
issuedConfigInfo.sourceType = issuedConfigObject.value(apiDefs::key::sourceType).toString();
|
||||||
|
issuedConfigInfo.osVersion = issuedConfigObject.value(apiDefs::key::osVersion).toString();
|
||||||
|
|
||||||
|
issuedConfigInfo.countryName = issuedConfigObject.value(apiDefs::key::serverCountryName).toString();
|
||||||
|
issuedConfigInfo.countryCode = issuedConfigObject.value(apiDefs::key::serverCountryCode).toString();
|
||||||
|
|
||||||
|
m_issuedConfigs.push_back(issuedConfigInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> ApiDevicesModel::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> roles;
|
||||||
|
roles[OsVersionRole] = "osVersion";
|
||||||
|
roles[SupportTagRole] = "supportTag";
|
||||||
|
roles[CountryCodeRole] = "countryCode";
|
||||||
|
roles[LastUpdateRole] = "lastUpdate";
|
||||||
|
roles[IsCurrentDeviceRole] = "isCurrentDevice";
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
#ifndef APIDEVICESMODEL_H
|
||||||
|
#define APIDEVICESMODEL_H
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
class ApiDevicesModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Roles {
|
||||||
|
OsVersionRole = Qt::UserRole + 1,
|
||||||
|
SupportTagRole,
|
||||||
|
CountryCodeRole,
|
||||||
|
LastUpdateRole,
|
||||||
|
IsCurrentDeviceRole
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit ApiDevicesModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void updateModel(const QJsonArray &issuedConfigs);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct IssuedConfigInfo
|
||||||
|
{
|
||||||
|
QString installationUuid;
|
||||||
|
QString workerLastUpdated;
|
||||||
|
QString lastDownloaded;
|
||||||
|
QString sourceType;
|
||||||
|
QString osVersion;
|
||||||
|
|
||||||
|
QString countryName;
|
||||||
|
QString countryCode;
|
||||||
|
};
|
||||||
|
|
||||||
|
QVector<IssuedConfigInfo> m_issuedConfigs;
|
||||||
|
|
||||||
|
std::shared_ptr<Settings> m_settings;
|
||||||
|
};
|
||||||
|
#endif // APIDEVICESMODEL_H
|
||||||
@@ -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 VPN for comfortable work, downloading large files and watching videos in 8K resolution. "
|
return tr("Amnezia Premium is classic VPN for seamless work, downloading large files, and watching videos. "
|
||||||
"Works for any sites with no restrictions. Speed up to %1 MBit/s. Unlimited traffic.")
|
"Access all websites and online resources. Speeds up to %1 Mbps.")
|
||||||
.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 VPN for comfortable work, downloading large files and watching videos in 8K resolution. "
|
return tr("Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. "
|
||||||
"Works for any sites with no restrictions.");
|
"Access all websites and online resources.");
|
||||||
} 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.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ Rectangle {
|
|||||||
Layout.rightMargin: 10
|
Layout.rightMargin: 10
|
||||||
Layout.leftMargin: 10
|
Layout.leftMargin: 10
|
||||||
|
|
||||||
text: qsTr("Amnezia Premium - for access to any website")
|
text: qsTr("Amnezia Premium - for access to all websites and online resources")
|
||||||
color: AmneziaStyle.color.pearlGray
|
color: AmneziaStyle.color.pearlGray
|
||||||
|
|
||||||
lineHeight: 18
|
lineHeight: 18
|
||||||
|
|||||||
@@ -27,5 +27,6 @@ QtObject {
|
|||||||
readonly property color mistyGray: Qt.rgba(215/255, 216/255, 219/255, 0.8)
|
readonly property color mistyGray: Qt.rgba(215/255, 216/255, 219/255, 0.8)
|
||||||
readonly property color cloudyGray: Qt.rgba(215/255, 216/255, 219/255, 0.65)
|
readonly property color cloudyGray: Qt.rgba(215/255, 216/255, 219/255, 0.65)
|
||||||
readonly property color pearlGray: '#EAEAEC'
|
readonly property color pearlGray: '#EAEAEC'
|
||||||
|
readonly property color translucentRichBrown: Qt.rgba(99/255, 51/255, 3/255, 0.26)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ PageType {
|
|||||||
actionButtonImage: "qrc:/images/controls/settings.svg"
|
actionButtonImage: "qrc:/images/controls/settings.svg"
|
||||||
|
|
||||||
headerText: root.processedServer.name
|
headerText: root.processedServer.name
|
||||||
descriptionText: qsTr("Locations for connection")
|
descriptionText: qsTr("Location for connection")
|
||||||
|
|
||||||
actionButtonFunction: function() {
|
actionButtonFunction: function() {
|
||||||
PageController.showBusyIndicator(true)
|
PageController.showBusyIndicator(true)
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Dialogs
|
||||||
|
|
||||||
|
import QtCore
|
||||||
|
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
import PageEnum 1.0
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "./"
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
import "../Config"
|
||||||
|
import "../Components"
|
||||||
|
|
||||||
|
PageType {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
ListViewType {
|
||||||
|
id: listView
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.topMargin: 20
|
||||||
|
anchors.bottomMargin: 24
|
||||||
|
|
||||||
|
model: ApiDevicesModel
|
||||||
|
|
||||||
|
header: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
|
BackButtonType {
|
||||||
|
id: backButton
|
||||||
|
}
|
||||||
|
|
||||||
|
HeaderType {
|
||||||
|
id: header
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
|
headerText: qsTr("Active Devices")
|
||||||
|
descriptionText: qsTr("Manage currently connected devices")
|
||||||
|
}
|
||||||
|
|
||||||
|
WarningType {
|
||||||
|
Layout.topMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
textString: qsTr("You can find the identifier on the Support tab or, for older versions of the app, "
|
||||||
|
+ "by tapping '+' and then the three dots at the top of the page.")
|
||||||
|
|
||||||
|
iconPath: "qrc:/images/controls/alert-circle.svg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 6
|
||||||
|
|
||||||
|
text: osVersion + (isCurrentDevice ? qsTr(" (current device)") : "")
|
||||||
|
descriptionText: qsTr("Support tag: ") + "\n" + supportTag + "\n" + qsTr("Last updated: ") + lastUpdate
|
||||||
|
rightImageSource: "qrc:/images/controls/trash.svg"
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
if (isCurrentDevice && ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
|
||||||
|
PageController.showNotificationMessage(qsTr("Cannot unlink device during active connection"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var headerText = qsTr("Are you sure you want to unlink this device?")
|
||||||
|
var descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect.")
|
||||||
|
var yesButtonText = qsTr("Continue")
|
||||||
|
var noButtonText = qsTr("Cancel")
|
||||||
|
|
||||||
|
var yesButtonFunction = function() {
|
||||||
|
Qt.callLater(deactivateExternalDevice, supportTag, countryCode)
|
||||||
|
}
|
||||||
|
var noButtonFunction = function() {
|
||||||
|
}
|
||||||
|
|
||||||
|
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deactivateExternalDevice(supportTag, countryCode) {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
if (ApiConfigsController.deactivateExternalDevice(supportTag, countryCode)) {
|
||||||
|
ApiSettingsController.getAccountInfo(true)
|
||||||
|
}
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -99,7 +99,7 @@ PageType {
|
|||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
headerText: qsTr("How to connect on another device")
|
headerText: qsTr("How to connect on another device")
|
||||||
descriptionText: qsTr("Instructions on the Amnezia website")
|
descriptionText: qsTr("Setup guides on the Amnezia website")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +107,6 @@ PageType {
|
|||||||
width: listView.width
|
width: listView.width
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
id: telegramButton
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 6
|
Layout.topMargin: 6
|
||||||
|
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ PageType {
|
|||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
headerText: qsTr("Configuration files")
|
headerText: qsTr("Configuration Files")
|
||||||
descriptionText: qsTr("To connect a router or AmneziaWG application")
|
descriptionText: qsTr("For router setup or the AmneziaWG app")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +58,9 @@ PageType {
|
|||||||
Layout.topMargin: 6
|
Layout.topMargin: 6
|
||||||
|
|
||||||
text: countryName
|
text: countryName
|
||||||
|
descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : ""
|
||||||
|
descriptionColor: AmneziaStyle.color.vibrantRed
|
||||||
|
|
||||||
leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
|
leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
|
||||||
rightImageSource: isIssued ? "qrc:/images/controls/more-vertical.svg" : "qrc:/images/controls/download.svg"
|
rightImageSource: isIssued ? "qrc:/images/controls/more-vertical.svg" : "qrc:/images/controls/download.svg"
|
||||||
|
|
||||||
@@ -120,13 +123,13 @@ PageType {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.margins: 16
|
Layout.margins: 16
|
||||||
|
|
||||||
headerText: qsTr("Configuration file ") + moreOptionsDrawer.countryName
|
headerText: moreOptionsDrawer.countryName + qsTr(" configuration file")
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
text: qsTr("Create a new")
|
text: qsTr("Generate a new configuration file")
|
||||||
descriptionText: qsTr("The previously created one will stop working")
|
descriptionText: qsTr("The previously created one will stop working")
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
@@ -190,9 +193,15 @@ PageType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showQuestion(isConfigIssue, countryCode, countryName) {
|
function showQuestion(isConfigIssue, countryCode, countryName) {
|
||||||
var headerText = qsTr("Revoke the actual %1 configuration file?").arg(countryName)
|
var headerText
|
||||||
var descriptionText = qsTr("The previously created file will no longer be valid. It will not be possible to connect using it.")
|
if (isConfigIssue) {
|
||||||
var yesButtonText = qsTr("Continue")
|
headerText = qsTr("Generate a new %1 configuration file?").arg(countryName)
|
||||||
|
} else {
|
||||||
|
headerText = qsTr("Revoke the current %1 configuration file?").arg(countryName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var descriptionText = qsTr("Your previous configuration file will no longer work, and it will not be possible to connect using it")
|
||||||
|
var yesButtonText = isConfigIssue ? qsTr("Download") : qsTr("Continue")
|
||||||
var noButtonText = qsTr("Cancel")
|
var noButtonText = qsTr("Cancel")
|
||||||
|
|
||||||
var yesButtonFunction = function() {
|
var yesButtonFunction = function() {
|
||||||
|
|||||||
@@ -26,15 +26,15 @@ 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/map-pin.svg"
|
readonly property string objectImageSource: "qrc:/images/controls/info.svg"
|
||||||
}
|
}
|
||||||
|
|
||||||
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,9 +42,9 @@ PageType {
|
|||||||
QtObject {
|
QtObject {
|
||||||
id: deviceCountObject
|
id: deviceCountObject
|
||||||
|
|
||||||
readonly property string title: qsTr("Connected devices")
|
readonly property string title: qsTr("Active Connections")
|
||||||
readonly property string contentKey: "connectedDevices"
|
readonly property string contentKey: "connectedDevices"
|
||||||
readonly property string objectImageSource: "qrc:/images/controls/gauge.svg"
|
readonly property string objectImageSource: "qrc:/images/controls/monitor.svg"
|
||||||
}
|
}
|
||||||
|
|
||||||
property var processedServer
|
property var processedServer
|
||||||
@@ -158,15 +158,32 @@ PageType {
|
|||||||
|
|
||||||
readonly property bool isVisibleForAmneziaFree: ApiAccountInfoModel.data("isComponentVisible")
|
readonly property bool isVisibleForAmneziaFree: ApiAccountInfoModel.data("isComponentVisible")
|
||||||
|
|
||||||
|
WarningType {
|
||||||
|
id: warning
|
||||||
|
|
||||||
|
Layout.topMargin: 32
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
backGroundColor: AmneziaStyle.color.translucentRichBrown
|
||||||
|
|
||||||
|
textString: qsTr("Configurations have been updated for some countries. Download and install the updated configuration files")
|
||||||
|
|
||||||
|
iconPath: "qrc:/images/controls/alert-circle.svg"
|
||||||
|
|
||||||
|
visible: ApiAccountInfoModel.data("hasExpiredWorker")
|
||||||
|
}
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
id: vpnKey
|
id: vpnKey
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 32
|
Layout.topMargin: warning.visible ? 16 : 32
|
||||||
|
|
||||||
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() {
|
||||||
@@ -174,7 +191,7 @@ PageType {
|
|||||||
|
|
||||||
shareConnectionDrawer.openTriggered()
|
shareConnectionDrawer.openTriggered()
|
||||||
shareConnectionDrawer.isSelfHostedConfig = false;
|
shareConnectionDrawer.isSelfHostedConfig = false;
|
||||||
shareConnectionDrawer.shareButtonText = qsTr("Save VPN key to file")
|
shareConnectionDrawer.shareButtonText = qsTr("Save VPN key as a file")
|
||||||
shareConnectionDrawer.copyButtonText = qsTr("Copy VPN key")
|
shareConnectionDrawer.copyButtonText = qsTr("Copy VPN key")
|
||||||
|
|
||||||
|
|
||||||
@@ -192,13 +209,13 @@ PageType {
|
|||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 32
|
Layout.topMargin: warning.visible ? 16 : 32
|
||||||
|
|
||||||
visible: footer.isVisibleForAmneziaFree
|
visible: footer.isVisibleForAmneziaFree
|
||||||
|
|
||||||
text: qsTr("Configuration files")
|
text: qsTr("Configuration Files")
|
||||||
|
|
||||||
descriptionText: qsTr("To connect a router or AmneziaWG application")
|
descriptionText: qsTr("Manage configuration files")
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
@@ -211,6 +228,26 @@ PageType {
|
|||||||
visible: footer.isVisibleForAmneziaFree
|
visible: footer.isVisibleForAmneziaFree
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: footer.isVisibleForAmneziaFree
|
||||||
|
|
||||||
|
text: qsTr("Active Devices")
|
||||||
|
|
||||||
|
descriptionText: qsTr("Manage currently connected devices")
|
||||||
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
ApiSettingsController.updateApiDevicesModel()
|
||||||
|
PageController.goToPage(PageEnum.PageSettingsApiDevices)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {
|
||||||
|
visible: footer.isVisibleForAmneziaFree
|
||||||
|
}
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: footer.isVisibleForAmneziaFree ? 0 : 32
|
Layout.topMargin: footer.isVisibleForAmneziaFree ? 0 : 32
|
||||||
@@ -228,6 +265,8 @@ PageType {
|
|||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: footer.isVisibleForAmneziaFree
|
||||||
|
|
||||||
text: qsTr("How to connect on another device")
|
text: qsTr("How to connect on another device")
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
@@ -236,7 +275,9 @@ PageType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
DividerType {
|
||||||
|
visible: footer.isVisibleForAmneziaFree
|
||||||
|
}
|
||||||
|
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
id: resetButton
|
id: resetButton
|
||||||
@@ -288,16 +329,17 @@ PageType {
|
|||||||
pressedColor: AmneziaStyle.color.sheerWhite
|
pressedColor: AmneziaStyle.color.sheerWhite
|
||||||
textColor: AmneziaStyle.color.vibrantRed
|
textColor: AmneziaStyle.color.vibrantRed
|
||||||
|
|
||||||
text: qsTr("Deactivate the subscription on this device")
|
text: qsTr("Unlink this device")
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
var headerText = qsTr("Deactivate the subscription on this device?")
|
var headerText = qsTr("Are you sure you want to unlink this device?")
|
||||||
|
var descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect.")
|
||||||
var yesButtonText = qsTr("Continue")
|
var yesButtonText = qsTr("Continue")
|
||||||
var noButtonText = qsTr("Cancel")
|
var noButtonText = qsTr("Cancel")
|
||||||
|
|
||||||
var yesButtonFunction = function() {
|
var yesButtonFunction = function() {
|
||||||
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
|
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
|
||||||
PageController.showNotificationMessage(qsTr("The next time the “Connect” button is pressed, the device will be activated again"))
|
PageController.showNotificationMessage(qsTr("Cannot unlink device during active connection"))
|
||||||
} else {
|
} else {
|
||||||
PageController.showBusyIndicator(true)
|
PageController.showBusyIndicator(true)
|
||||||
if (ApiConfigsController.deactivateDevice()) {
|
if (ApiConfigsController.deactivateDevice()) {
|
||||||
@@ -309,7 +351,7 @@ PageType {
|
|||||||
var noButtonFunction = function() {
|
var noButtonFunction = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,20 +16,61 @@ import "../Components"
|
|||||||
PageType {
|
PageType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
ColumnLayout {
|
QtObject {
|
||||||
id: backButtonLayout
|
id: telegram
|
||||||
|
|
||||||
anchors.top: parent.top
|
readonly property string title: qsTr("Telegram")
|
||||||
anchors.left: parent.left
|
readonly property string description: "@" + ApiAccountInfoModel.getTelegramBotLink()
|
||||||
anchors.right: parent.right
|
readonly property string link: "https://t.me/" + ApiAccountInfoModel.getTelegramBotLink()
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: techSupport
|
||||||
|
|
||||||
|
readonly property string title: qsTr("Email")
|
||||||
|
readonly property string description: qsTr("support@amnezia.org")
|
||||||
|
readonly property string link: "mailto:support@amnezia.org"
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: paymentSupport
|
||||||
|
|
||||||
|
readonly property string title: qsTr("Email Billing & Orders")
|
||||||
|
readonly property string description: qsTr("help@vpnpay.io")
|
||||||
|
readonly property string link: "mailto:help@vpnpay.io"
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: site
|
||||||
|
|
||||||
|
readonly property string title: qsTr("Website")
|
||||||
|
readonly property string description: qsTr("amnezia.org")
|
||||||
|
readonly property string link: LanguageModel.getCurrentSiteUrl()
|
||||||
|
}
|
||||||
|
|
||||||
|
property list<QtObject> supportModel: [
|
||||||
|
telegram,
|
||||||
|
techSupport,
|
||||||
|
paymentSupport,
|
||||||
|
site
|
||||||
|
]
|
||||||
|
|
||||||
|
ListViewType {
|
||||||
|
id: listView
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
anchors.topMargin: 20
|
anchors.topMargin: 20
|
||||||
|
anchors.bottomMargin: 24
|
||||||
|
|
||||||
|
model: supportModel
|
||||||
|
|
||||||
|
header: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
BackButtonType {
|
BackButtonType {
|
||||||
id: backButton
|
id: backButton
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
HeaderType {
|
HeaderType {
|
||||||
id: header
|
id: header
|
||||||
|
|
||||||
@@ -38,52 +79,28 @@ PageType {
|
|||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
headerText: qsTr("Support")
|
headerText: qsTr("Support")
|
||||||
descriptionText: qsTr("Our technical support specialists are ready to help you at any time")
|
descriptionText: qsTr("Our technical support specialists are available to assist you at any time")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
text: title
|
||||||
readonly property string telegramBotLink: ApiAccountInfoModel.getTelegramBotLink()
|
descriptionText: description
|
||||||
|
|
||||||
text: qsTr("Telegram")
|
|
||||||
descriptionText: "@" + telegramBotLink
|
|
||||||
rightImageSource: "qrc:/images/controls/external-link.svg"
|
rightImageSource: "qrc:/images/controls/external-link.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
Qt.openUrlExternally("https://t.me/" + telegramBotLink)
|
Qt.openUrlExternally(link)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
DividerType {}
|
||||||
|
|
||||||
LabelWithButtonType {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: qsTr("Mail")
|
|
||||||
descriptionText: qsTr("support@amnezia.org")
|
|
||||||
rightImageSource: "qrc:/images/controls/external-link.svg"
|
|
||||||
|
|
||||||
clickedFunction: function() {
|
|
||||||
Qt.openUrlExternally(qsTr("mailto:support@amnezia.org"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
|
||||||
|
|
||||||
LabelWithButtonType {
|
footer: ColumnLayout {
|
||||||
Layout.fillWidth: true
|
width: listView.width
|
||||||
|
|
||||||
text: qsTr("Site")
|
|
||||||
descriptionText: qsTr("amnezia.org")
|
|
||||||
rightImageSource: "qrc:/images/controls/external-link.svg"
|
|
||||||
|
|
||||||
clickedFunction: function() {
|
|
||||||
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DividerType {}
|
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
id: supportUuid
|
id: supportUuid
|
||||||
@@ -107,3 +124,4 @@ PageType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user