Merge branch 'dev' into feature/app-update

This commit is contained in:
aiamnezia 2025-06-17 01:10:37 +04:00
commit 273eeb78c6
23 changed files with 154 additions and 98 deletions

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.7.1 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 2085) 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
)
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 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

@ -36,6 +36,8 @@ 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, const bool isDevEnvironment, const int requestTimeoutMsecs, GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
@ -311,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

@ -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

@ -75,6 +75,7 @@ QString errorString(ErrorCode code) {
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 has 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,9 +26,21 @@ 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"
)
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 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"

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

@ -103,6 +103,8 @@ namespace amnezia
constexpr char clientId[] = "clientId"; constexpr char clientId[] = "clientId";
constexpr char nameOverriddenByUser[] = "nameOverriddenByUser";
} }
namespace protocols namespace protocols

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

View file

@ -89,17 +89,17 @@
<context> <context>
<name>ApiConfigsController</name> <name>ApiConfigsController</name>
<message> <message>
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="207"/> <location filename="../ui/controllers/api/apiConfigsController.cpp" line="210"/>
<source>%1 installed successfully.</source> <source>%1 installed successfully.</source>
<translation>%1 успешно установлен.</translation> <translation>%1 успешно установлен.</translation>
</message> </message>
<message> <message>
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="262"/> <location filename="../ui/controllers/api/apiConfigsController.cpp" line="266"/>
<source>API config reloaded</source> <source>API config reloaded</source>
<translation>Конфигурация API перезагружена</translation> <translation>Конфигурация API перезагружена</translation>
</message> </message>
<message> <message>
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="266"/> <location filename="../ui/controllers/api/apiConfigsController.cpp" line="270"/>
<source>Successfully changed the country of connection to %1</source> <source>Successfully changed the country of connection to %1</source>
<translation>Страна подключения изменена на %1</translation> <translation>Страна подключения изменена на %1</translation>
</message> </message>
@ -358,7 +358,7 @@
<context> <context>
<name>ContextMenuType</name> <name>ContextMenuType</name>
<message> <message>
<location filename="../ui/qml/Controls2/ContextMenuType.qml" line="9"/> <location filename="../ui/qml/Controls2/ContextMenuType.qml" line="10"/>
<source>C&amp;ut</source> <source>C&amp;ut</source>
<translation>Вырезать</translation> <translation>Вырезать</translation>
</message> </message>
@ -368,12 +368,12 @@
<translation>Копировать</translation> <translation>Копировать</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Controls2/ContextMenuType.qml" line="21"/> <location filename="../ui/qml/Controls2/ContextMenuType.qml" line="20"/>
<source>&amp;Paste</source> <source>&amp;Paste</source>
<translation>Вставить</translation> <translation>Вставить</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Controls2/ContextMenuType.qml" line="29"/> <location filename="../ui/qml/Controls2/ContextMenuType.qml" line="27"/>
<source>&amp;SelectAll</source> <source>&amp;SelectAll</source>
<translation>Выбрать всё</translation> <translation>Выбрать всё</translation>
</message> </message>
@ -2412,42 +2412,42 @@ Thank you for staying with us!</source>
<translation>Доступ в интернет блокируется при разрыве VPN-соединения</translation> <translation>Доступ в интернет блокируется при разрыве VPN-соединения</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsKillSwitch.qml" line="87"/> <location filename="../ui/qml/Pages2/PageSettingsKillSwitch.qml" line="88"/>
<source>Strict KillSwitch</source> <source>Strict KillSwitch</source>
<translation>Strict KillSwitch</translation> <translation>Strict KillSwitch</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsKillSwitch.qml" line="88"/> <location filename="../ui/qml/Pages2/PageSettingsKillSwitch.qml" line="89"/>
<source>Internet connection is blocked even when VPN is turned off manually or hasn&apos;t started</source> <source>Internet connection is blocked even when VPN is turned off manually or hasn&apos;t started</source>
<translation>Доступ в интернет блокируется, даже если VPN отключен вручную или не был запущен</translation> <translation>Доступ в интернет блокируется, даже если VPN отключен вручную или не был запущен</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsKillSwitch.qml" line="91"/> <location filename="../ui/qml/Pages2/PageSettingsKillSwitch.qml" line="92"/>
<source>Just a little heads-up</source> <source>Just a little heads-up</source>
<translation>Небольшое предупреждение</translation> <translation>Небольшое предупреждение</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsKillSwitch.qml" line="92"/> <location filename="../ui/qml/Pages2/PageSettingsKillSwitch.qml" line="93"/>
<source>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.</source> <source>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.</source>
<translation>Если VPN отключится или соединение прервётся при включённом Strict KillSwitch, доступ в интернет будет заблокирован. Чтобы восстановить доступ, снова подключитесь к VPN или отключите (измените) режим KillSwitch.</translation> <translation>Если VPN отключится или соединение прервётся при включённом Strict KillSwitch, доступ в интернет будет заблокирован. Чтобы восстановить доступ, снова подключитесь к VPN или отключите (измените) режим KillSwitch.</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsKillSwitch.qml" line="93"/> <location filename="../ui/qml/Pages2/PageSettingsKillSwitch.qml" line="94"/>
<source>Continue</source> <source>Continue</source>
<translation>Продолжить</translation> <translation>Продолжить</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsKillSwitch.qml" line="94"/> <location filename="../ui/qml/Pages2/PageSettingsKillSwitch.qml" line="95"/>
<source>Cancel</source> <source>Cancel</source>
<translation>Отменить</translation> <translation>Отменить</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsKillSwitch.qml" line="113"/> <location filename="../ui/qml/Pages2/PageSettingsKillSwitch.qml" line="116"/>
<source>DNS Exceptions</source> <source>DNS Exceptions</source>
<translation>Исключения для DNS</translation> <translation>Исключения для DNS</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsKillSwitch.qml" line="114"/> <location filename="../ui/qml/Pages2/PageSettingsKillSwitch.qml" line="117"/>
<source>DNS servers listed here will remain accessible when KillSwitch is active.</source> <source>DNS servers listed here will remain accessible when KillSwitch is active.</source>
<translation>DNS-серверы из этого списка останутся доступными при активном KillSwitch.</translation> <translation>DNS-серверы из этого списка останутся доступными при активном KillSwitch.</translation>
</message> </message>
@ -4085,7 +4085,12 @@ Thank you for staying with us!</source>
<translation>Произошла ошибка миграции. Обратитесь в нашу техническую поддержку</translation> <translation>Произошла ошибка миграции. Обратитесь в нашу техническую поддержку</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="92"/> <location filename="../core/errorstrings.cpp" line="78"/>
<source>Please update the application to use this feature</source>
<translation>Пожалуйста, обновите приложение, чтобы использовать эту функцию</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="93"/>
<source>ErrorCode: %1. </source> <source>ErrorCode: %1. </source>
<translation>Код ошибки: %1. </translation> <translation>Код ошибки: %1. </translation>
</message> </message>
@ -4196,37 +4201,37 @@ Thank you for staying with us!</source>
<translation type="vanished">Произошла ошибка миграции. Обратитесь в нашу техническую поддержку</translation> <translation type="vanished">Произошла ошибка миграции. Обратитесь в нашу техническую поддержку</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="80"/> <location filename="../core/errorstrings.cpp" line="81"/>
<source>QFile error: The file could not be opened</source> <source>QFile error: The file could not be opened</source>
<translation>Ошибка QFile: не удалось открыть файл</translation> <translation>Ошибка QFile: не удалось открыть файл</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="81"/> <location filename="../core/errorstrings.cpp" line="82"/>
<source>QFile error: An error occurred when reading from the file</source> <source>QFile error: An error occurred when reading from the file</source>
<translation>Ошибка QFile: произошла ошибка при чтении из файла</translation> <translation>Ошибка QFile: произошла ошибка при чтении из файла</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="82"/> <location filename="../core/errorstrings.cpp" line="83"/>
<source>QFile error: The file could not be accessed</source> <source>QFile error: The file could not be accessed</source>
<translation>Ошибка QFile: не удалось получить доступ к файлу</translation> <translation>Ошибка QFile: не удалось получить доступ к файлу</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="83"/> <location filename="../core/errorstrings.cpp" line="84"/>
<source>QFile error: An unspecified error occurred</source> <source>QFile error: An unspecified error occurred</source>
<translation>Ошибка QFile: произошла неизвестная ошибка</translation> <translation>Ошибка QFile: произошла неизвестная ошибка</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="84"/> <location filename="../core/errorstrings.cpp" line="85"/>
<source>QFile error: A fatal error occurred</source> <source>QFile error: A fatal error occurred</source>
<translation>Ошибка QFile: произошла фатальная ошибка</translation> <translation>Ошибка QFile: произошла фатальная ошибка</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="85"/> <location filename="../core/errorstrings.cpp" line="86"/>
<source>QFile error: The operation was aborted</source> <source>QFile error: The operation was aborted</source>
<translation>Ошибка QFile: операция была прервана</translation> <translation>Ошибка QFile: операция была прервана</translation>
</message> </message>
<message> <message>
<location filename="../core/errorstrings.cpp" line="89"/> <location filename="../core/errorstrings.cpp" line="90"/>
<source>Internal error</source> <source>Internal error</source>
<translation>Внутренняя ошибка</translation> <translation>Внутренняя ошибка</translation>
</message> </message>

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

@ -77,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);
@ -109,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);
@ -188,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);
@ -233,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;
@ -257,6 +261,10 @@ 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);
if (serverConfig.value(config_key::nameOverriddenByUser).toBool()) {
newServerConfig.insert(config_key::name, serverConfig.value(config_key::name));
newServerConfig.insert(config_key::nameOverriddenByUser, true);
}
m_serversModel->editServer(newServerConfig, serverIndex); m_serversModel->editServer(newServerConfig, serverIndex);
if (reloadServiceConfig) { if (reloadServiceConfig) {
emit reloadServerFromApiFinished(tr("API config reloaded")); emit reloadServerFromApiFinished(tr("API config reloaded"));
@ -330,6 +338,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);
@ -366,6 +375,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

@ -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
{ {
@ -60,6 +61,7 @@ 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;

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)
@ -140,9 +146,30 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
{ {
bool ok = m_settings->restoreAppConfig(data); bool ok = m_settings->restoreAppConfig(data);
if (ok) { if (ok) {
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()));
#if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
int appSplitTunnelingRouteMode = newConfigData.value("Conf/appsRouteMode").toInt();
bool appSplittunnelingEnabled = newConfigData.value("Conf/appsSplitTunnelingEnabled").toBool();
m_appSplitTunnelingModel->setRouteMode(appSplitTunnelingRouteMode);
m_appSplitTunnelingModel->toggleSplitTunneling(appSplittunnelingEnabled);
#endif
int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt();
bool siteSplittunnelingEnabled = newConfigData.value("Conf/sitesSplitTunnelingEnabled").toBool();
m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode);
m_sitesModel->toggleSplitTunneling(siteSplittunnelingEnabled);
emit restoreBackupFinished(); emit restoreBackupFinished();
} else { } else {
emit changeSettingsErrorOccurred(tr("Backup file is corrupted")); emit changeSettingsErrorOccurred(tr("Backup file is corrupted"));
@ -167,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

@ -66,6 +66,7 @@ bool ServersModel::setData(const QModelIndex &index, const QVariant &value, int
} else { } else {
server.insert(config_key::description, value.toString()); server.insert(config_key::description, value.toString());
} }
server.insert(config_key::nameOverriddenByUser, true);
m_settings->editServer(index.row(), server); m_settings->editServer(index.row(), server);
m_servers.replace(index.row(), server); m_servers.replace(index.row(), server);
if (index.row() == m_defaultServerIndex) { if (index.row() == m_defaultServerIndex) {

View file

@ -81,7 +81,8 @@ 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")
@ -103,7 +104,9 @@ PageType {
} }
} }
DividerType {} DividerType {
visible: false
}
LabelWithButtonType { LabelWithButtonType {
Layout.topMargin: 32 Layout.topMargin: 32

View file

@ -29,10 +29,10 @@ wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSIO
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_armeabi-v7a.apk wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_armeabi-v7a.apk
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_x86.apk wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_x86.apk
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_x86_64.apk wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_x86_64.apk
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_linux.tar.zip wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_linux_x64.tar.zip
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos.dmg wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos.dmg
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos_old.dmg wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos_old.dmg
wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_x64.exe wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_windows_x64.exe
cd ../ cd ../