diff --git a/CMakeLists.txt b/CMakeLists.txt index dc76a6c3..424dcf3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT AmneziaVPN) -project(${PROJECT} VERSION 4.8.7.1 +project(${PROJECT} VERSION 4.8.7.2 DESCRIPTION "AmneziaVPN" HOMEPAGE_URL "https://amnezia.org/" ) @@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d") set(RELEASE_DATE "${CURRENT_DATE}") 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") set(MZ_PLATFORM_NAME "linux") diff --git a/client/core/api/apiDefs.h b/client/core/api/apiDefs.h index 6d1a27fa..4588ef04 100644 --- a/client/core/api/apiDefs.h +++ b/client/core/api/apiDefs.h @@ -31,6 +31,7 @@ namespace apiDefs constexpr QLatin1String apiConfig("api_config"); constexpr QLatin1String stackType("stack_type"); constexpr QLatin1String serviceType("service_type"); + constexpr QLatin1String cliVersion("cli_version"); constexpr QLatin1String vpnKey("vpn_key"); constexpr QLatin1String config("config"); diff --git a/client/core/api/apiUtils.cpp b/client/core/api/apiUtils.cpp index f85d2207..7f3e6db3 100644 --- a/client/core/api/apiUtils.cpp +++ b/client/core/api/apiUtils.cpp @@ -41,32 +41,34 @@ bool apiUtils::isServerFromApi(const QJsonObject &serverConfigObject) apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObject) { auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt(); + switch (configVersion) { 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: { constexpr QLatin1String servicePremium("amnezia-premium"); constexpr QLatin1String serviceFree("amnezia-free"); 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 serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString(); - auto apiEndpoint = serverConfigObject.value(apiDefs::key::apiEndpoint).toString(); - if (serviceType == servicePremium) { return apiDefs::ConfigType::AmneziaPremiumV2; } else if (serviceType == serviceFree) { return apiDefs::ConfigType::AmneziaFreeV3; } else if (serviceType == serviceExternalPremium) { return apiDefs::ConfigType::ExternalPremium; - } else if (apiEndpoint.contains(premiumV1Endpoint)) { - return apiDefs::ConfigType::AmneziaPremiumV1; - } else if (apiEndpoint.contains(freeV2Endpoint)) { - return apiDefs::ConfigType::AmneziaFreeV2; } } default: { @@ -94,6 +96,9 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList &ssl || reply->error() == QNetworkReply::NetworkError::TimeoutError) { qDebug() << reply->error(); return amnezia::ErrorCode::ApiConfigTimeoutError; + } else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) { + qDebug() << reply->error(); + return amnezia::ErrorCode::ApiUpdateRequestError; } else { QString err = reply->errorString(); int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); diff --git a/client/core/controllers/gatewayController.cpp b/client/core/controllers/gatewayController.cpp index 9a7ee6e5..26855ae6 100644 --- a/client/core/controllers/gatewayController.cpp +++ b/client/core/controllers/gatewayController.cpp @@ -36,6 +36,8 @@ namespace constexpr QLatin1String errorResponsePattern1("No active configuration found for"); constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for"); 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, @@ -311,6 +313,13 @@ bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray qDebug() << reply->error(); 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) { qDebug() << reply->error(); return true; diff --git a/client/core/defs.h b/client/core/defs.h index 674d1add..df6a1342 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -118,6 +118,7 @@ namespace amnezia ApiConfigLimitError = 1108, ApiNotFoundError = 1109, ApiMigrationError = 1110, + ApiUpdateRequestError = 1111, // QFile errors OpenError = 1200, diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index e141b3c7..7cc46220 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -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::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::ApiUpdateRequestError): errorMessage = QObject::tr("Please update the application to use this feature"); break; // QFile errors case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break; diff --git a/client/daemon/daemon.cpp b/client/daemon/daemon.cpp index e4b0ab3d..24e72629 100644 --- a/client/daemon/daemon.cpp +++ b/client/daemon/daemon.cpp @@ -149,8 +149,7 @@ bool Daemon::activate(const InterfaceConfig& config) { // set routing for (const IPAddress& ip : config.m_allowedIPAddressRanges) { if (!wgutils()->updateRoutePrefix(ip)) { - logger.debug() << "Routing configuration failed for" - << logger.sensitive(ip.toString()); + logger.debug() << "Routing configuration failed for" << ip.toString(); return false; } } diff --git a/client/platforms/linux/daemon/iputilslinux.cpp b/client/platforms/linux/daemon/iputilslinux.cpp index 63bd92f9..25d4f631 100644 --- a/client/platforms/linux/daemon/iputilslinux.cpp +++ b/client/platforms/linux/daemon/iputilslinux.cpp @@ -97,7 +97,7 @@ bool IPUtilsLinux::addIP4AddressToDevice(const InterfaceConfig& config) { // Set ifr to interface int ret = ioctl(sockfd, SIOCSIFADDR, &ifr); if (ret) { - logger.error() << "Failed to set IPv4: " << logger.sensitive(deviceAddr) + logger.error() << "Failed to set IPv4: " << deviceAddr << "error:" << strerror(errno); return false; } @@ -138,7 +138,7 @@ bool IPUtilsLinux::addIP6AddressToDevice(const InterfaceConfig& config) { // Set ifr6 to the interface ret = ioctl(sockfd, SIOCSIFADDR, &ifr6); if (ret && (errno != EEXIST)) { - logger.error() << "Failed to set IPv6: " << logger.sensitive(deviceAddr) + logger.error() << "Failed to set IPv6: " << deviceAddr << "error:" << strerror(errno); return false; } diff --git a/client/platforms/macos/daemon/iputilsmacos.cpp b/client/platforms/macos/daemon/iputilsmacos.cpp index 0599e2eb..901436ae 100644 --- a/client/platforms/macos/daemon/iputilsmacos.cpp +++ b/client/platforms/macos/daemon/iputilsmacos.cpp @@ -122,7 +122,7 @@ bool IPUtilsMacos::addIP4AddressToDevice(const InterfaceConfig& config) { // Set ifr to interface int ret = ioctl(sockfd, SIOCAIFADDR, &ifr); if (ret) { - logger.error() << "Failed to set IPv4: " << logger.sensitive(deviceAddr) + logger.error() << "Failed to set IPv4: " << deviceAddr << "error:" << strerror(errno); return false; } @@ -162,7 +162,7 @@ bool IPUtilsMacos::addIP6AddressToDevice(const InterfaceConfig& config) { // Set ifr to interface int ret = ioctl(sockfd, SIOCAIFADDR_IN6, &ifr6); if (ret) { - logger.error() << "Failed to set IPv6: " << logger.sensitive(deviceAddr) + logger.error() << "Failed to set IPv6: " << deviceAddr << "error:" << strerror(errno); return false; } diff --git a/client/platforms/macos/daemon/macosroutemonitor.cpp b/client/platforms/macos/daemon/macosroutemonitor.cpp index bd991c01..062f97f3 100644 --- a/client/platforms/macos/daemon/macosroutemonitor.cpp +++ b/client/platforms/macos/daemon/macosroutemonitor.cpp @@ -144,7 +144,7 @@ void MacosRouteMonitor::handleRtmDelete(const struct rt_msghdr* rtm, for (const IPAddress& prefix : m_exclusionRoutes) { if (prefix.address().protocol() == protocol) { logger.debug() << "Removing exclusion route to" - << logger.sensitive(prefix.toString()); + << prefix.toString(); 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) { if (prefix.address().protocol() == protocol) { logger.debug() << "Updating exclusion route to" - << logger.sensitive(prefix.toString()); + << prefix.toString(); 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) { - logger.debug() << "Adding exclusion route for" - << logger.sensitive(prefix.toString()); + logger.debug() << "Adding exclusion route for" << prefix.toString(); if (m_exclusionRoutes.contains(prefix)) { logger.warning() << "Exclusion route already exists"; @@ -536,8 +535,7 @@ bool MacosRouteMonitor::addExclusionRoute(const IPAddress& prefix) { } bool MacosRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) { - logger.debug() << "Deleting exclusion route for" - << logger.sensitive(prefix.toString()); + logger.debug() << "Deleting exclusion route for" << prefix.toString(); m_exclusionRoutes.removeAll(prefix); if (prefix.address().protocol() == QAbstractSocket::IPv4Protocol) { diff --git a/client/platforms/windows/daemon/windowsroutemonitor.cpp b/client/platforms/windows/daemon/windowsroutemonitor.cpp index fb0fbf7e..1d0ce4c2 100644 --- a/client/platforms/windows/daemon/windowsroutemonitor.cpp +++ b/client/platforms/windows/daemon/windowsroutemonitor.cpp @@ -303,8 +303,7 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) { data->Age++; continue; } - logger.debug() << "Capturing route to" - << logger.sensitive(prefix.toString()); + logger.debug() << "Capturing route to" << prefix.toString(); // Clone the route and direct it into the VPN tunnel. data = new MIB_IPFORWARD_ROW2; @@ -354,8 +353,7 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) { continue; } - logger.debug() << "Removing route capture for" - << logger.sensitive(i.key().toString()); + logger.debug() << "Removing route capture for" << i.key().toString(); // Otherwise, this route is no longer in use. DWORD result = DeleteIpForwardEntry2(data); @@ -368,8 +366,7 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) { } bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) { - logger.debug() << "Adding exclusion route for" - << logger.sensitive(prefix.toString()); + logger.debug() << "Adding exclusion route for" << prefix.toString(); // Silently ignore non-routeable addresses. QHostAddress addr = prefix.address(); @@ -437,7 +434,7 @@ bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) { bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) { logger.debug() << "Deleting exclusion route for" - << logger.sensitive(prefix.address().toString()); + << prefix.address().toString(); MIB_IPFORWARD_ROW2* data = m_exclusionRoutes.take(prefix); if (data == nullptr) { @@ -447,7 +444,7 @@ bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) { DWORD result = DeleteIpForwardEntry2(data); if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) { logger.error() << "Failed to delete route to" - << logger.sensitive(prefix.toString()) + << prefix.toString() << "result:" << result; } @@ -465,7 +462,7 @@ void WindowsRouteMonitor::flushRouteTable( DWORD result = DeleteIpForwardEntry2(data); if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) { logger.error() << "Failed to delete route to" - << logger.sensitive(i.key().toString()) + << i.key().toString() << "result:" << result; } delete data; diff --git a/client/translations/amneziavpn_ru_RU.ts b/client/translations/amneziavpn_ru_RU.ts index 14de59d1..1d8766ac 100644 --- a/client/translations/amneziavpn_ru_RU.ts +++ b/client/translations/amneziavpn_ru_RU.ts @@ -89,17 +89,17 @@ ApiConfigsController - + %1 installed successfully. %1 успешно установлен. - + API config reloaded Конфигурация API перезагружена - + Successfully changed the country of connection to %1 Страна подключения изменена на %1 @@ -358,7 +358,7 @@ ContextMenuType - + C&ut Вырезать @@ -368,12 +368,12 @@ Копировать - + &Paste Вставить - + &SelectAll Выбрать всё @@ -2412,42 +2412,42 @@ Thank you for staying with us! Доступ в интернет блокируется при разрыве VPN-соединения - + Strict KillSwitch Strict KillSwitch - + Internet connection is blocked even when VPN is turned off manually or hasn't started Доступ в интернет блокируется, даже если VPN отключен вручную или не был запущен - + Just a little heads-up Небольшое предупреждение - + 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. Если VPN отключится или соединение прервётся при включённом Strict KillSwitch, доступ в интернет будет заблокирован. Чтобы восстановить доступ, снова подключитесь к VPN или отключите (измените) режим KillSwitch. - + Continue Продолжить - + Cancel Отменить - + DNS Exceptions Исключения для DNS - + DNS servers listed here will remain accessible when KillSwitch is active. DNS-серверы из этого списка останутся доступными при активном KillSwitch. @@ -4085,7 +4085,12 @@ Thank you for staying with us! Произошла ошибка миграции. Обратитесь в нашу техническую поддержку - + + Please update the application to use this feature + Пожалуйста, обновите приложение, чтобы использовать эту функцию + + + ErrorCode: %1. Код ошибки: %1. @@ -4196,37 +4201,37 @@ Thank you for staying with us! Произошла ошибка миграции. Обратитесь в нашу техническую поддержку - + QFile error: The file could not be opened Ошибка QFile: не удалось открыть файл - + QFile error: An error occurred when reading from the file Ошибка QFile: произошла ошибка при чтении из файла - + QFile error: The file could not be accessed Ошибка QFile: не удалось получить доступ к файлу - + QFile error: An unspecified error occurred Ошибка QFile: произошла неизвестная ошибка - + QFile error: A fatal error occurred Ошибка QFile: произошла фатальная ошибка - + QFile error: The operation was aborted Ошибка QFile: операция была прервана - + Internal error Внутренняя ошибка diff --git a/client/ui/Controls2 b/client/ui/Controls2 deleted file mode 100644 index 13f01bb7..00000000 --- a/client/ui/Controls2 +++ /dev/null @@ -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 - } -} diff --git a/client/ui/controllers/api/apiConfigsController.cpp b/client/ui/controllers/api/apiConfigsController.cpp index 21d371bb..1b70315a 100644 --- a/client/ui/controllers/api/apiConfigsController.cpp +++ b/client/ui/controllers/api/apiConfigsController.cpp @@ -77,6 +77,7 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode, apiPayload[configKey::serverCountryCode] = serverCountryCode; apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType); apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData); + apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION); QByteArray 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::serviceType] = apiConfigObject.value(configKey::serviceType); apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData); + apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION); QByteArray 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::serviceType] = serviceType; apiPayload[configKey::uuid] = installationUuid; + apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION); QByteArray 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::serviceType] = serviceType; apiPayload[configKey::uuid] = installationUuid; + apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION); if (!newCountryCode.isEmpty()) { apiPayload[configKey::serverCountryCode] = newCountryCode; @@ -330,6 +334,7 @@ bool ApiConfigsController::deactivateDevice() apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType); apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData); apiPayload[configKey::uuid] = m_settings->getInstallationUuid(true); + apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION); QByteArray responseBody; ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody); @@ -366,6 +371,7 @@ bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const Q apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType); apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData); apiPayload[configKey::uuid] = uuid; + apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION); QByteArray responseBody; ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody); diff --git a/client/ui/controllers/api/apiSettingsController.cpp b/client/ui/controllers/api/apiSettingsController.cpp index f20f92bf..c4a75a5b 100644 --- a/client/ui/controllers/api/apiSettingsController.cpp +++ b/client/ui/controllers/api/apiSettingsController.cpp @@ -5,6 +5,7 @@ #include "core/api/apiUtils.h" #include "core/controllers/gatewayController.h" +#include "version.h" namespace { @@ -60,6 +61,7 @@ bool ApiSettingsController::getAccountInfo(bool reload) apiPayload[configKey::userCountryCode] = apiConfig.value(configKey::userCountryCode).toString(); apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString(); apiPayload[configKey::authData] = authData; + apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION); QByteArray responseBody;