Compare commits
8 Commits
dev
...
fix/linux-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a472c6793 | ||
|
|
9bf974a2f4 | ||
|
|
46100fd311 | ||
|
|
34b62f8fd6 | ||
|
|
b8e9ec8570 | ||
|
|
c35988d79f | ||
|
|
410538c6b2 | ||
|
|
cc71330c27 |
82
.github/workflows/deploy.yml
vendored
@@ -745,37 +745,11 @@ jobs:
|
||||
needs: Bake-Prebuilts-Android
|
||||
if: ${{ always() }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- label: 'android9-10'
|
||||
qt_version: '6.10.1'
|
||||
min_sdk: 28
|
||||
max_sdk: 29
|
||||
version_code_offset: 0
|
||||
build_aab: true
|
||||
- label: 'android11+'
|
||||
qt_version: '6.10.3'
|
||||
min_sdk: 30
|
||||
max_sdk: ''
|
||||
version_code_offset: 1
|
||||
build_aab: true
|
||||
- label: 'android9-10-qt610-3'
|
||||
qt_version: '6.10.3'
|
||||
min_sdk: 28
|
||||
max_sdk: 29
|
||||
version_code_offset: 0
|
||||
build_aab: false
|
||||
|
||||
env:
|
||||
ANDROID_PLATFORM: android-${{ matrix.min_sdk }}
|
||||
ANDROID_PLATFORM: android-28
|
||||
NDK_VERSION: 27.0.11718014
|
||||
QT_VERSION: ${{ matrix.qt_version }}
|
||||
QT_VERSION: 6.10.3
|
||||
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
||||
APP_ANDROID_MIN_SDK: ${{ matrix.min_sdk }}
|
||||
APP_ANDROID_MAX_SDK: ${{ matrix.max_sdk }}
|
||||
APP_ANDROID_VERSION_CODE_OFFSET: ${{ matrix.version_code_offset }}
|
||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||
FALLBACK_S3_ENDPOINT: ${{ secrets.FALLBACK_S3_ENDPOINT }}
|
||||
@@ -883,82 +857,62 @@ jobs:
|
||||
QT_ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android.keystore
|
||||
QT_ANDROID_KEYSTORE_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }}
|
||||
QT_ANDROID_KEYSTORE_STORE_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
|
||||
LABEL: ${{ matrix.label }}
|
||||
BUILD_AAB: ${{ matrix.build_aab }}
|
||||
shell: bash
|
||||
run: |
|
||||
OUT=deploy/artifacts
|
||||
rm -rf $OUT && mkdir -p $OUT
|
||||
|
||||
if [[ "$BUILD_AAB" == "true" ]]; then
|
||||
deploy/build.sh -t android --sign --aab
|
||||
else
|
||||
deploy/build.sh -t android --sign
|
||||
fi
|
||||
deploy/build.sh -t android --sign --aab
|
||||
|
||||
VERSION=$(grep CMAKE_PROJECT_VERSION:STATIC deploy/build/CMakeCache.txt | cut -d= -f2)
|
||||
|
||||
mv deploy/build/client/android-build/AmneziaVPN.apk \
|
||||
$OUT/AmneziaVPN_${VERSION}_${LABEL}_universal.apk
|
||||
|
||||
if [[ "$BUILD_AAB" == "true" ]]; then
|
||||
mv deploy/build/client/android-build/build/outputs/bundle/release/android-build-release.aab \
|
||||
$OUT/AmneziaVPN_${VERSION}_${LABEL}.aab
|
||||
fi
|
||||
(cd deploy/build/client/android-build && mv AmneziaVPN.apk AmneziaVPN_${VERSION}_android9+_universal.apk)
|
||||
(cd deploy/build/client/android-build/build/outputs/bundle/release && mv android-build-release.aab AmneziaVPN_${VERSION}.aab)
|
||||
|
||||
for abi in arm64-v8a armeabi-v7a x86 x86_64; do
|
||||
deploy/build.sh -t android --sign --abi ${abi} --build ./deploy/build/${abi}
|
||||
mv deploy/build/${abi}/client/android-build/AmneziaVPN.apk \
|
||||
$OUT/AmneziaVPN_${VERSION}_${LABEL}_${abi}.apk
|
||||
(cd deploy/build/${abi}/client/android-build && mv AmneziaVPN.apk AmneziaVPN_${VERSION}_android9+_${abi}.apk)
|
||||
done
|
||||
|
||||
ls -l $OUT
|
||||
|
||||
- name: 'Upload universal APK'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: deploy/artifacts/*_universal.apk
|
||||
path: deploy/build/client/android-build/*.apk
|
||||
archive: false
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Upload AAB'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: deploy/build/client/android-build/build/outputs/bundle/release/*.aab
|
||||
archive: false
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Upload arm64-v8a APK'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: deploy/artifacts/*_arm64-v8a.apk
|
||||
path: deploy/build/arm64-v8a/client/android-build/*.apk
|
||||
archive: false
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Upload armeabi-v7a APK'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: deploy/artifacts/*_armeabi-v7a.apk
|
||||
path: deploy/build/armeabi-v7a/client/android-build/*.apk
|
||||
archive: false
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Upload x86 APK'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: deploy/artifacts/*_x86.apk
|
||||
path: deploy/build/x86/client/android-build/*.apk
|
||||
archive: false
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Upload x86_64 APK'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: deploy/artifacts/*_x86_64.apk
|
||||
path: deploy/build/x86_64/client/android-build/*.apk
|
||||
archive: false
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Upload AAB'
|
||||
if: ${{ matrix.build_aab }}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: deploy/artifacts/*.aab
|
||||
archive: false
|
||||
retention-days: 7
|
||||
|
||||
# ------------------------------------------------------
|
||||
|
||||
Extra:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
@@ -4,7 +4,7 @@ set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
set(AMNEZIAVPN_VERSION 5.0.1.1)
|
||||
set(AMNEZIAVPN_VERSION 5.0.1.0)
|
||||
|
||||
set(QT_CREATOR_SKIP_PACKAGE_MANAGER_SETUP ON CACHE BOOL "" FORCE)
|
||||
set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES
|
||||
@@ -28,12 +28,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})
|
||||
|
||||
# bump by 2 on every release, because we're releasing two versions on the play store
|
||||
set(APP_ANDROID_VERSION_CODE 2144)
|
||||
if(DEFINED APP_ANDROID_VERSION_CODE_OFFSET)
|
||||
math(EXPR APP_ANDROID_VERSION_CODE "${APP_ANDROID_VERSION_CODE} + ${APP_ANDROID_VERSION_CODE_OFFSET}")
|
||||
endif()
|
||||
set(APP_ANDROID_VERSION_CODE 2141)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
|
||||
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 35 KiB |
@@ -1,8 +1,6 @@
|
||||
message("Client android ${CMAKE_ANDROID_ARCH_ABI} build")
|
||||
|
||||
if(NOT DEFINED APP_ANDROID_MIN_SDK)
|
||||
set(APP_ANDROID_MIN_SDK 28)
|
||||
endif()
|
||||
set(APP_ANDROID_MIN_SDK 28)
|
||||
set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING
|
||||
"The minimum API level supported by the application or library" FORCE)
|
||||
|
||||
@@ -15,6 +13,7 @@ set_target_properties(${PROJECT} PROPERTIES
|
||||
QT_ANDROID_MIN_SDK_VERSION ${APP_ANDROID_MIN_SDK}
|
||||
QT_ANDROID_TARGET_SDK_VERSION 36
|
||||
QT_ANDROID_SDK_BUILD_TOOLS_REVISION 36.0.0
|
||||
QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android
|
||||
)
|
||||
|
||||
set(QT_ANDROID_MULTI_ABI_FORWARD_VARS "QT_NO_GLOBAL_APK_TARGET_PART_OF_ALL;CMAKE_BUILD_TYPE")
|
||||
@@ -54,27 +53,3 @@ file(COPY ${AMNEZIA_LIBXRAY_PATH} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/androi
|
||||
find_package(openvpn-pt-android REQUIRED)
|
||||
set(LIBS ${LIBS} amnezia::openvpn-pt-android)
|
||||
set_property(TARGET ${PROJECT} APPEND PROPERTY QT_ANDROID_EXTRA_LIBS ${OPENVPN_PT_ANDROID_LIBCK_OVPN_PLUGIN_PATH})
|
||||
|
||||
set(APP_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android)
|
||||
|
||||
if(APP_ANDROID_MAX_SDK)
|
||||
set(APP_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/android-package-source)
|
||||
file(REMOVE_RECURSE ${APP_ANDROID_PACKAGE_SOURCE_DIR})
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/android/ DESTINATION ${APP_ANDROID_PACKAGE_SOURCE_DIR})
|
||||
|
||||
set(manifest_path ${APP_ANDROID_PACKAGE_SOURCE_DIR}/AndroidManifest.xml)
|
||||
set(manifest_anchor "android:installLocation=\"auto\">")
|
||||
file(READ ${manifest_path} manifest_contents)
|
||||
string(REPLACE
|
||||
"${manifest_anchor}"
|
||||
"${manifest_anchor}\n\n <uses-sdk android:maxSdkVersion=\"${APP_ANDROID_MAX_SDK}\" />"
|
||||
patched_contents "${manifest_contents}")
|
||||
if(patched_contents STREQUAL manifest_contents)
|
||||
message(FATAL_ERROR
|
||||
"Failed to set maxSdkVersion=${APP_ANDROID_MAX_SDK}: anchor '${manifest_anchor}' "
|
||||
"not found in ${CMAKE_CURRENT_SOURCE_DIR}/android/AndroidManifest.xml")
|
||||
endif()
|
||||
file(WRITE ${manifest_path} "${patched_contents}")
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${PROJECT} PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR ${APP_ANDROID_PACKAGE_SOURCE_DIR})
|
||||
|
||||
@@ -64,7 +64,6 @@ set(HEADERS ${HEADERS}
|
||||
${CLIENT_ROOT_DIR}/../common/crypto/cryptoUtils.h
|
||||
${CLIENT_ROOT_DIR}/ui/utils/qmlUtils.h
|
||||
${CLIENT_ROOT_DIR}/core/utils/api/apiUtils.h
|
||||
${CLIENT_ROOT_DIR}/core/utils/api/gatewayPayloadBuilder.h
|
||||
${CLIENT_ROOT_DIR}/core/utils/osSignalHandler.h
|
||||
${CLIENT_ROOT_DIR}/core/utils/utilities.h
|
||||
${CLIENT_ROOT_DIR}/core/utils/managementServer.h
|
||||
@@ -148,7 +147,6 @@ set(SOURCES ${SOURCES}
|
||||
${CLIENT_ROOT_DIR}/../common/crypto/cryptoUtils.cpp
|
||||
${CLIENT_ROOT_DIR}/ui/utils/qmlUtils.cpp
|
||||
${CLIENT_ROOT_DIR}/core/utils/api/apiUtils.cpp
|
||||
${CLIENT_ROOT_DIR}/core/utils/api/gatewayPayloadBuilder.cpp
|
||||
${CLIENT_ROOT_DIR}/core/utils/serverConfigUtils.cpp
|
||||
${CLIENT_ROOT_DIR}/core/utils/osSignalHandler.cpp
|
||||
${CLIENT_ROOT_DIR}/core/utils/utilities.cpp
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
#include "core/controllers/gatewayController.h"
|
||||
#include "core/repositories/secureServersRepository.h"
|
||||
#include "core/utils/api/apiUtils.h"
|
||||
#include "core/utils/api/gatewayPayloadBuilder.h"
|
||||
#include "core/utils/constants/apiKeys.h"
|
||||
#include "core/utils/constants/apiConstants.h"
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
@@ -83,12 +81,15 @@ QFuture<QPair<ErrorCode, QJsonArray>> NewsController::fetchNews()
|
||||
m_appSettingsRepository->isStrictKillSwitchEnabled(),
|
||||
m_appSettingsRepository);
|
||||
|
||||
// both country codes and service types are arrays here, one entry per gateway stack the user has
|
||||
const QJsonObject payload = GatewayPayloadBuilder(m_appSettingsRepository)
|
||||
.addField(apiDefs::key::locale, apiUtils::getAppLanguageCode(m_appSettingsRepository))
|
||||
.addField(apiDefs::key::userCountryCode, services.value(apiDefs::key::userCountryCode))
|
||||
.addField(apiDefs::key::serviceType, services.value(apiDefs::key::serviceType))
|
||||
.build();
|
||||
QJsonObject payload;
|
||||
payload.insert("locale", m_appSettingsRepository->getAppLanguage().name().split("_").first());
|
||||
|
||||
if (services.contains(apiDefs::key::userCountryCode)) {
|
||||
payload.insert(apiDefs::key::userCountryCode, services.value(apiDefs::key::userCountryCode));
|
||||
}
|
||||
if (services.contains(apiDefs::key::serviceType)) {
|
||||
payload.insert(apiDefs::key::serviceType, services.value(apiDefs::key::serviceType));
|
||||
}
|
||||
|
||||
auto future = gatewayController->postAsync(QString("%1v1/news"), payload);
|
||||
return future.then([gatewayController](QPair<ErrorCode, QByteArray> result) -> QPair<ErrorCode, QJsonArray> {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "servicesCatalogController.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QSysInfo>
|
||||
#include <QJsonArray>
|
||||
#include <QEventLoop>
|
||||
#include <QDebug>
|
||||
@@ -10,7 +11,6 @@
|
||||
#include <limits>
|
||||
|
||||
#include "core/controllers/gatewayController.h"
|
||||
#include "core/utils/api/gatewayPayloadBuilder.h"
|
||||
#include "core/utils/serverConfigUtils.h"
|
||||
#include "core/utils/constants/apiKeys.h"
|
||||
#include "core/utils/constants/apiConstants.h"
|
||||
@@ -212,9 +212,11 @@ ServicesCatalogController::ServicesCatalogController(SecureAppSettingsRepository
|
||||
|
||||
ErrorCode ServicesCatalogController::fillAvailableServices(QJsonObject &servicesData)
|
||||
{
|
||||
QJsonObject apiPayload = GatewayPayloadBuilder(m_appSettingsRepository)
|
||||
.addField(apiDefs::key::cliName, QString(APPLICATION_NAME))
|
||||
.build();
|
||||
QJsonObject apiPayload;
|
||||
apiPayload[apiDefs::key::osVersion] = QSysInfo::productType();
|
||||
apiPayload[apiDefs::key::appVersion] = QString(APP_VERSION);
|
||||
apiPayload[apiDefs::key::cliName] = QString(APPLICATION_NAME);
|
||||
apiPayload[apiDefs::key::appLanguage] = m_appSettingsRepository->getAppLanguage().name().split("_").first();
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/services"), apiPayload, responseBody);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <QJsonObject>
|
||||
#include <QPromise>
|
||||
#include <QSet>
|
||||
#include <QSysInfo>
|
||||
#include <QUuid>
|
||||
#include <QVariantMap>
|
||||
|
||||
@@ -20,7 +21,6 @@
|
||||
#include "core/utils/constants/apiKeys.h"
|
||||
#include "core/utils/constants/apiConstants.h"
|
||||
#include "core/utils/api/apiUtils.h"
|
||||
#include "core/utils/api/gatewayPayloadBuilder.h"
|
||||
#include "core/controllers/gatewayController.h"
|
||||
#include "core/utils/protocolEnum.h"
|
||||
#include "core/protocols/protocolUtils.h"
|
||||
@@ -97,6 +97,39 @@ SubscriptionController::SubscriptionController(SecureServersRepository* serversR
|
||||
{
|
||||
}
|
||||
|
||||
QJsonObject SubscriptionController::GatewayRequestData::toJsonObject() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
if (!osVersion.isEmpty()) {
|
||||
obj[apiDefs::key::osVersion] = osVersion;
|
||||
}
|
||||
if (!appVersion.isEmpty()) {
|
||||
obj[apiDefs::key::appVersion] = appVersion;
|
||||
}
|
||||
if (!appLanguage.isEmpty()) {
|
||||
obj[apiDefs::key::appLanguage] = appLanguage;
|
||||
}
|
||||
if (!installationUuid.isEmpty()) {
|
||||
obj[apiDefs::key::uuid] = installationUuid;
|
||||
}
|
||||
if (!userCountryCode.isEmpty()) {
|
||||
obj[apiDefs::key::userCountryCode] = userCountryCode;
|
||||
}
|
||||
if (!serverCountryCode.isEmpty()) {
|
||||
obj[apiDefs::key::serverCountryCode] = serverCountryCode;
|
||||
}
|
||||
if (!serviceType.isEmpty()) {
|
||||
obj[apiDefs::key::serviceType] = serviceType;
|
||||
}
|
||||
if (!serviceProtocol.isEmpty()) {
|
||||
obj[apiDefs::key::serviceProtocol] = serviceProtocol;
|
||||
}
|
||||
if (!authData.isEmpty()) {
|
||||
obj[apiDefs::key::authData] = authData;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
SubscriptionController::ProtocolData SubscriptionController::generateProtocolData(const QString &protocol)
|
||||
{
|
||||
ProtocolData protocolData;
|
||||
@@ -111,15 +144,13 @@ SubscriptionController::ProtocolData SubscriptionController::generateProtocolDat
|
||||
return protocolData;
|
||||
}
|
||||
|
||||
QString SubscriptionController::publicKeyForProtocol(const QString &protocol, const ProtocolData &protocolData)
|
||||
void SubscriptionController::appendProtocolDataToApiPayload(const QString &protocol, const ProtocolData &protocolData, QJsonObject &apiPayload)
|
||||
{
|
||||
if (protocol == configKey::awg) {
|
||||
return protocolData.wireGuardClientPubKey;
|
||||
apiPayload[apiDefs::key::publicKey] = protocolData.wireGuardClientPubKey;
|
||||
} else if (protocol == configKey::vless) {
|
||||
apiPayload[apiDefs::key::publicKey] = protocolData.xrayUuid;
|
||||
}
|
||||
if (protocol == configKey::vless) {
|
||||
return protocolData.xrayUuid;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorCode SubscriptionController::extractServerConfigJsonFromResponse(const QByteArray &apiResponseBody, const QString &protocol,
|
||||
@@ -209,12 +240,18 @@ ErrorCode SubscriptionController::importServiceFromGateway(const QString &userCo
|
||||
const QString &serviceProtocol, const ProtocolData &protocolData,
|
||||
CaptchaInfo &captchaInfo)
|
||||
{
|
||||
QJsonObject apiPayload = GatewayPayloadBuilder(m_appSettingsRepository)
|
||||
.addField(apiDefs::key::userCountryCode, userCountryCode)
|
||||
.addField(apiDefs::key::serviceType, serviceType)
|
||||
.addField(apiDefs::key::serviceProtocol, serviceProtocol)
|
||||
.addField(apiDefs::key::publicKey, publicKeyForProtocol(serviceProtocol, protocolData))
|
||||
.build();
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_appSettingsRepository->getAppLanguage().name().split("_").first(),
|
||||
m_appSettingsRepository->getInstallationUuid(true),
|
||||
userCountryCode,
|
||||
"",
|
||||
serviceType,
|
||||
serviceProtocol,
|
||||
QJsonObject() };
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody);
|
||||
@@ -254,15 +291,20 @@ ErrorCode SubscriptionController::importTrialFromGateway(const QString &userCoun
|
||||
return ErrorCode::ApiConfigEmptyError;
|
||||
}
|
||||
|
||||
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_appSettingsRepository->getAppLanguage().name().split("_").first(),
|
||||
m_appSettingsRepository->getInstallationUuid(true),
|
||||
userCountryCode,
|
||||
"",
|
||||
serviceType,
|
||||
serviceProtocol,
|
||||
QJsonObject() };
|
||||
|
||||
QJsonObject apiPayload = GatewayPayloadBuilder(m_appSettingsRepository)
|
||||
.addField(apiDefs::key::userCountryCode, userCountryCode)
|
||||
.addField(apiDefs::key::serviceType, serviceType)
|
||||
.addField(apiDefs::key::serviceProtocol, serviceProtocol)
|
||||
.addField(apiDefs::key::publicKey, publicKeyForProtocol(serviceProtocol, protocolData))
|
||||
.addField(apiDefs::key::email, trimmedEmail)
|
||||
.build();
|
||||
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload);
|
||||
apiPayload.insert(apiDefs::key::email, trimmedEmail);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/trial"), apiPayload, responseBody);
|
||||
@@ -303,13 +345,19 @@ ErrorCode SubscriptionController::importServiceFromAppStore(const QString &userC
|
||||
const QString &transactionId, bool isTestPurchase,
|
||||
int *duplicateServerIndex)
|
||||
{
|
||||
QJsonObject apiPayload = GatewayPayloadBuilder(m_appSettingsRepository)
|
||||
.addField(apiDefs::key::userCountryCode, userCountryCode)
|
||||
.addField(apiDefs::key::serviceType, serviceType)
|
||||
.addField(apiDefs::key::serviceProtocol, serviceProtocol)
|
||||
.addField(apiDefs::key::publicKey, publicKeyForProtocol(serviceProtocol, protocolData))
|
||||
.addField(apiDefs::key::transactionId, transactionId)
|
||||
.build();
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_appSettingsRepository->getAppLanguage().name().split("_").first(),
|
||||
m_appSettingsRepository->getInstallationUuid(true),
|
||||
userCountryCode,
|
||||
"",
|
||||
serviceType,
|
||||
serviceProtocol,
|
||||
QJsonObject() };
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload);
|
||||
apiPayload[apiDefs::key::transactionId] = transactionId;
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/subscriptions"), apiPayload, responseBody, isTestPurchase);
|
||||
@@ -403,15 +451,23 @@ ErrorCode SubscriptionController::updateServiceFromGateway(const QString &server
|
||||
|
||||
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
||||
|
||||
QJsonObject apiPayload = GatewayPayloadBuilder(m_appSettingsRepository)
|
||||
.addField(apiDefs::key::userCountryCode, apiV2->apiConfig.userCountryCode)
|
||||
.addField(apiDefs::key::serverCountryCode, newCountryCode)
|
||||
.addField(apiDefs::key::serviceType, apiV2->serviceType())
|
||||
.addField(apiDefs::key::serviceProtocol, serviceProtocol)
|
||||
.addField(apiDefs::key::publicKey, publicKeyForProtocol(serviceProtocol, protocolData))
|
||||
.addField(apiDefs::key::authData, apiV2->authData.toJson())
|
||||
.addField(apiDefs::key::isConnectEvent, isConnectEvent ? QJsonValue(true) : QJsonValue())
|
||||
.build();
|
||||
QJsonObject authDataJson = apiV2->authData.toJson();
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_appSettingsRepository->getAppLanguage().name().split("_").first(),
|
||||
m_appSettingsRepository->getInstallationUuid(true),
|
||||
apiV2->apiConfig.userCountryCode,
|
||||
newCountryCode,
|
||||
apiV2->serviceType(),
|
||||
serviceProtocol,
|
||||
authDataJson };
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload);
|
||||
|
||||
if (isConnectEvent) {
|
||||
apiPayload[apiDefs::key::isConnectEvent] = true;
|
||||
}
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody, isTestPurchase);
|
||||
@@ -487,17 +543,26 @@ ErrorCode SubscriptionController::resolveUpdateServiceCaptcha(const QString &ser
|
||||
const bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
||||
QString serviceProtocol = apiV2->serviceProtocol();
|
||||
|
||||
QJsonObject apiPayload = GatewayPayloadBuilder(m_appSettingsRepository)
|
||||
.addField(apiDefs::key::userCountryCode, apiV2->apiConfig.userCountryCode)
|
||||
.addField(apiDefs::key::serverCountryCode, newCountryCode)
|
||||
.addField(apiDefs::key::serviceType, apiV2->serviceType())
|
||||
.addField(apiDefs::key::serviceProtocol, serviceProtocol)
|
||||
.addField(apiDefs::key::publicKey, publicKeyForProtocol(serviceProtocol, protocolData))
|
||||
.addField(apiDefs::key::authData, apiV2->authData.toJson())
|
||||
.addField(apiDefs::key::captchaId, captchaId)
|
||||
.addField(apiDefs::key::captchaSolution, normalizeCaptchaSolution(captchaSolution))
|
||||
.addField(apiDefs::key::isConnectEvent, isConnectEvent ? QJsonValue(true) : QJsonValue())
|
||||
.build();
|
||||
QJsonObject authDataJson = apiV2->authData.toJson();
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_appSettingsRepository->getAppLanguage().name().split("_").first(),
|
||||
m_appSettingsRepository->getInstallationUuid(true),
|
||||
apiV2->apiConfig.userCountryCode,
|
||||
newCountryCode,
|
||||
apiV2->serviceType(),
|
||||
serviceProtocol,
|
||||
authDataJson };
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload);
|
||||
|
||||
if (isConnectEvent) {
|
||||
apiPayload[apiDefs::key::isConnectEvent] = true;
|
||||
}
|
||||
|
||||
apiPayload["captcha_id"] = captchaId;
|
||||
apiPayload["captcha_solution"] = normalizeCaptchaSolution(captchaSolution);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody, isTestPurchase);
|
||||
@@ -530,12 +595,18 @@ ErrorCode SubscriptionController::deactivateDevice(const QString &serverId)
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
QJsonObject apiPayload = GatewayPayloadBuilder(m_appSettingsRepository)
|
||||
.addField(apiDefs::key::userCountryCode, apiV2->apiConfig.userCountryCode)
|
||||
.addField(apiDefs::key::serverCountryCode, apiV2->apiConfig.serverCountryCode)
|
||||
.addField(apiDefs::key::serviceType, apiV2->serviceType())
|
||||
.addField(apiDefs::key::authData, apiV2->authData.toJson())
|
||||
.build();
|
||||
QJsonObject authDataJson = apiV2->authData.toJson();
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_appSettingsRepository->getAppLanguage().name().split("_").first(),
|
||||
m_appSettingsRepository->getInstallationUuid(true),
|
||||
apiV2->apiConfig.userCountryCode,
|
||||
apiV2->apiConfig.serverCountryCode,
|
||||
apiV2->serviceType(),
|
||||
"",
|
||||
authDataJson };
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
|
||||
const bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
||||
QByteArray responseBody;
|
||||
@@ -561,13 +632,18 @@ ErrorCode SubscriptionController::deactivateExternalDevice(const QString &server
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
QJsonObject apiPayload = GatewayPayloadBuilder(m_appSettingsRepository)
|
||||
.addField(apiDefs::key::installationUuid, uuid)
|
||||
.addField(apiDefs::key::userCountryCode, apiV2->apiConfig.userCountryCode)
|
||||
.addField(apiDefs::key::serverCountryCode, serverCountryCode)
|
||||
.addField(apiDefs::key::serviceType, apiV2->serviceType())
|
||||
.addField(apiDefs::key::authData, apiV2->authData.toJson())
|
||||
.build();
|
||||
QJsonObject authDataJson = apiV2->authData.toJson();
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_appSettingsRepository->getAppLanguage().name().split("_").first(),
|
||||
uuid,
|
||||
apiV2->apiConfig.userCountryCode,
|
||||
serverCountryCode,
|
||||
apiV2->serviceType(),
|
||||
"",
|
||||
authDataJson };
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
|
||||
const bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
||||
QByteArray responseBody;
|
||||
@@ -595,14 +671,19 @@ ErrorCode SubscriptionController::exportNativeConfig(const QString &serverId, co
|
||||
QString protocol = configKey::awg;
|
||||
ProtocolData protocolData = generateProtocolData(protocol);
|
||||
|
||||
QJsonObject apiPayload = GatewayPayloadBuilder(m_appSettingsRepository)
|
||||
.addField(apiDefs::key::userCountryCode, apiV2->apiConfig.userCountryCode)
|
||||
.addField(apiDefs::key::serverCountryCode, serverCountryCode)
|
||||
.addField(apiDefs::key::serviceType, apiV2->serviceType())
|
||||
.addField(apiDefs::key::serviceProtocol, protocol)
|
||||
.addField(apiDefs::key::publicKey, publicKeyForProtocol(protocol, protocolData))
|
||||
.addField(apiDefs::key::authData, apiV2->authData.toJson())
|
||||
.build();
|
||||
QJsonObject authDataJson = apiV2->authData.toJson();
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_appSettingsRepository->getAppLanguage().name().split("_").first(),
|
||||
m_appSettingsRepository->getInstallationUuid(true),
|
||||
apiV2->apiConfig.userCountryCode,
|
||||
serverCountryCode,
|
||||
apiV2->serviceType(),
|
||||
protocol,
|
||||
authDataJson };
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
appendProtocolDataToApiPayload(protocol, protocolData, apiPayload);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/native_config"), apiPayload, responseBody, isTestPurchase);
|
||||
@@ -625,13 +706,18 @@ ErrorCode SubscriptionController::revokeNativeConfig(const QString &serverId, co
|
||||
const bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
||||
QString protocol = configKey::awg;
|
||||
|
||||
QJsonObject apiPayload = GatewayPayloadBuilder(m_appSettingsRepository)
|
||||
.addField(apiDefs::key::userCountryCode, apiV2->apiConfig.userCountryCode)
|
||||
.addField(apiDefs::key::serverCountryCode, serverCountryCode)
|
||||
.addField(apiDefs::key::serviceType, apiV2->serviceType())
|
||||
.addField(apiDefs::key::serviceProtocol, protocol)
|
||||
.addField(apiDefs::key::authData, apiV2->authData.toJson())
|
||||
.build();
|
||||
QJsonObject authDataJson = apiV2->authData.toJson();
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_appSettingsRepository->getAppLanguage().name().split("_").first(),
|
||||
m_appSettingsRepository->getInstallationUuid(true),
|
||||
apiV2->apiConfig.userCountryCode,
|
||||
serverCountryCode,
|
||||
apiV2->serviceType(),
|
||||
protocol,
|
||||
authDataJson };
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/revoke_native_config"), apiPayload, responseBody, isTestPurchase);
|
||||
@@ -938,13 +1024,20 @@ ErrorCode SubscriptionController::getAccountInfo(const QString &serverId, QJsonO
|
||||
}
|
||||
bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
||||
|
||||
QJsonObject apiPayload = GatewayPayloadBuilder(m_appSettingsRepository)
|
||||
.addField(apiDefs::key::userCountryCode, apiV2->apiConfig.userCountryCode)
|
||||
.addField(apiDefs::key::serviceType, apiV2->serviceType())
|
||||
.addField(apiDefs::key::authData, apiV2->authData.toJson())
|
||||
.addField(apiDefs::key::cliVersion, QString(APP_VERSION))
|
||||
.addField(apiDefs::key::subscriptionStatus, getSubscriptionStatusForRenewal(apiV2->apiConfig))
|
||||
.build();
|
||||
QJsonObject authDataJson = apiV2->authData.toJson();
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_appSettingsRepository->getAppLanguage().name().split("_").first(),
|
||||
m_appSettingsRepository->getInstallationUuid(true),
|
||||
apiV2->apiConfig.userCountryCode,
|
||||
"",
|
||||
apiV2->serviceType(),
|
||||
"",
|
||||
authDataJson };
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||
apiPayload[apiDefs::key::subscriptionStatus] = getSubscriptionStatusForRenewal(apiV2->apiConfig);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/account_info"), apiPayload, responseBody, isTestPurchase);
|
||||
@@ -969,13 +1062,20 @@ QFuture<QPair<ErrorCode, QString>> SubscriptionController::getRenewalLink(const
|
||||
}
|
||||
|
||||
bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
||||
QJsonObject apiPayload = GatewayPayloadBuilder(m_appSettingsRepository)
|
||||
.addField(apiDefs::key::userCountryCode, apiV2->apiConfig.userCountryCode)
|
||||
.addField(apiDefs::key::serviceType, apiV2->serviceType())
|
||||
.addField(apiDefs::key::authData, apiV2->authData.toJson())
|
||||
.addField(apiDefs::key::cliVersion, QString(APP_VERSION))
|
||||
.addField(apiDefs::key::subscriptionStatus, getSubscriptionStatusForRenewal(apiV2->apiConfig))
|
||||
.build();
|
||||
QJsonObject authDataJson = apiV2->authData.toJson();
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_appSettingsRepository->getAppLanguage().name().split("_").first(),
|
||||
m_appSettingsRepository->getInstallationUuid(true),
|
||||
apiV2->apiConfig.userCountryCode,
|
||||
"",
|
||||
apiV2->serviceType(),
|
||||
"",
|
||||
authDataJson };
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||
apiPayload[apiDefs::key::subscriptionStatus] = getSubscriptionStatusForRenewal(apiV2->apiConfig);
|
||||
|
||||
auto gatewayController = QSharedPointer<GatewayController>::create(m_appSettingsRepository->getGatewayEndpoint(isTestPurchase),
|
||||
m_appSettingsRepository->isDevGatewayEnv(isTestPurchase),
|
||||
@@ -1011,14 +1111,21 @@ ErrorCode SubscriptionController::resolveImportServiceCaptcha(const QString &use
|
||||
const QString &captchaSolution,
|
||||
CaptchaInfo *retryCaptchaOut)
|
||||
{
|
||||
QJsonObject apiPayload = GatewayPayloadBuilder(m_appSettingsRepository)
|
||||
.addField(apiDefs::key::userCountryCode, userCountryCode)
|
||||
.addField(apiDefs::key::serviceType, serviceType)
|
||||
.addField(apiDefs::key::serviceProtocol, serviceProtocol)
|
||||
.addField(apiDefs::key::publicKey, publicKeyForProtocol(serviceProtocol, protocolData))
|
||||
.addField(apiDefs::key::captchaId, captchaId)
|
||||
.addField(apiDefs::key::captchaSolution, normalizeCaptchaSolution(captchaSolution))
|
||||
.build();
|
||||
GatewayRequestData gatewayRequestData{QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_appSettingsRepository->getAppLanguage().name().split("_").first(),
|
||||
m_appSettingsRepository->getInstallationUuid(true),
|
||||
userCountryCode,
|
||||
"",
|
||||
serviceType,
|
||||
serviceProtocol,
|
||||
QJsonObject()};
|
||||
|
||||
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||
appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload);
|
||||
|
||||
apiPayload["captcha_id"] = captchaId;
|
||||
apiPayload["captcha_solution"] = normalizeCaptchaSolution(captchaSolution);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody);
|
||||
|
||||
@@ -27,6 +27,21 @@ public:
|
||||
QString xrayUuid;
|
||||
};
|
||||
|
||||
struct GatewayRequestData
|
||||
{
|
||||
QString osVersion;
|
||||
QString appVersion;
|
||||
QString appLanguage;
|
||||
QString installationUuid;
|
||||
QString userCountryCode;
|
||||
QString serverCountryCode;
|
||||
QString serviceType;
|
||||
QString serviceProtocol;
|
||||
QJsonObject authData;
|
||||
|
||||
QJsonObject toJsonObject() const;
|
||||
};
|
||||
|
||||
struct CaptchaInfo {
|
||||
QString captchaId;
|
||||
QString captchaImageBase64;
|
||||
@@ -38,8 +53,7 @@ public:
|
||||
SecureAppSettingsRepository* appSettingsRepository);
|
||||
|
||||
ProtocolData generateProtocolData(const QString &protocol);
|
||||
|
||||
static QString publicKeyForProtocol(const QString &protocol, const ProtocolData &protocolData);
|
||||
void appendProtocolDataToApiPayload(const QString &protocol, const ProtocolData &protocolData, QJsonObject &apiPayload);
|
||||
|
||||
ErrorCode importServiceFromGateway(const QString &userCountryCode, const QString &serviceType,
|
||||
const QString &serviceProtocol, const ProtocolData &protocolData,
|
||||
|
||||
@@ -154,13 +154,8 @@ QString IpSplitTunnelingController::normalizeHostname(const QString &hostname) c
|
||||
normalized.replace("https://", "");
|
||||
normalized.replace("http://", "");
|
||||
normalized.replace("ftp://", "");
|
||||
|
||||
if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(normalized)) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
const QStringList parts = normalized.split("/", Qt::SkipEmptyParts);
|
||||
return parts.isEmpty() ? QString() : parts.first();
|
||||
normalized = normalized.split("/", Qt::SkipEmptyParts).first();
|
||||
return normalized;
|
||||
}
|
||||
|
||||
bool IpSplitTunnelingController::validateHostname(const QString &hostname) const
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
#include <QUrl>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QSysInfo>
|
||||
#include <QTimer>
|
||||
|
||||
#include "amneziaApplication.h"
|
||||
#include "logger.h"
|
||||
#include "version.h"
|
||||
#include "core/controllers/gatewayController.h"
|
||||
#include "core/utils/api/gatewayPayloadBuilder.h"
|
||||
#include "core/utils/constants/apiKeys.h"
|
||||
#include "core/utils/selfhosted/scriptsRegistry.h"
|
||||
|
||||
@@ -98,9 +98,10 @@ void UpdateController::fetchGatewayUrl()
|
||||
m_appSettingsRepository->isStrictKillSwitchEnabled(),
|
||||
m_appSettingsRepository);
|
||||
|
||||
QJsonObject apiPayload = GatewayPayloadBuilder(m_appSettingsRepository)
|
||||
.addField(apiDefs::key::cliVersion, QString(APP_VERSION))
|
||||
.build();
|
||||
QJsonObject apiPayload;
|
||||
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||
apiPayload[apiDefs::key::osVersion] = QSysInfo::productType();
|
||||
apiPayload[apiDefs::key::installationUuid] = m_appSettingsRepository->getInstallationUuid(true);
|
||||
|
||||
// Workaround: wait before contacting gateway to avoid rate limit triggered by other requests (news etc.)
|
||||
QTimer::singleShot(1000, this, [this, gatewayController, apiPayload]() {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "apiUtils.h"
|
||||
|
||||
#include "core/repositories/secureAppSettingsRepository.h"
|
||||
#include "core/utils/serverConfigUtils.h"
|
||||
#include "core/utils/constants/configKeys.h"
|
||||
#include <QDateTime>
|
||||
@@ -49,19 +48,6 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
QString apiUtils::getAppLanguageCode(const SecureAppSettingsRepository *appSettingsRepository)
|
||||
{
|
||||
if (appSettingsRepository == nullptr) {
|
||||
return {};
|
||||
}
|
||||
return appSettingsRepository->getAppLanguage().name().split("_").first();
|
||||
}
|
||||
|
||||
QString apiUtils::getCountryFlagCode(const QString &serverCountryCode)
|
||||
{
|
||||
return serverCountryCode.section('-', 0, 0).toUpper();
|
||||
}
|
||||
|
||||
bool apiUtils::isSubscriptionExpired(const QString &subscriptionEndDate)
|
||||
{
|
||||
if (subscriptionEndDate.isEmpty()) {
|
||||
|
||||
@@ -11,17 +11,8 @@
|
||||
#include "core/utils/routeModes.h"
|
||||
#include "core/utils/commonStructs.h"
|
||||
|
||||
class SecureAppSettingsRepository;
|
||||
|
||||
namespace apiUtils
|
||||
{
|
||||
QString getAppLanguageCode(const SecureAppSettingsRepository *appSettingsRepository);
|
||||
|
||||
// The gateway may report a country code with a region suffix, e.g. "us-west", while flag
|
||||
// resources are named after the ISO 3166-1 alpha-2 code alone. Returns the part before the dash
|
||||
// in upper case.
|
||||
QString getCountryFlagCode(const QString &serverCountryCode);
|
||||
|
||||
bool isSubscriptionExpired(const QString &subscriptionEndDate);
|
||||
|
||||
bool isSubscriptionExpiringSoon(const QString &subscriptionEndDate, int withinDays = 30);
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
#include "gatewayPayloadBuilder.h"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QSysInfo>
|
||||
|
||||
#include "core/repositories/secureAppSettingsRepository.h"
|
||||
#include "core/utils/api/apiUtils.h"
|
||||
#include "core/utils/constants/apiKeys.h"
|
||||
#include "version.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
bool isEmptyValue(const QJsonValue &value)
|
||||
{
|
||||
switch (value.type()) {
|
||||
case QJsonValue::Null:
|
||||
case QJsonValue::Undefined:
|
||||
return true;
|
||||
case QJsonValue::String:
|
||||
return value.toString().isEmpty();
|
||||
case QJsonValue::Object:
|
||||
return value.toObject().isEmpty();
|
||||
case QJsonValue::Array:
|
||||
return value.toArray().isEmpty();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GatewayPayloadBuilder::GatewayPayloadBuilder(const SecureAppSettingsRepository *appSettingsRepository)
|
||||
{
|
||||
m_payload[apiDefs::key::osVersion] = QSysInfo::productType();
|
||||
m_payload[apiDefs::key::appVersion] = QString(APP_VERSION);
|
||||
|
||||
if (appSettingsRepository == nullptr) {
|
||||
qWarning() << "SecureAppSettingsRepository is null, skip app language and installation uuid";
|
||||
return;
|
||||
}
|
||||
|
||||
m_payload[apiDefs::key::appLanguage] = apiUtils::getAppLanguageCode(appSettingsRepository);
|
||||
m_payload[apiDefs::key::installationUuid] = appSettingsRepository->getInstallationUuid(true);
|
||||
}
|
||||
|
||||
GatewayPayloadBuilder &GatewayPayloadBuilder::addField(QLatin1String key, const QJsonValue &value)
|
||||
{
|
||||
if (!isEmptyValue(value)) {
|
||||
m_payload[key] = value;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
QJsonObject GatewayPayloadBuilder::build() const
|
||||
{
|
||||
return m_payload;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
#ifndef GATEWAYPAYLOADBUILDER_H
|
||||
#define GATEWAYPAYLOADBUILDER_H
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QLatin1String>
|
||||
|
||||
class SecureAppSettingsRepository;
|
||||
|
||||
class GatewayPayloadBuilder
|
||||
{
|
||||
public:
|
||||
explicit GatewayPayloadBuilder(const SecureAppSettingsRepository *appSettingsRepository);
|
||||
|
||||
GatewayPayloadBuilder &addField(QLatin1String key, const QJsonValue &value);
|
||||
QJsonObject build() const;
|
||||
|
||||
private:
|
||||
QJsonObject m_payload;
|
||||
};
|
||||
|
||||
#endif // GATEWAYPAYLOADBUILDER_H
|
||||
@@ -40,10 +40,6 @@ namespace apiDefs
|
||||
constexpr QLatin1String lastDownloaded("last_downloaded");
|
||||
constexpr QLatin1String sourceType("source_type");
|
||||
constexpr QLatin1String appLanguage("app_language");
|
||||
constexpr QLatin1String locale("locale");
|
||||
|
||||
constexpr QLatin1String captchaId("captcha_id");
|
||||
constexpr QLatin1String captchaSolution("captcha_solution");
|
||||
|
||||
constexpr QLatin1String activeDeviceCount("active_device_count");
|
||||
constexpr QLatin1String maxDeviceCount("max_device_count");
|
||||
|
||||
@@ -637,7 +637,7 @@ void Daemon::checkHandshake() {
|
||||
pendingHandshakes++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check again if there were connections that haven't completed a handshake.
|
||||
if (pendingHandshakes > 0) {
|
||||
m_handshakeTimer.start(HANDSHAKE_POLL_MSEC);
|
||||
|
||||
@@ -7,10 +7,14 @@
|
||||
#include <net/if.h>
|
||||
|
||||
#include <QDBusVariant>
|
||||
#include <QNetworkInterface>
|
||||
#include <QTimer>
|
||||
#include <QtDBus/QtDBus>
|
||||
|
||||
#include "core/utils/networkUtilities.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "router_linux.h"
|
||||
|
||||
constexpr const char* DBUS_RESOLVE_SERVICE = "org.freedesktop.resolve1";
|
||||
constexpr const char* DBUS_RESOLVE_PATH = "/org/freedesktop/resolve1";
|
||||
@@ -27,24 +31,56 @@ DnsUtilsLinux::DnsUtilsLinux(QObject* parent) : DnsUtils(parent) {
|
||||
logger.debug() << "DnsUtilsLinux created.";
|
||||
|
||||
QDBusConnection conn = QDBusConnection::systemBus();
|
||||
m_resolver = new QDBusInterface(DBUS_RESOLVE_SERVICE, DBUS_RESOLVE_PATH,
|
||||
DBUS_RESOLVE_MANAGER, conn, this);
|
||||
auto* watcher = new QDBusServiceWatcher(
|
||||
DBUS_RESOLVE_SERVICE, conn,
|
||||
QDBusServiceWatcher::WatchForRegistration |
|
||||
QDBusServiceWatcher::WatchForUnregistration, this);
|
||||
|
||||
connect(watcher, &QDBusServiceWatcher::serviceRegistered,
|
||||
this, &DnsUtilsLinux::onResolverRegistered);
|
||||
connect(watcher, &QDBusServiceWatcher::serviceUnregistered,
|
||||
this, &DnsUtilsLinux::onResolverUnregistered);
|
||||
|
||||
if (conn.interface()->isServiceRegistered(DBUS_RESOLVE_SERVICE)) {
|
||||
onResolverRegistered();
|
||||
}
|
||||
}
|
||||
|
||||
void DnsUtilsLinux::onResolverRegistered() {
|
||||
m_resolver.reset(new QDBusInterface(DBUS_RESOLVE_SERVICE, DBUS_RESOLVE_PATH,
|
||||
DBUS_RESOLVE_MANAGER,
|
||||
QDBusConnection::systemBus()));
|
||||
logger.debug() << "systemd-resolved available, DNS resolver initialized";
|
||||
|
||||
if (!m_pendingIfname.isEmpty()) {
|
||||
logger.debug() << "Re-applying DNS configuration for" << m_pendingIfname;
|
||||
updateResolvers(m_pendingIfname, m_pendingResolvers);
|
||||
}
|
||||
}
|
||||
|
||||
void DnsUtilsLinux::onResolverUnregistered() {
|
||||
logger.debug() << "systemd-resolved disappeared, dropping DNS resolver";
|
||||
m_resolver.reset();
|
||||
}
|
||||
|
||||
DnsUtilsLinux::~DnsUtilsLinux() {
|
||||
MZ_COUNT_DTOR(DnsUtilsLinux);
|
||||
|
||||
for (auto iterator = m_linkDomains.constBegin();
|
||||
iterator != m_linkDomains.constEnd(); ++iterator) {
|
||||
QList<QVariant> argumentList;
|
||||
argumentList << QVariant::fromValue(iterator.key());
|
||||
argumentList << QVariant::fromValue(iterator.value());
|
||||
m_resolver->asyncCallWithArgumentList(QStringLiteral("SetLinkDomains"),
|
||||
argumentList);
|
||||
}
|
||||
if (m_resolver) {
|
||||
if (m_gatewayIfindex > 0)
|
||||
setLinkDefaultRoute(m_gatewayIfindex, true);
|
||||
|
||||
if (m_ifindex > 0) {
|
||||
m_resolver->asyncCall(QStringLiteral("RevertLink"), m_ifindex);
|
||||
for (auto iterator = m_linkDomains.constBegin();
|
||||
iterator != m_linkDomains.constEnd(); ++iterator) {
|
||||
QList<QVariant> argumentList;
|
||||
argumentList << QVariant::fromValue(iterator.key());
|
||||
argumentList << QVariant::fromValue(iterator.value());
|
||||
m_resolver->asyncCallWithArgumentList(QStringLiteral("SetLinkDomains"),
|
||||
argumentList);
|
||||
}
|
||||
if (m_ifindex > 0) {
|
||||
m_resolver->asyncCall(QStringLiteral("RevertLink"), m_ifindex);
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug() << "DnsUtilsLinux destroyed.";
|
||||
@@ -52,19 +88,52 @@ DnsUtilsLinux::~DnsUtilsLinux() {
|
||||
|
||||
bool DnsUtilsLinux::updateResolvers(const QString& ifname,
|
||||
const QList<QHostAddress>& resolvers) {
|
||||
if (m_gatewayIfindex > 0) {
|
||||
setLinkDefaultRoute(m_gatewayIfindex, true);
|
||||
m_gatewayIfindex = 0;
|
||||
}
|
||||
|
||||
const int previousIfindex = m_ifindex;
|
||||
m_ifindex = if_nametoindex(qPrintable(ifname));
|
||||
if (m_ifindex <= 0) {
|
||||
logger.error() << "Unable to resolve ifindex for" << ifname;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pendingIfname = ifname;
|
||||
m_pendingResolvers = resolvers;
|
||||
|
||||
if (!m_resolver) {
|
||||
logger.debug() << "systemd-resolved not ready, queuing DNS configuration";
|
||||
return true;
|
||||
}
|
||||
|
||||
const int gwIdx = NetworkUtilities::getGatewayAndIface().second.index();
|
||||
if (gwIdx > 0 && gwIdx != m_ifindex && gwIdx != m_gatewayIfindex) {
|
||||
m_gatewayIfindex = gwIdx;
|
||||
setLinkDefaultRoute(gwIdx, false);
|
||||
}
|
||||
|
||||
setLinkDNS(m_ifindex, resolvers);
|
||||
setLinkDefaultRoute(m_ifindex, true);
|
||||
updateLinkDomains();
|
||||
|
||||
if (previousIfindex > 0 && previousIfindex != m_ifindex) {
|
||||
m_resolver->callWithArgumentList(QDBus::Block, QStringLiteral("RevertLink"),
|
||||
{QVariant::fromValue(previousIfindex)});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DnsUtilsLinux::restoreResolvers() {
|
||||
m_pendingIfname.clear();
|
||||
m_pendingResolvers.clear();
|
||||
|
||||
if (m_gatewayIfindex > 0) {
|
||||
setLinkDefaultRoute(m_gatewayIfindex, true);
|
||||
m_gatewayIfindex = 0;
|
||||
}
|
||||
|
||||
for (auto iterator = m_linkDomains.constBegin();
|
||||
iterator != m_linkDomains.constEnd(); ++iterator) {
|
||||
setLinkDomains(iterator.key(), iterator.value());
|
||||
@@ -72,7 +141,7 @@ bool DnsUtilsLinux::restoreResolvers() {
|
||||
m_linkDomains.clear();
|
||||
|
||||
/* Revert the VPN interface's DNS configuration */
|
||||
if (m_ifindex > 0) {
|
||||
if (m_ifindex > 0 && m_resolver) {
|
||||
QList<QVariant> argumentList = {QVariant::fromValue(m_ifindex)};
|
||||
QDBusPendingReply<> reply = m_resolver->asyncCallWithArgumentList(
|
||||
QStringLiteral("RevertLink"), argumentList);
|
||||
@@ -90,13 +159,17 @@ bool DnsUtilsLinux::restoreResolvers() {
|
||||
void DnsUtilsLinux::dnsCallCompleted(QDBusPendingCallWatcher* call) {
|
||||
QDBusPendingReply<> reply = *call;
|
||||
if (reply.isError()) {
|
||||
logger.error() << "Error received from the DBus service";
|
||||
logger.debug() << "DBus call failed (may be transient after systemd-resolved restart)";
|
||||
logger.debug() << "Restarting resolved to clear its query backlog";
|
||||
RouterLinux::Instance().flushDns();
|
||||
scheduleRetry();
|
||||
}
|
||||
delete call;
|
||||
}
|
||||
|
||||
void DnsUtilsLinux::setLinkDNS(int ifindex,
|
||||
const QList<QHostAddress>& resolvers) {
|
||||
if (!m_resolver) return;
|
||||
QList<DnsResolver> resolverList;
|
||||
char ifnamebuf[IF_NAMESIZE];
|
||||
const char* ifname = if_indextoname(ifindex, ifnamebuf);
|
||||
@@ -121,6 +194,7 @@ void DnsUtilsLinux::setLinkDNS(int ifindex,
|
||||
|
||||
void DnsUtilsLinux::setLinkDomains(int ifindex,
|
||||
const QList<DnsLinkDomain>& domains) {
|
||||
if (!m_resolver) return;
|
||||
char ifnamebuf[IF_NAMESIZE];
|
||||
const char* ifname = if_indextoname(ifindex, ifnamebuf);
|
||||
if (ifname) {
|
||||
@@ -144,6 +218,7 @@ void DnsUtilsLinux::setLinkDomains(int ifindex,
|
||||
}
|
||||
|
||||
void DnsUtilsLinux::setLinkDefaultRoute(int ifindex, bool enable) {
|
||||
if (!m_resolver) return;
|
||||
QList<QVariant> argumentList;
|
||||
argumentList << QVariant::fromValue(ifindex);
|
||||
argumentList << QVariant::fromValue(enable);
|
||||
@@ -156,6 +231,7 @@ void DnsUtilsLinux::setLinkDefaultRoute(int ifindex, bool enable) {
|
||||
}
|
||||
|
||||
void DnsUtilsLinux::updateLinkDomains() {
|
||||
if (!m_resolver) return;
|
||||
/* Get the list of search domains, and remove any others that might conspire
|
||||
* to satisfy DNS resolution. Unfortunately, this is a pain because Qt doesn't
|
||||
* seem to be able to demarshall complex property types.
|
||||
@@ -174,11 +250,20 @@ void DnsUtilsLinux::updateLinkDomains() {
|
||||
|
||||
void DnsUtilsLinux::dnsDomainsReceived(QDBusPendingCallWatcher* call) {
|
||||
QDBusPendingReply<QVariant> reply = *call;
|
||||
call->deleteLater();
|
||||
if (reply.isError()) {
|
||||
logger.error() << "Error retrieving the DNS domains from the DBus service";
|
||||
delete call;
|
||||
// systemd-resolved may still be starting up after a restart — retry a few times
|
||||
if (m_ifindex > 0 && m_domainRetries++ < 5) {
|
||||
logger.debug() << "systemd-resolved not ready yet, retrying DNS setup ("
|
||||
<< m_domainRetries << "/5)";
|
||||
QTimer::singleShot(500, this, &DnsUtilsLinux::updateLinkDomains);
|
||||
} else {
|
||||
logger.warning() << "Failed to configure DNS after 5 retries";
|
||||
m_domainRetries = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
m_domainRetries = 0;
|
||||
|
||||
/* Update the state of the DNS domains */
|
||||
m_linkDomains.clear();
|
||||
@@ -204,9 +289,30 @@ void DnsUtilsLinux::dnsDomainsReceived(QDBusPendingCallWatcher* call) {
|
||||
}
|
||||
|
||||
/* Add a root search domain for the new interface. */
|
||||
QList<DnsLinkDomain> newlist = {root};
|
||||
setLinkDomains(m_ifindex, newlist);
|
||||
delete call;
|
||||
if (m_ifindex > 0) {
|
||||
setLinkDomains(m_ifindex, {root});
|
||||
|
||||
/* Disable DefaultRoute on the physical gateway so systemd-resolved
|
||||
* routes all DNS through the VPN interface. */
|
||||
const int gwIdx = NetworkUtilities::getGatewayAndIface().second.index();
|
||||
if (gwIdx > 0 && gwIdx != m_ifindex && gwIdx != m_gatewayIfindex) {
|
||||
m_gatewayIfindex = gwIdx;
|
||||
setLinkDefaultRoute(gwIdx, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DnsUtilsLinux::scheduleRetry() {
|
||||
if (m_pendingIfname.isEmpty() || m_retryPending || m_domainRetries >= 5)
|
||||
return;
|
||||
m_retryPending = true;
|
||||
++m_domainRetries;
|
||||
logger.debug() << "Retrying full DNS setup (" << m_domainRetries << "/5)";
|
||||
QTimer::singleShot(1000, this, [this]() {
|
||||
m_retryPending = false;
|
||||
if (!m_pendingIfname.isEmpty())
|
||||
updateResolvers(m_pendingIfname, m_pendingResolvers);
|
||||
});
|
||||
}
|
||||
|
||||
static DnsMetatypeRegistrationProxy s_dnsMetatypeProxy;
|
||||
|
||||
@@ -6,7 +6,12 @@
|
||||
#define DNSUTILSLINUX_H
|
||||
|
||||
#include <QDBusInterface>
|
||||
#include <QScopedPointer>
|
||||
#include <QDBusPendingCallWatcher>
|
||||
#include <QDBusServiceWatcher>
|
||||
#include <QHostAddress>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
#include "daemon/dnsutils.h"
|
||||
#include "dbustypeslinux.h"
|
||||
@@ -29,13 +34,22 @@ class DnsUtilsLinux final : public DnsUtils {
|
||||
void updateLinkDomains();
|
||||
|
||||
private slots:
|
||||
void onResolverRegistered();
|
||||
void onResolverUnregistered();
|
||||
void dnsCallCompleted(QDBusPendingCallWatcher*);
|
||||
void dnsDomainsReceived(QDBusPendingCallWatcher*);
|
||||
|
||||
private:
|
||||
void scheduleRetry();
|
||||
|
||||
int m_ifindex = 0;
|
||||
int m_gatewayIfindex = 0;
|
||||
int m_domainRetries = 0;
|
||||
bool m_retryPending = false;
|
||||
QMap<int, DnsLinkDomainList> m_linkDomains;
|
||||
QDBusInterface* m_resolver = nullptr;
|
||||
QScopedPointer<QDBusInterface> m_resolver;
|
||||
QString m_pendingIfname;
|
||||
QList<QHostAddress> m_pendingResolvers;
|
||||
};
|
||||
|
||||
#endif // DNSUTILSLINUX_H
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "linuxfirewall.h"
|
||||
#include "logger.h"
|
||||
#include "xray_defs.h"
|
||||
#include <QFileInfo>
|
||||
#include <QProcess>
|
||||
|
||||
#define BRAND_CODE "amn"
|
||||
@@ -109,7 +110,7 @@ int LinuxFirewall::linkChain(LinuxFirewall::IPVersion ip, const QString& chain,
|
||||
// (we can't safely delete all rules at once since rule numbers change)
|
||||
// TODO: occasionally this script results in warnings in logs "Bad rule (does a matching rule exist in the chain?)" - this happens when
|
||||
// the e.g OUTPUT chain is empty but this script attempts to delete things from it anyway. It doesn't cause any problems, but we should still fix at some point..
|
||||
return execute(QStringLiteral("if ! %1 -L %2 -n --line-numbers -t %4 2> /dev/null | awk 'int($1) == 1 && $2 == \"%3\" { found=1 } END { if(found==1) { exit 0 } else { exit 1 } }' ; then %1 -I %2 -j %3 -t %4 && %1 -L %2 -n --line-numbers -t %4 2> /dev/null | awk 'int($1) > 1 && $2 == \"%3\" { print $1; exit }' | xargs %1 -t %4 -D %2 ; fi").arg(cmd, parent, chain, tableName));
|
||||
return execute(QStringLiteral("if ! %1 -L %2 -n --line-numbers -t %4 2> /dev/null | awk 'int($1) == 1 && $2 == \"%3\" { found=1 } END { if(found==1) { exit 0 } else { exit 1 } }' ; then %1 -I %2 -j %3 -t %4 && %1 -L %2 -n --line-numbers -t %4 2> /dev/null | awk 'int($1) > 1 && $2 == \"%3\" { print $1; exit }' | xargs -r %1 -t %4 -D %2 ; fi").arg(cmd, parent, chain, tableName));
|
||||
}
|
||||
else
|
||||
return execute(QStringLiteral("if ! %1 -C %2 -j %3 -t %4 2> /dev/null ; then %1 -A %2 -j %3 -t %4; fi").arg(cmd, parent, chain, tableName));
|
||||
@@ -501,13 +502,22 @@ int LinuxFirewall::execute(const QString &command, bool ignoreErrors)
|
||||
logger.debug() << "(" << exitCode << ") $ " << command;
|
||||
if (!out.isEmpty())
|
||||
logger.info() << out;
|
||||
if (!err.isEmpty())
|
||||
if (!err.isEmpty() && !ignoreErrors)
|
||||
logger.warning() << err;
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
void LinuxFirewall::setupTrafficSplitting()
|
||||
{
|
||||
const QString cgroupBase = QStringLiteral("/sys/fs/cgroup/net_cls");
|
||||
if (!QFileInfo::exists(cgroupBase)) {
|
||||
logger.warning() << "net_cls cgroup v1 not available, traffic splitting disabled";
|
||||
return;
|
||||
}
|
||||
execute(QStringLiteral(
|
||||
"if ! grep -qE '^[0-9]+[[:space:]]+%1$' /etc/iproute2/rt_tables 2>/dev/null ; then "
|
||||
"echo '200 %1' >> /etc/iproute2/rt_tables ; fi"
|
||||
).arg(kRtableName));
|
||||
auto cGroupDir = "/sys/fs/cgroup/net_cls/" BRAND_CODE "vpnexclusions/";
|
||||
logger.info() << "Should be setting up cgroup in" << cGroupDir << "for traffic splitting";
|
||||
execute(QStringLiteral("if [ ! -d %1 ] ; then mkdir %1 ; sleep 0.1 ; echo %2 > %1/net_cls.classid ; fi").arg(cGroupDir).arg(kCGroupId));
|
||||
@@ -519,6 +529,7 @@ void LinuxFirewall::teardownTrafficSplitting()
|
||||
{
|
||||
logger.info() << "Tearing down cgroup and routing rules";
|
||||
execute(QStringLiteral("if ip rule list | grep -q %1; then ip rule del from all fwmark %1 lookup %2 2> /dev/null ; fi").arg(kPacketTag, kRtableName));
|
||||
execute(QStringLiteral("ip route flush table %1").arg(kRtableName));
|
||||
execute(QStringLiteral("ip route flush table %1").arg(kRtableName), true);
|
||||
execute(QStringLiteral("ip route flush cache"));
|
||||
execute(QStringLiteral("sed -i '/%1/d' /etc/iproute2/rt_tables").arg(kRtableName));
|
||||
}
|
||||
|
||||
@@ -263,7 +263,11 @@ bool WireguardUtilsLinux::updatePeer(const InterfaceConfig& config) {
|
||||
// Exclude the server address, except for multihop exit servers.
|
||||
if ((config.m_hopType != InterfaceConfig::MultiHopExit) &&
|
||||
(m_rtmonitor != nullptr)) {
|
||||
m_rtmonitor->addExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
|
||||
if (!config.m_serverIpv4AddrIn.isEmpty() &&
|
||||
!m_rtmonitor->addExclusionRoute(IPAddress(config.m_serverIpv4AddrIn))) {
|
||||
logger.error() << "No gateway — cannot add server exclusion route";
|
||||
return false;
|
||||
}
|
||||
m_rtmonitor->addExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
|
||||
}
|
||||
|
||||
|
||||
@@ -272,7 +272,7 @@ bool SubscriptionUiController::restoreServiceFromAppStore()
|
||||
return false;
|
||||
}
|
||||
|
||||
emit installServerFromApiFinished(tr("Subscription restored successfully"));
|
||||
emit installServerFromApiFinished(tr("Subscription restored successfully."));
|
||||
if (result.duplicateCount > 0) {
|
||||
qInfo().noquote() << "[IAP] Skipped" << result.duplicateCount
|
||||
<< "duplicate restored transactions for original transaction IDs already processed";
|
||||
@@ -469,7 +469,7 @@ bool SubscriptionUiController::importTrialFromGateway(const QString &email)
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
if (errorCode == ErrorCode::ApiTrialAlreadyUsedError) {
|
||||
emit trialEmailError(
|
||||
tr("This email address has already been used to activate a trial. Like the service? Upgrade to Premium"));
|
||||
tr("This email address has already been used to activate a trial. If you like the service, you can upgrade to Premium"));
|
||||
} else {
|
||||
emit errorOccurred(errorCode);
|
||||
}
|
||||
@@ -648,7 +648,7 @@ QStringList SubscriptionUiController::availableProtocols(const QString &serverId
|
||||
void SubscriptionUiController::removeApiConfig(const QString &serverId)
|
||||
{
|
||||
m_subscriptionController->removeApiConfig(serverId);
|
||||
emit apiConfigRemoved(tr("API config removed"));
|
||||
emit apiConfigRemoved(tr("Api config removed"));
|
||||
}
|
||||
|
||||
void SubscriptionUiController::removeServer(const QString &serverId)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "serversUiController.h"
|
||||
|
||||
#include "core/utils/api/apiUtils.h"
|
||||
#include "core/utils/containerEnum.h"
|
||||
#include "core/utils/containers/containerUtils.h"
|
||||
#include "core/utils/protocolEnum.h"
|
||||
@@ -206,14 +205,10 @@ QString ServersUiController::getDefaultServerDescriptionCollapsed() const
|
||||
QString ServersUiController::getDefaultServerImagePathCollapsed() const
|
||||
{
|
||||
const auto &description = serverDescriptionById(getDefaultServerId());
|
||||
if (!description.isApiV2) {
|
||||
if (!description.isApiV2 || description.apiServerCountryCode.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
const QString flagCode = apiUtils::getCountryFlagCode(description.apiServerCountryCode);
|
||||
if (flagCode.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(flagCode);
|
||||
return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(description.apiServerCountryCode.toUpper());
|
||||
}
|
||||
|
||||
QString ServersUiController::getDefaultServerDescriptionExpanded() const
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "core/utils/api/apiUtils.h"
|
||||
#include "core/utils/serverConfigUtils.h"
|
||||
#include "core/utils/constants/apiKeys.h"
|
||||
#include "core/utils/constants/apiConstants.h"
|
||||
@@ -42,7 +41,7 @@ QVariant ApiCountryModel::data(const QModelIndex &index, int role) const
|
||||
return countryInfo.countryName;
|
||||
}
|
||||
case CountryImageCodeRole: {
|
||||
return apiUtils::getCountryFlagCode(countryInfo.countryCode);
|
||||
return countryInfo.countryCode.toUpper();
|
||||
}
|
||||
case IsIssuedRole: {
|
||||
return isIssued;
|
||||
|
||||
@@ -63,8 +63,8 @@ Menu {
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("&Paste")
|
||||
// Fix calling paste from clipboard when launching app on android/ios
|
||||
enabled: (Qt.platform.os === "android" || Qt.platform.os === "ios") ? true : textObj.canPaste
|
||||
// Fix calling paste from clipboard when launching app on android
|
||||
enabled: Qt.platform.os === "android" ? true : textObj.canPaste
|
||||
onTriggered: textObj.paste()
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,6 @@ PageType {
|
||||
property string apiCurrentProtocol: ""
|
||||
|
||||
readonly property bool isApiProtocolSelectionVisible: ServersUiController.isDefaultServerFromApi && root.apiAvailableProtocols.length > 0
|
||||
readonly property bool isOutdatedAwgWarningVisible: drawer.isCollapsedStateActive()
|
||||
&& ServersUiController.defaultServerHasOutdatedAwgContainer
|
||||
|
||||
function updateApiProtocolState() {
|
||||
if (ServersUiController.isDefaultServerFromApi) {
|
||||
@@ -301,6 +299,26 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: outdatedContainerWarningIcon
|
||||
objectName: "outdatedContainerWarningIcon"
|
||||
|
||||
Layout.rightMargin: 8
|
||||
|
||||
visible: drawer.isCollapsedStateActive() && ServersUiController.defaultServerHasOutdatedAwgContainer
|
||||
|
||||
hoverEnabled: false
|
||||
image: "qrc:/images/controls/alert-circle.svg"
|
||||
imageColor: AmneziaStyle.color.goldenApricot
|
||||
|
||||
icon.width: 18
|
||||
icon.height: 18
|
||||
backgroundRadius: 16
|
||||
horizontalPadding: 4
|
||||
topPadding: 4
|
||||
bottomPadding: 3
|
||||
}
|
||||
|
||||
Header1TextType {
|
||||
id: collapsedButtonHeader
|
||||
objectName: "collapsedButtonHeader"
|
||||
@@ -352,9 +370,7 @@ PageType {
|
||||
objectName: "rowLayoutLabel"
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
Layout.topMargin: 8
|
||||
Layout.bottomMargin: root.isOutdatedAwgWarningVisible
|
||||
? 8
|
||||
: (root.isApiProtocolSelectionVisible ? 8 : (drawer.isCollapsedStateActive ? 44 : ServersUiController.isDefaultServerFromApi ? 61 : 16))
|
||||
Layout.bottomMargin: root.isApiProtocolSelectionVisible ? 8 : (drawer.isCollapsedStateActive ? 44 : ServersUiController.isDefaultServerFromApi ? 61 : 16)
|
||||
spacing: 0
|
||||
|
||||
BasicButtonType {
|
||||
@@ -409,23 +425,6 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
WarningType {
|
||||
objectName: "outdatedContainerWarning"
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
visible: root.isOutdatedAwgWarningVisible
|
||||
|
||||
backGroundColor: AmneziaStyle.color.transparent
|
||||
iconPath: "qrc:/images/controls/alert-circle.svg"
|
||||
imageColor: AmneziaStyle.color.goldenApricot
|
||||
textColor: AmneziaStyle.color.goldenApricot
|
||||
textString: qsTr("AmneziaWG 2.0 is outdated and no longer supported. Continued use requires a fresh installation of the AmneziaWG 3.0 container.")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
objectName: "protocolRowLayout"
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
|
||||
@@ -235,7 +235,7 @@ PageType {
|
||||
Layout.rightMargin: 32
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
source: countryImageCode !== "" ? "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg" : ""
|
||||
source: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ PageType {
|
||||
hideDescription: isWorkerExpired ? false : true
|
||||
descriptionColor: AmneziaStyle.color.vibrantRed
|
||||
|
||||
leftImageSource: countryImageCode !== "" ? "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg" : ""
|
||||
leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
|
||||
rightImageSource: isIssued ? "qrc:/images/controls/more-vertical.svg" : "qrc:/images/controls/download.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
|
||||
@@ -72,17 +72,6 @@ if(WIN32)
|
||||
DESTINATION "."
|
||||
COMPONENT AmneziaVPN
|
||||
)
|
||||
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
|
||||
include(InstallRequiredSystemLibraries)
|
||||
if(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS)
|
||||
install(PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}
|
||||
DESTINATION "."
|
||||
COMPONENT AmneziaVPN
|
||||
)
|
||||
else()
|
||||
message(WARNING "MSVC runtime libraries were not found, packages will not ship them")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (APPLE AND NOT IOS AND NOT MACOS_NE)
|
||||
|
||||
@@ -123,7 +123,7 @@ case "$TARGET" in
|
||||
android)
|
||||
no_installers=1
|
||||
: ${CMAKE_GENERATOR:="Ninja"}
|
||||
: ${ANDROID_PLATFORM:="android-${APP_ANDROID_MIN_SDK:-28}"}
|
||||
: ${ANDROID_PLATFORM:="android-28"}
|
||||
|
||||
if [[ -n "$SIGN" ]]; then
|
||||
QT_ANDROID_SIGN_APK=TRUE
|
||||
@@ -198,9 +198,6 @@ args=()
|
||||
[[ -n "$ANDROID_SDK_ROOT" ]] && args+=("-DANDROID_SDK_ROOT=$ANDROID_SDK_ROOT")
|
||||
[[ -n "$ANDROID_NDK_ROOT" ]] && args+=("-DANDROID_NDK_ROOT=$ANDROID_NDK_ROOT")
|
||||
[[ -n "$ANDROID_PLATFORM" ]] && args+=("-DANDROID_PLATFORM=$ANDROID_PLATFORM")
|
||||
[[ -n "$APP_ANDROID_MIN_SDK" ]] && args+=("-DAPP_ANDROID_MIN_SDK=$APP_ANDROID_MIN_SDK")
|
||||
[[ -n "$APP_ANDROID_MAX_SDK" ]] && args+=("-DAPP_ANDROID_MAX_SDK=$APP_ANDROID_MAX_SDK")
|
||||
[[ -n "$APP_ANDROID_VERSION_CODE_OFFSET" ]] && args+=("-DAPP_ANDROID_VERSION_CODE_OFFSET=$APP_ANDROID_VERSION_CODE_OFFSET")
|
||||
[[ -n "$QT_ANDROID_SIGN_APK" ]] && args+=("-DQT_ANDROID_SIGN_APK=$QT_ANDROID_SIGN_APK")
|
||||
[[ -n "$QT_ANDROID_SIGN_AAB" ]] && args+=("-DQT_ANDROID_SIGN_AAB=$QT_ANDROID_SIGN_AAB")
|
||||
[[ -n "$QT_ANDROID_ABIS" ]] && args+=("-DQT_ANDROID_ABIS=$QT_ANDROID_ABIS")
|
||||
|
||||
@@ -34,6 +34,10 @@ download_file() {
|
||||
echo "Successfully downloaded $filename"
|
||||
}
|
||||
|
||||
download_file https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android9+_arm64-v8a.apk
|
||||
download_file https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android9+_armeabi-v7a.apk
|
||||
download_file https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android9+_x86.apk
|
||||
download_file https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android9+_x86_64.apk
|
||||
download_file https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_linux_x64.run
|
||||
download_file https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos_x64.pkg
|
||||
download_file https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_windows_x64.exe
|
||||
|
||||
@@ -73,15 +73,15 @@ Component.prototype.createOperations = function()
|
||||
"workingDirectory=@TargetDir@", "iconPath=@TargetDir@\\" + appExecutableFileName(), "iconId=0");
|
||||
|
||||
if (!vcRuntimeIsInstalled()) {
|
||||
var vcRedistFileName = (systemInfo.currentCpuArchitecture.search("64") < 0) ? "vc_redist.x86.exe"
|
||||
: "vc_redist.x64.exe";
|
||||
if (installer.findPath(vcRedistFileName, [installer.value("TargetDir").replace(/\//g, '\\')]).length !== 0) {
|
||||
component.addElevatedOperation("Execute", "@TargetDir@\\" + vcRedistFileName, "/install", "/quiet", "/norestart", "/log", "vc_redist.log");
|
||||
} else {
|
||||
console.log(vcRedistFileName + " is not bundled, relying on the application-local MSVC runtime");
|
||||
}
|
||||
if (systemInfo.currentCpuArchitecture.search("64") < 0) {
|
||||
component.addElevatedOperation("Execute", "@TargetDir@\\" + "vc_redist.x86.exe", "/install", "/quiet", "/norestart", "/log", "vc_redist.log");
|
||||
}
|
||||
else {
|
||||
component.addElevatedOperation("Execute", "@TargetDir@\\" + "vc_redist.x64.exe", "/install", "/quiet", "/norestart", "/log", "vc_redist.log");
|
||||
}
|
||||
|
||||
} else {
|
||||
console.log("Microsoft Visual C++ Redistributable already installed");
|
||||
console.log("Microsoft Visual C++ 2017 Redistributable already installed");
|
||||
}
|
||||
|
||||
let pu_path = installer.value("TargetDir").replace(/\//g, '\\') + "\\"
|
||||
|
||||
@@ -178,11 +178,16 @@ bool RouterLinux::flushDns()
|
||||
}
|
||||
|
||||
p.waitForFinished();
|
||||
QByteArray output(p.readAll());
|
||||
QByteArray output = p.readAll();
|
||||
if ((p.exitStatus() != QProcess::NormalExit) || (p.exitCode() != 0)) {
|
||||
qDebug().noquote() << "Failed to flush DNS: " + output;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (output.isEmpty())
|
||||
qDebug().noquote() << "Flush dns completed";
|
||||
else
|
||||
qDebug().noquote() << "OUTPUT systemctl restart nscd/systemd-resolved: " + output;
|
||||
qDebug().noquote() << "OUTPUT systemctl restart: " + output;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||