Merge branch 'dev' into bugfix/split-tunneling-not-preserving-after-backup

This commit is contained in:
Mitternacht822 2025-06-05 17:37:54 +04:00 committed by GitHub
commit 193bcac1be
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 654 additions and 850 deletions

4
.gitignore vendored
View file

@ -134,3 +134,7 @@ out/
# CMake files # CMake files
CMakeFiles/ CMakeFiles/
ios-ne-build.sh
macos-ne-build.sh
macos-signed-build.sh

View file

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN) set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.8.6.0 project(${PROJECT} VERSION 4.8.7.2
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 2083) set(APP_ANDROID_VERSION_CODE 2086)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux") set(MZ_PLATFORM_NAME "linux")

View file

@ -76,8 +76,22 @@ set_target_properties(${PROJECT} PROPERTIES
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks" XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
XCODE_EMBED_APP_EXTENSIONS networkextension XCODE_EMBED_APP_EXTENSIONS networkextension
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
) )
if(DEFINED DEPLOY)
set_target_properties(${PROJECT} PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.AmneziaVPN"
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev ios.org.amnezia.AmneziaVPN"
)
else()
set_target_properties(${PROJECT} PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
)
endif()
set_target_properties(${PROJECT} PROPERTIES set_target_properties(${PROJECT} PROPERTIES
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0" XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES" XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"

View file

@ -31,6 +31,7 @@ namespace apiDefs
constexpr QLatin1String apiConfig("api_config"); constexpr QLatin1String apiConfig("api_config");
constexpr QLatin1String stackType("stack_type"); constexpr QLatin1String stackType("stack_type");
constexpr QLatin1String serviceType("service_type"); constexpr QLatin1String serviceType("service_type");
constexpr QLatin1String cliVersion("cli_version");
constexpr QLatin1String vpnKey("vpn_key"); constexpr QLatin1String vpnKey("vpn_key");
constexpr QLatin1String config("config"); constexpr QLatin1String config("config");

View file

@ -41,32 +41,34 @@ bool apiUtils::isServerFromApi(const QJsonObject &serverConfigObject)
apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObject) apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObject)
{ {
auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt(); auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt();
switch (configVersion) { switch (configVersion) {
case apiDefs::ConfigSource::Telegram: { case apiDefs::ConfigSource::Telegram: {
constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT);
constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT);
auto apiEndpoint = serverConfigObject.value(apiDefs::key::apiEndpoint).toString();
if (apiEndpoint.contains(premiumV1Endpoint)) {
return apiDefs::ConfigType::AmneziaPremiumV1;
} else if (apiEndpoint.contains(freeV2Endpoint)) {
return apiDefs::ConfigType::AmneziaFreeV2;
}
}; };
case apiDefs::ConfigSource::AmneziaGateway: { case apiDefs::ConfigSource::AmneziaGateway: {
constexpr QLatin1String servicePremium("amnezia-premium"); constexpr QLatin1String servicePremium("amnezia-premium");
constexpr QLatin1String serviceFree("amnezia-free"); constexpr QLatin1String serviceFree("amnezia-free");
constexpr QLatin1String serviceExternalPremium("external-premium"); constexpr QLatin1String serviceExternalPremium("external-premium");
constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT);
constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT);
auto apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject(); auto apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject();
auto serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString(); auto serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString();
auto apiEndpoint = serverConfigObject.value(apiDefs::key::apiEndpoint).toString();
if (serviceType == servicePremium) { if (serviceType == servicePremium) {
return apiDefs::ConfigType::AmneziaPremiumV2; return apiDefs::ConfigType::AmneziaPremiumV2;
} else if (serviceType == serviceFree) { } else if (serviceType == serviceFree) {
return apiDefs::ConfigType::AmneziaFreeV3; return apiDefs::ConfigType::AmneziaFreeV3;
} else if (serviceType == serviceExternalPremium) { } else if (serviceType == serviceExternalPremium) {
return apiDefs::ConfigType::ExternalPremium; return apiDefs::ConfigType::ExternalPremium;
} else if (apiEndpoint.contains(premiumV1Endpoint)) {
return apiDefs::ConfigType::AmneziaPremiumV1;
} else if (apiEndpoint.contains(freeV2Endpoint)) {
return apiDefs::ConfigType::AmneziaFreeV2;
} }
} }
default: { default: {
@ -94,6 +96,9 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) { || reply->error() == QNetworkReply::NetworkError::TimeoutError) {
qDebug() << reply->error(); qDebug() << reply->error();
return amnezia::ErrorCode::ApiConfigTimeoutError; return amnezia::ErrorCode::ApiConfigTimeoutError;
} else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) {
qDebug() << reply->error();
return amnezia::ErrorCode::ApiUpdateRequestError;
} else { } else {
QString err = reply->errorString(); QString err = reply->errorString();
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();

View file

@ -14,8 +14,8 @@
#include "amnezia_application.h" #include "amnezia_application.h"
#include "core/api/apiUtils.h" #include "core/api/apiUtils.h"
#include "utilities.h"
#include "core/networkUtilities.h" #include "core/networkUtilities.h"
#include "utilities.h"
#ifdef AMNEZIA_DESKTOP #ifdef AMNEZIA_DESKTOP
#include "core/ipcclient.h" #include "core/ipcclient.h"
@ -36,10 +36,17 @@ namespace
constexpr QLatin1String errorResponsePattern1("No active configuration found for"); constexpr QLatin1String errorResponsePattern1("No active configuration found for");
constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for"); constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for");
constexpr QLatin1String errorResponsePattern3("Account not found."); constexpr QLatin1String errorResponsePattern3("Account not found.");
constexpr QLatin1String updateRequestResponsePattern("client version update is required");
} }
GatewayController::GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent) GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
: QObject(parent), m_gatewayEndpoint(gatewayEndpoint), m_isDevEnvironment(isDevEnvironment), m_requestTimeoutMsecs(requestTimeoutMsecs) const bool isStrictKillSwitchEnabled, QObject *parent)
: QObject(parent),
m_gatewayEndpoint(gatewayEndpoint),
m_isDevEnvironment(isDevEnvironment),
m_requestTimeoutMsecs(requestTimeoutMsecs),
m_isStrictKillSwitchEnabled(isStrictKillSwitchEnabled)
{ {
} }
@ -58,11 +65,11 @@ ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBo
// bypass killSwitch exceptions for API-gateway // bypass killSwitch exceptions for API-gateway
#ifdef AMNEZIA_DESKTOP #ifdef AMNEZIA_DESKTOP
{ if (m_isStrictKillSwitchEnabled) {
QString host = QUrl(request.url()).host(); QString host = QUrl(request.url()).host();
QString ip = NetworkUtilities::getIPAddress(host); QString ip = NetworkUtilities::getIPAddress(host);
if (!ip.isEmpty()) { if (!ip.isEmpty()) {
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList{ip}); IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip });
} }
} }
#endif #endif
@ -120,11 +127,11 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
// bypass killSwitch exceptions for API-gateway // bypass killSwitch exceptions for API-gateway
#ifdef AMNEZIA_DESKTOP #ifdef AMNEZIA_DESKTOP
{ if (m_isStrictKillSwitchEnabled) {
QString host = QUrl(request.url()).host(); QString host = QUrl(request.url()).host();
QString ip = NetworkUtilities::getIPAddress(host); QString ip = NetworkUtilities::getIPAddress(host);
if (!ip.isEmpty()) { if (!ip.isEmpty()) {
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList{ip}); IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip });
} }
} }
#endif #endif
@ -306,6 +313,13 @@ bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray
qDebug() << reply->error(); qDebug() << reply->error();
return true; return true;
} }
} else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) {
if (responseBody.contains(updateRequestResponsePattern)) {
return false;
} else {
qDebug() << reply->error();
return true;
}
} else if (reply->error() != QNetworkReply::NetworkError::NoError) { } else if (reply->error() != QNetworkReply::NetworkError::NoError) {
qDebug() << reply->error(); qDebug() << reply->error();
return true; return true;

View file

@ -15,7 +15,8 @@ class GatewayController : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent = nullptr); explicit GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
const bool isStrictKillSwitchEnabled, QObject *parent = nullptr);
amnezia::ErrorCode get(const QString &endpoint, QByteArray &responseBody); amnezia::ErrorCode get(const QString &endpoint, QByteArray &responseBody);
amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody); amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody);
@ -30,6 +31,7 @@ private:
int m_requestTimeoutMsecs; int m_requestTimeoutMsecs;
QString m_gatewayEndpoint; QString m_gatewayEndpoint;
bool m_isDevEnvironment = false; bool m_isDevEnvironment = false;
bool m_isStrictKillSwitchEnabled = false;
}; };
#endif // GATEWAYCONTROLLER_H #endif // GATEWAYCONTROLLER_H

View file

@ -118,6 +118,7 @@ namespace amnezia
ApiConfigLimitError = 1108, ApiConfigLimitError = 1108,
ApiNotFoundError = 1109, ApiNotFoundError = 1109,
ApiMigrationError = 1110, ApiMigrationError = 1110,
ApiUpdateRequestError = 1111,
// QFile errors // QFile errors
OpenError = 1200, OpenError = 1200,

View file

@ -74,7 +74,8 @@ QString errorString(ErrorCode code) {
case (ErrorCode::ApiServicesMissingError): errorMessage = QObject::tr("Missing list of available services"); break; case (ErrorCode::ApiServicesMissingError): errorMessage = QObject::tr("Missing list of available services"); break;
case (ErrorCode::ApiConfigLimitError): errorMessage = QObject::tr("The limit of allowed configurations per subscription has been exceeded"); break; case (ErrorCode::ApiConfigLimitError): errorMessage = QObject::tr("The limit of allowed configurations per subscription has been exceeded"); break;
case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break; case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break;
case (ErrorCode::ApiMigrationError): errorMessage = QObject::tr("A migration error occurred. Please contact our technical support"); break; case (ErrorCode::ApiMigrationError): errorMessage = QObject::tr("A migration error has occurred. Please contact our technical support"); break;
case (ErrorCode::ApiUpdateRequestError): errorMessage = QObject::tr("Please update the application to use this feature"); break;
// QFile errors // QFile errors
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break; case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;

View file

@ -149,8 +149,7 @@ bool Daemon::activate(const InterfaceConfig& config) {
// set routing // set routing
for (const IPAddress& ip : config.m_allowedIPAddressRanges) { for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
if (!wgutils()->updateRoutePrefix(ip)) { if (!wgutils()->updateRoutePrefix(ip)) {
logger.debug() << "Routing configuration failed for" logger.debug() << "Routing configuration failed for" << ip.toString();
<< logger.sensitive(ip.toString());
return false; return false;
} }
} }

View file

@ -26,10 +26,22 @@ set_target_properties(networkextension PROPERTIES
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2" XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks" XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks"
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
) )
if(DEPLOY)
set_target_properties(networkextension PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.AmneziaVPN"
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev ios.org.amnezia.AmneziaVPN"
)
else()
set_target_properties(networkextension PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
)
endif()
set_target_properties(networkextension PROPERTIES set_target_properties(networkextension PROPERTIES
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0" XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES" XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"

View file

@ -97,7 +97,7 @@ bool IPUtilsLinux::addIP4AddressToDevice(const InterfaceConfig& config) {
// Set ifr to interface // Set ifr to interface
int ret = ioctl(sockfd, SIOCSIFADDR, &ifr); int ret = ioctl(sockfd, SIOCSIFADDR, &ifr);
if (ret) { if (ret) {
logger.error() << "Failed to set IPv4: " << logger.sensitive(deviceAddr) logger.error() << "Failed to set IPv4: " << deviceAddr
<< "error:" << strerror(errno); << "error:" << strerror(errno);
return false; return false;
} }
@ -138,7 +138,7 @@ bool IPUtilsLinux::addIP6AddressToDevice(const InterfaceConfig& config) {
// Set ifr6 to the interface // Set ifr6 to the interface
ret = ioctl(sockfd, SIOCSIFADDR, &ifr6); ret = ioctl(sockfd, SIOCSIFADDR, &ifr6);
if (ret && (errno != EEXIST)) { if (ret && (errno != EEXIST)) {
logger.error() << "Failed to set IPv6: " << logger.sensitive(deviceAddr) logger.error() << "Failed to set IPv6: " << deviceAddr
<< "error:" << strerror(errno); << "error:" << strerror(errno);
return false; return false;
} }

View file

@ -122,7 +122,7 @@ bool IPUtilsMacos::addIP4AddressToDevice(const InterfaceConfig& config) {
// Set ifr to interface // Set ifr to interface
int ret = ioctl(sockfd, SIOCAIFADDR, &ifr); int ret = ioctl(sockfd, SIOCAIFADDR, &ifr);
if (ret) { if (ret) {
logger.error() << "Failed to set IPv4: " << logger.sensitive(deviceAddr) logger.error() << "Failed to set IPv4: " << deviceAddr
<< "error:" << strerror(errno); << "error:" << strerror(errno);
return false; return false;
} }
@ -162,7 +162,7 @@ bool IPUtilsMacos::addIP6AddressToDevice(const InterfaceConfig& config) {
// Set ifr to interface // Set ifr to interface
int ret = ioctl(sockfd, SIOCAIFADDR_IN6, &ifr6); int ret = ioctl(sockfd, SIOCAIFADDR_IN6, &ifr6);
if (ret) { if (ret) {
logger.error() << "Failed to set IPv6: " << logger.sensitive(deviceAddr) logger.error() << "Failed to set IPv6: " << deviceAddr
<< "error:" << strerror(errno); << "error:" << strerror(errno);
return false; return false;
} }

View file

@ -144,7 +144,7 @@ void MacosRouteMonitor::handleRtmDelete(const struct rt_msghdr* rtm,
for (const IPAddress& prefix : m_exclusionRoutes) { for (const IPAddress& prefix : m_exclusionRoutes) {
if (prefix.address().protocol() == protocol) { if (prefix.address().protocol() == protocol) {
logger.debug() << "Removing exclusion route to" logger.debug() << "Removing exclusion route to"
<< logger.sensitive(prefix.toString()); << prefix.toString();
rtmSendRoute(RTM_DELETE, prefix, rtm->rtm_index, nullptr); rtmSendRoute(RTM_DELETE, prefix, rtm->rtm_index, nullptr);
} }
} }
@ -259,7 +259,7 @@ void MacosRouteMonitor::handleRtmUpdate(const struct rt_msghdr* rtm,
for (const IPAddress& prefix : m_exclusionRoutes) { for (const IPAddress& prefix : m_exclusionRoutes) {
if (prefix.address().protocol() == protocol) { if (prefix.address().protocol() == protocol) {
logger.debug() << "Updating exclusion route to" logger.debug() << "Updating exclusion route to"
<< logger.sensitive(prefix.toString()); << prefix.toString();
rtmSendRoute(rtm_type, prefix, ifindex, addrlist[1].constData()); rtmSendRoute(rtm_type, prefix, ifindex, addrlist[1].constData());
} }
} }
@ -510,8 +510,7 @@ bool MacosRouteMonitor::deleteRoute(const IPAddress& prefix, int flags) {
} }
bool MacosRouteMonitor::addExclusionRoute(const IPAddress& prefix) { bool MacosRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
logger.debug() << "Adding exclusion route for" logger.debug() << "Adding exclusion route for" << prefix.toString();
<< logger.sensitive(prefix.toString());
if (m_exclusionRoutes.contains(prefix)) { if (m_exclusionRoutes.contains(prefix)) {
logger.warning() << "Exclusion route already exists"; logger.warning() << "Exclusion route already exists";
@ -536,8 +535,7 @@ bool MacosRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
} }
bool MacosRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) { bool MacosRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) {
logger.debug() << "Deleting exclusion route for" logger.debug() << "Deleting exclusion route for" << prefix.toString();
<< logger.sensitive(prefix.toString());
m_exclusionRoutes.removeAll(prefix); m_exclusionRoutes.removeAll(prefix);
if (prefix.address().protocol() == QAbstractSocket::IPv4Protocol) { if (prefix.address().protocol() == QAbstractSocket::IPv4Protocol) {

View file

@ -303,8 +303,7 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) {
data->Age++; data->Age++;
continue; continue;
} }
logger.debug() << "Capturing route to" logger.debug() << "Capturing route to" << prefix.toString();
<< logger.sensitive(prefix.toString());
// Clone the route and direct it into the VPN tunnel. // Clone the route and direct it into the VPN tunnel.
data = new MIB_IPFORWARD_ROW2; data = new MIB_IPFORWARD_ROW2;
@ -354,8 +353,7 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) {
continue; continue;
} }
logger.debug() << "Removing route capture for" logger.debug() << "Removing route capture for" << i.key().toString();
<< logger.sensitive(i.key().toString());
// Otherwise, this route is no longer in use. // Otherwise, this route is no longer in use.
DWORD result = DeleteIpForwardEntry2(data); DWORD result = DeleteIpForwardEntry2(data);
@ -368,8 +366,7 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) {
} }
bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) { bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
logger.debug() << "Adding exclusion route for" logger.debug() << "Adding exclusion route for" << prefix.toString();
<< logger.sensitive(prefix.toString());
// Silently ignore non-routeable addresses. // Silently ignore non-routeable addresses.
QHostAddress addr = prefix.address(); QHostAddress addr = prefix.address();
@ -437,7 +434,7 @@ bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) { bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) {
logger.debug() << "Deleting exclusion route for" logger.debug() << "Deleting exclusion route for"
<< logger.sensitive(prefix.address().toString()); << prefix.address().toString();
MIB_IPFORWARD_ROW2* data = m_exclusionRoutes.take(prefix); MIB_IPFORWARD_ROW2* data = m_exclusionRoutes.take(prefix);
if (data == nullptr) { if (data == nullptr) {
@ -447,7 +444,7 @@ bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) {
DWORD result = DeleteIpForwardEntry2(data); DWORD result = DeleteIpForwardEntry2(data);
if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) { if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) {
logger.error() << "Failed to delete route to" logger.error() << "Failed to delete route to"
<< logger.sensitive(prefix.toString()) << prefix.toString()
<< "result:" << result; << "result:" << result;
} }
@ -465,7 +462,7 @@ void WindowsRouteMonitor::flushRouteTable(
DWORD result = DeleteIpForwardEntry2(data); DWORD result = DeleteIpForwardEntry2(data);
if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) { if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) {
logger.error() << "Failed to delete route to" logger.error() << "Failed to delete route to"
<< logger.sensitive(i.key().toString()) << i.key().toString()
<< "result:" << result; << "result:" << result;
} }
delete data; delete data;

View file

@ -174,11 +174,12 @@ public:
QLocale getAppLanguage() QLocale getAppLanguage()
{ {
return value("Conf/appLanguage", QLocale()).toLocale(); QString localeStr = m_settings.value("Conf/appLanguage").toString();
return QLocale(localeStr);
}; };
void setAppLanguage(QLocale locale) void setAppLanguage(QLocale locale)
{ {
setValue("Conf/appLanguage", locale); setValue("Conf/appLanguage", locale.name());
}; };
bool isScreenshotsEnabled() const bool isScreenshotsEnabled() const

File diff suppressed because it is too large Load diff

View file

@ -1,34 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
TextArea {
id: root
width: parent.width
topPadding: 16
leftPadding: 16
color: "#D7D8DB"
selectionColor: "#412102"
selectedTextColor: "#D7D8DB"
placeholderTextColor: "#878B91"
font.pixelSize: 16
font.weight: Font.Medium
font.family: "PT Root UI VF"
wrapMode: Text.Wrap
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: contextMenu.open()
}
ContextMenuType {
id: contextMenu
textObj: textField
}
}

View file

@ -63,7 +63,8 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
return false; return false;
} }
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex()); auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
@ -76,6 +77,7 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
apiPayload[configKey::serverCountryCode] = serverCountryCode; apiPayload[configKey::serverCountryCode] = serverCountryCode;
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType); apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData); apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
QByteArray responseBody; QByteArray responseBody;
ErrorCode errorCode = gatewayController.post(QString("%1v1/native_config"), apiPayload, responseBody); ErrorCode errorCode = gatewayController.post(QString("%1v1/native_config"), apiPayload, responseBody);
@ -94,7 +96,8 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode) bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
{ {
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex()); auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
@ -107,6 +110,7 @@ bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
apiPayload[configKey::serverCountryCode] = serverCountryCode; apiPayload[configKey::serverCountryCode] = serverCountryCode;
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType); apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData); apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
QByteArray responseBody; QByteArray responseBody;
ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_native_config"), apiPayload, responseBody); ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_native_config"), apiPayload, responseBody);
@ -140,7 +144,8 @@ void ApiConfigsController::copyVpnKeyToClipboard()
bool ApiConfigsController::fillAvailableServices() bool ApiConfigsController::fillAvailableServices()
{ {
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
QJsonObject apiPayload; QJsonObject apiPayload;
apiPayload[configKey::osVersion] = QSysInfo::productType(); apiPayload[configKey::osVersion] = QSysInfo::productType();
@ -171,7 +176,8 @@ bool ApiConfigsController::importServiceFromGateway()
return false; return false;
} }
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
auto installationUuid = m_settings->getInstallationUuid(true); auto installationUuid = m_settings->getInstallationUuid(true);
auto userCountryCode = m_apiServicesModel->getCountryCode(); auto userCountryCode = m_apiServicesModel->getCountryCode();
@ -184,6 +190,7 @@ bool ApiConfigsController::importServiceFromGateway()
apiPayload[configKey::userCountryCode] = userCountryCode; apiPayload[configKey::userCountryCode] = userCountryCode;
apiPayload[configKey::serviceType] = serviceType; apiPayload[configKey::serviceType] = serviceType;
apiPayload[configKey::uuid] = installationUuid; apiPayload[configKey::uuid] = installationUuid;
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
QByteArray responseBody; QByteArray responseBody;
ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody); ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody);
@ -211,7 +218,8 @@ bool ApiConfigsController::importServiceFromGateway()
bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName, bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
bool reloadServiceConfig) bool reloadServiceConfig)
{ {
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
auto serverConfig = m_serversModel->getServerConfig(serverIndex); auto serverConfig = m_serversModel->getServerConfig(serverIndex);
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject(); auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
@ -228,6 +236,7 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
apiPayload[configKey::userCountryCode] = userCountryCode; apiPayload[configKey::userCountryCode] = userCountryCode;
apiPayload[configKey::serviceType] = serviceType; apiPayload[configKey::serviceType] = serviceType;
apiPayload[configKey::uuid] = installationUuid; apiPayload[configKey::uuid] = installationUuid;
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
if (!newCountryCode.isEmpty()) { if (!newCountryCode.isEmpty()) {
apiPayload[configKey::serverCountryCode] = newCountryCode; apiPayload[configKey::serverCountryCode] = newCountryCode;
@ -274,7 +283,8 @@ bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex)
QThread::msleep(10); QThread::msleep(10);
#endif #endif
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
auto serverConfig = m_serversModel->getServerConfig(serverIndex); auto serverConfig = m_serversModel->getServerConfig(serverIndex);
auto installationUuid = m_settings->getInstallationUuid(true); auto installationUuid = m_settings->getInstallationUuid(true);
@ -304,7 +314,8 @@ bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex)
bool ApiConfigsController::deactivateDevice() bool ApiConfigsController::deactivateDevice()
{ {
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
auto serverIndex = m_serversModel->getProcessedServerIndex(); auto serverIndex = m_serversModel->getProcessedServerIndex();
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex); auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
@ -323,6 +334,7 @@ bool ApiConfigsController::deactivateDevice()
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType); apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData); apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
apiPayload[configKey::uuid] = m_settings->getInstallationUuid(true); apiPayload[configKey::uuid] = m_settings->getInstallationUuid(true);
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
QByteArray responseBody; QByteArray responseBody;
ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody); ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody);
@ -339,7 +351,8 @@ bool ApiConfigsController::deactivateDevice()
bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode) bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode)
{ {
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
auto serverIndex = m_serversModel->getProcessedServerIndex(); auto serverIndex = m_serversModel->getProcessedServerIndex();
auto serverConfigObject = m_serversModel->getServerConfig(serverIndex); auto serverConfigObject = m_serversModel->getServerConfig(serverIndex);
@ -358,6 +371,7 @@ bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const Q
apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType); apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType);
apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData); apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData);
apiPayload[configKey::uuid] = uuid; apiPayload[configKey::uuid] = uuid;
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
QByteArray responseBody; QByteArray responseBody;
ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody); ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody);

View file

@ -29,17 +29,20 @@ bool ApiPremV1MigrationController::hasConfigsToMigration()
vpnKeys.append(vpnKey); vpnKeys.append(vpnKey);
} }
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); if (!vpnKeys.isEmpty()) {
QJsonObject apiPayload; GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
QJsonObject apiPayload;
apiPayload["configs"] = vpnKeys; apiPayload["configs"] = vpnKeys;
QByteArray responseBody; QByteArray responseBody;
ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/is-active-subscription"), apiPayload, responseBody); ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/is-active-subscription"), apiPayload, responseBody);
auto migrationsStatus = QJsonDocument::fromJson(responseBody).object(); auto migrationsStatus = QJsonDocument::fromJson(responseBody).object();
for (const auto &migrationStatus : migrationsStatus) { for (const auto &migrationStatus : migrationsStatus) {
if (migrationStatus == "not_found") { if (migrationStatus == "not_found") {
return true; return true;
}
} }
} }
@ -48,7 +51,8 @@ bool ApiPremV1MigrationController::hasConfigsToMigration()
void ApiPremV1MigrationController::getSubscriptionList(const QString &email) void ApiPremV1MigrationController::getSubscriptionList(const QString &email)
{ {
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
QJsonObject apiPayload; QJsonObject apiPayload;
apiPayload[apiDefs::key::email] = email; apiPayload[apiDefs::key::email] = email;
@ -80,7 +84,8 @@ void ApiPremV1MigrationController::sendMigrationCode(const int subscriptionIndex
QTimer::singleShot(1000, &wait, &QEventLoop::quit); QTimer::singleShot(1000, &wait, &QEventLoop::quit);
wait.exec(); wait.exec();
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
QJsonObject apiPayload; QJsonObject apiPayload;
apiPayload[apiDefs::key::email] = m_email; apiPayload[apiDefs::key::email] = m_email;
@ -97,7 +102,8 @@ void ApiPremV1MigrationController::sendMigrationCode(const int subscriptionIndex
void ApiPremV1MigrationController::migrate(const QString &migrationCode) void ApiPremV1MigrationController::migrate(const QString &migrationCode)
{ {
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
QJsonObject apiPayload; QJsonObject apiPayload;
apiPayload[apiDefs::key::email] = m_email; apiPayload[apiDefs::key::email] = m_email;

View file

@ -5,6 +5,7 @@
#include "core/api/apiUtils.h" #include "core/api/apiUtils.h"
#include "core/controllers/gatewayController.h" #include "core/controllers/gatewayController.h"
#include "version.h"
namespace namespace
{ {
@ -48,7 +49,8 @@ bool ApiSettingsController::getAccountInfo(bool reload)
wait.exec(); wait.exec();
} }
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), requestTimeoutMsecs); GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), requestTimeoutMsecs,
m_settings->isStrictKillSwitchEnabled());
auto processedIndex = m_serversModel->getProcessedServerIndex(); auto processedIndex = m_serversModel->getProcessedServerIndex();
auto serverConfig = m_serversModel->getServerConfig(processedIndex); auto serverConfig = m_serversModel->getServerConfig(processedIndex);
@ -59,15 +61,14 @@ bool ApiSettingsController::getAccountInfo(bool reload)
apiPayload[configKey::userCountryCode] = apiConfig.value(configKey::userCountryCode).toString(); apiPayload[configKey::userCountryCode] = apiConfig.value(configKey::userCountryCode).toString();
apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString(); apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString();
apiPayload[configKey::authData] = authData; apiPayload[configKey::authData] = authData;
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
QByteArray responseBody; QByteArray responseBody;
if (apiUtils::isPremiumServer(serverConfig)) { ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody);
ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody); if (errorCode != ErrorCode::NoError) {
if (errorCode != ErrorCode::NoError) { emit errorOccurred(errorCode);
emit errorOccurred(errorCode); return false;
return false;
}
} }
QJsonObject accountInfo = QJsonDocument::fromJson(responseBody).object(); QJsonObject accountInfo = QJsonDocument::fromJson(responseBody).object();

View file

@ -126,7 +126,13 @@ void SettingsController::clearLogs()
void SettingsController::backupAppConfig(const QString &fileName) void SettingsController::backupAppConfig(const QString &fileName)
{ {
SystemController::saveFile(fileName, m_settings->backupAppConfig()); QByteArray data = m_settings->backupAppConfig();
QJsonDocument doc = QJsonDocument::fromJson(data);
QJsonObject config = doc.object();
config["Conf/autoStart"] = Autostart::isAutostart();
SystemController::saveFile(fileName, QJsonDocument(config).toJson());
} }
void SettingsController::restoreAppConfig(const QString &fileName) void SettingsController::restoreAppConfig(const QString &fileName)
@ -142,6 +148,13 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
if (ok) { if (ok) {
QJsonObject newConfigData = QJsonDocument::fromJson(data).object(); QJsonObject newConfigData = QJsonDocument::fromJson(data).object();
#if defined(Q_OS_WINDOWS) || defined(Q_OS_LINUX) || defined(Q_OS_MACX)
bool autoStart = false;
if (newConfigData.contains("Conf/autoStart")) {
autoStart = newConfigData["Conf/autoStart"].toBool();
}
toggleAutoStart(autoStart);
#endif
m_serversModel->resetModel(); m_serversModel->resetModel();
m_languageModel->changeLanguage( m_languageModel->changeLanguage(
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex())); static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
@ -181,6 +194,8 @@ void SettingsController::clearSettings()
m_appSplitTunnelingModel->setRouteMode(Settings::AppsRouteMode::VpnAllExceptApps); m_appSplitTunnelingModel->setRouteMode(Settings::AppsRouteMode::VpnAllExceptApps);
m_appSplitTunnelingModel->toggleSplitTunneling(false); m_appSplitTunnelingModel->toggleSplitTunneling(false);
toggleAutoStart(false);
emit changeSettingsFinished(tr("All settings have been reset to default values")); emit changeSettingsFinished(tr("All settings have been reset to default values"));
#ifdef Q_OS_IOS #ifdef Q_OS_IOS

View file

@ -73,7 +73,7 @@ DrawerType2 {
var str = qsTr("We'll preserve all remaining days of your current subscription and give you an extra month as a thank you. ") var str = qsTr("We'll preserve all remaining days of your current subscription and give you an extra month as a thank you. ")
str += qsTr("This new subscription type will be actively developed with more locations and features added regularly. Currently available:") str += qsTr("This new subscription type will be actively developed with more locations and features added regularly. Currently available:")
str += "<ul style='margin-left: -16px;'>" str += "<ul style='margin-left: -16px;'>"
str += qsTr("<li>9 locations (with more coming soon)</li>") str += qsTr("<li>13 locations (with more coming soon)</li>")
str += qsTr("<li>Easier switching between countries in the app</li>") str += qsTr("<li>Easier switching between countries in the app</li>")
str += qsTr("<li>Personal dashboard to manage your subscription</li>") str += qsTr("<li>Personal dashboard to manage your subscription</li>")
str += "</ul>" str += "</ul>"

View file

@ -88,6 +88,7 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
visible: link !== ""
text: title text: title
descriptionText: description descriptionText: description
rightImageSource: "qrc:/images/controls/external-link.svg" rightImageSource: "qrc:/images/controls/external-link.svg"

View file

@ -49,7 +49,7 @@ PageType {
if (!ConnectionController.isConnected) { if (!ConnectionController.isConnected) {
SettingsController.isKillSwitchEnabled = checked SettingsController.isKillSwitchEnabled = checked
} else { } else {
PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection")) PageController.showNotificationMessage(qsTr("KillSwitch settings cannot be changed during an active connection"))
switcher.checked = SettingsController.isKillSwitchEnabled switcher.checked = SettingsController.isKillSwitchEnabled
} }
} }
@ -66,7 +66,7 @@ PageType {
checked: !SettingsController.strictKillSwitchEnabled checked: !SettingsController.strictKillSwitchEnabled
text: qsTr("Soft KillSwitch") text: qsTr("Soft KillSwitch")
descriptionText: qsTr("Internet connection is blocked if VPN connection drops accidentally") descriptionText: qsTr("Internet access is blocked if the VPN disconnects unexpectedly")
onClicked: function() { onClicked: function() {
SettingsController.strictKillSwitchEnabled = false SettingsController.strictKillSwitchEnabled = false
@ -81,15 +81,16 @@ PageType {
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected visible: false
enabled: false //SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected
checked: SettingsController.strictKillSwitchEnabled checked: SettingsController.strictKillSwitchEnabled
text: qsTr("Strict KillSwitch") text: qsTr("Strict KillSwitch")
descriptionText: qsTr("Internet connection is blocked even if VPN was turned off manually or not started") descriptionText: qsTr("Internet connection is blocked even when VPN is turned off manually or hasn't started")
onClicked: function() { onClicked: function() {
var headerText = qsTr("Just a little heads-up") var headerText = qsTr("Just a little heads-up")
var descriptionText = qsTr("If you disconnect from VPN or the VPN connection drops while the Strict Kill Switch is turned on, your internet access will be disabled. To restore it, connect to VPN, change the Kill Switch mode or turn the Kill Switch off.") var descriptionText = qsTr("If the VPN disconnects or drops while Strict KillSwitch is enabled, internet access will be blocked. To restore access, reconnect VPN or disable/change the KillSwitch.")
var yesButtonText = qsTr("Continue") var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel") var noButtonText = qsTr("Cancel")
@ -103,7 +104,9 @@ PageType {
} }
} }
DividerType {} DividerType {
visible: false
}
LabelWithButtonType { LabelWithButtonType {
Layout.topMargin: 32 Layout.topMargin: 32
@ -111,7 +114,7 @@ PageType {
enabled: true enabled: true
text: qsTr("DNS Exceptions") text: qsTr("DNS Exceptions")
descriptionText: qsTr("DNS servers from the list will remain accessible when Kill Switch is triggered") descriptionText: qsTr("DNS servers listed here will remain accessible when KillSwitch is active.")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {

View file

@ -43,7 +43,7 @@ PageType {
Layout.leftMargin: 16 Layout.leftMargin: 16
headerText: qsTr("DNS Exceptions") headerText: qsTr("DNS Exceptions")
descriptionText: qsTr("DNS servers from the list will remain accessible when Kill Switch is triggered") descriptionText: qsTr("DNS servers listed here will remain accessible when KillSwitch is active")
} }
} }