diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index dbd52e0f..370c069d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,7 +16,10 @@ jobs: QT_VERSION: 6.6.2 QIF_VERSION: 4.7 PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} + PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} + DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} + DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} steps: - name: 'Install Qt' @@ -83,7 +86,10 @@ jobs: QIF_VERSION: 4.7 BUILD_ARCH: 64 PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} + PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} + DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} + DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} steps: - name: 'Get sources' @@ -146,7 +152,10 @@ jobs: CC: cc CXX: c++ PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} + PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} + DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} + DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} steps: - name: 'Setup xcode' @@ -238,7 +247,10 @@ jobs: QT_VERSION: 6.4.3 QIF_VERSION: 4.6 PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} + PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} + DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} + DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} steps: - name: 'Setup xcode' @@ -304,7 +316,10 @@ jobs: QT_VERSION: 6.6.3 QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools' PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} + PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} + DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} + DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} steps: - name: 'Install desktop Qt' diff --git a/.github/workflows/tag-deploy.yml b/.github/workflows/tag-deploy.yml index dffb3ab1..2bcbd8c6 100644 --- a/.github/workflows/tag-deploy.yml +++ b/.github/workflows/tag-deploy.yml @@ -16,7 +16,10 @@ jobs: QT_VERSION: 6.4.1 QIF_VERSION: 4.5 PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} + PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} + DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} + DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} steps: - name: 'Install desktop Qt' diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fb66178..dc8bf355 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 1066) +set(APP_ANDROID_VERSION_CODE 1067) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(MZ_PLATFORM_NAME "linux") diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 2de5db48..2ec4082c 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -26,9 +26,11 @@ add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}") add_definitions(-DPROD_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}") add_definitions(-DPROD_PROXY_STORAGE_KEY="$ENV{PROD_PROXY_STORAGE_KEY}") +add_definitions(-DPROD_S3_ENDPOINT="$ENV{PROD_S3_ENDPOINT}") add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}") add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}") +add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}") if(IOS) set(PACKAGES ${PACKAGES} Multimedia) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 2d06b443..4e25097d 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -111,10 +111,11 @@ void AmneziaApplication::init() qFatal("Android controller initialization failed"); } - connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, [this](QString data) { - m_pageController->goToPageHome(); + connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, this, [this](QString data) { + emit m_pageController->goToPageHome(); m_importController->extractConfigFromData(data); - m_pageController->goToPageViewConfig(); + data.clear(); + emit m_pageController->goToPageViewConfig(); }); m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider); @@ -122,16 +123,16 @@ void AmneziaApplication::init() #ifdef Q_OS_IOS IosController::Instance()->initialize(); - connect(IosController::Instance(), &IosController::importConfigFromOutside, [this](QString data) { - m_pageController->goToPageHome(); + connect(IosController::Instance(), &IosController::importConfigFromOutside, this, [this](QString data) { + emit m_pageController->goToPageHome(); m_importController->extractConfigFromData(data); - m_pageController->goToPageViewConfig(); + emit m_pageController->goToPageViewConfig(); }); - connect(IosController::Instance(), &IosController::importBackupFromOutside, [this](QString filePath) { - m_pageController->goToPageHome(); + connect(IosController::Instance(), &IosController::importBackupFromOutside, this, [this](QString filePath) { + emit m_pageController->goToPageHome(); m_pageController->goToPageSettingsBackup(); - m_settingsController->importBackupFromOutside(filePath); + emit m_settingsController->importBackupFromOutside(filePath); }); QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); }); diff --git a/client/android/gradle.properties b/client/android/gradle.properties index 5a27838c..ce651e1c 100644 --- a/client/android/gradle.properties +++ b/client/android/gradle.properties @@ -33,7 +33,7 @@ android.library.defaults.buildfeatures.androidresources=false # For development copy and set local values for these parameters in local.properties #androidCompileSdkVersion=android-34 #androidBuildToolsVersion=34.0.0 -#qtMinSdkVersion=24 +#qtMinSdkVersion=26 #qtTargetSdkVersion=34 #androidNdkVersion=26.1.10909125 #qtTargetAbiList=x86_64 diff --git a/client/android/protocolApi/src/main/kotlin/Protocol.kt b/client/android/protocolApi/src/main/kotlin/Protocol.kt index 76900bf0..8aa72c39 100644 --- a/client/android/protocolApi/src/main/kotlin/Protocol.kt +++ b/client/android/protocolApi/src/main/kotlin/Protocol.kt @@ -1,6 +1,5 @@ package org.amnezia.vpn.protocol -import android.annotation.SuppressLint import android.content.Context import android.net.IpPrefix import android.net.VpnService @@ -8,9 +7,6 @@ import android.net.VpnService.Builder import android.os.Build import android.system.OsConstants import androidx.annotation.RequiresApi -import java.io.File -import java.io.FileOutputStream -import java.util.zip.ZipFile import kotlinx.coroutines.flow.MutableStateFlow import org.amnezia.vpn.util.Log import org.amnezia.vpn.util.net.InetNetwork diff --git a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt index 93bbf37e..68a5c37b 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt @@ -19,6 +19,7 @@ import android.os.Looper import android.os.Message import android.os.Messenger import android.provider.Settings +import android.view.MotionEvent import android.view.WindowManager.LayoutParams import android.webkit.MimeTypeMap import android.widget.Toast @@ -155,7 +156,7 @@ class AmneziaActivity : QtActivity() { */ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - Log.d(TAG, "Create Amnezia activity: $intent") + Log.d(TAG, "Create Amnezia activity") loadLibs() window.apply { addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) @@ -190,7 +191,7 @@ class AmneziaActivity : QtActivity() { override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) - Log.d(TAG, "onNewIntent: $intent") + Log.v(TAG, "onNewIntent: $intent") intent?.let(::processIntent) } @@ -377,7 +378,7 @@ class AmneziaActivity : QtActivity() { @MainThread private fun startVpn(vpnConfig: String) { getVpnProto(vpnConfig)?.let { proto -> - Log.d(TAG, "Proto from config: $proto, current proto: $vpnProto") + Log.v(TAG, "Proto from config: $proto, current proto: $vpnProto") if (isServiceConnected) { if (proto.serviceClass == vpnProto?.serviceClass) { vpnProto = proto @@ -490,7 +491,7 @@ class AmneziaActivity : QtActivity() { startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler( onSuccess = { it?.data?.let { uri -> - Log.d(TAG, "Save file to $uri") + Log.v(TAG, "Save file to $uri") try { contentResolver.openOutputStream(uri)?.use { os -> os.bufferedWriter().use { it.write(data) } @@ -539,7 +540,7 @@ class AmneziaActivity : QtActivity() { startActivityForResult(it, OPEN_FILE_ACTION_CODE, ActivityResultHandler( onAny = { val uri = it?.data?.toString() ?: "" - Log.d(TAG, "Open file: $uri") + Log.v(TAG, "Open file: $uri") mainScope.launch { qtInitialized.await() QtAndroidController.onFileOpened(uri) @@ -694,6 +695,66 @@ class AmneziaActivity : QtActivity() { } } + // workaround for a bug in Qt that causes the mouse click event not to be handled + // also disable right-click, as it causes the application to crash + private var lastButtonState = 0 + private fun MotionEvent.fixCopy(): MotionEvent = MotionEvent.obtain( + downTime, + eventTime, + action, + pointerCount, + (0 until pointerCount).map { i -> + MotionEvent.PointerProperties().apply { + getPointerProperties(i, this) + } + }.toTypedArray(), + (0 until pointerCount).map { i -> + MotionEvent.PointerCoords().apply { + getPointerCoords(i, this) + } + }.toTypedArray(), + metaState, + MotionEvent.BUTTON_PRIMARY, + xPrecision, + yPrecision, + deviceId, + edgeFlags, + source, + flags + ) + + private fun handleMouseEvent(ev: MotionEvent, superDispatch: (MotionEvent?) -> Boolean): Boolean { + when (ev.action) { + MotionEvent.ACTION_DOWN -> { + lastButtonState = ev.buttonState + if (ev.buttonState == MotionEvent.BUTTON_SECONDARY) return true + } + + MotionEvent.ACTION_UP -> { + when (lastButtonState) { + MotionEvent.BUTTON_SECONDARY -> return true + MotionEvent.BUTTON_PRIMARY -> { + val modEvent = ev.fixCopy() + return superDispatch(modEvent).apply { modEvent.recycle() } + } + } + } + } + return superDispatch(ev) + } + + override fun dispatchTouchEvent(ev: MotionEvent?): Boolean { + if (ev != null && ev.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) { + return handleMouseEvent(ev) { super.dispatchTouchEvent(it) } + } + return super.dispatchTouchEvent(ev) + } + + override fun dispatchTrackballEvent(ev: MotionEvent?): Boolean { + ev?.let { return handleMouseEvent(ev) { super.dispatchTrackballEvent(it) }} + return super.dispatchTrackballEvent(ev) + } + /** * Utils methods */ diff --git a/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt b/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt index 4ce51130..85f06b36 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt @@ -285,7 +285,7 @@ open class AmneziaVpnService : VpnService() { arrayOf(ACTION_CONNECT, ACTION_DISCONNECT), ContextCompat.RECEIVER_NOT_EXPORTED ) { it?.action?.let { action -> - Log.d(TAG, "Broadcast request received: $action") + Log.v(TAG, "Broadcast request received: $action") when (action) { ACTION_CONNECT -> connect() ACTION_DISCONNECT -> disconnect() @@ -416,7 +416,7 @@ open class AmneziaVpnService : VpnService() { serviceNotification.isNotificationEnabled() && getSystemService()?.isInteractive != false ) { - Log.d(TAG, "Launch traffic stats update") + Log.v(TAG, "Launch traffic stats update") trafficStats.reset() startTrafficStatsUpdateJob() } diff --git a/client/android/src/org/amnezia/vpn/AuthActivity.kt b/client/android/src/org/amnezia/vpn/AuthActivity.kt index 2593315c..46401548 100644 --- a/client/android/src/org/amnezia/vpn/AuthActivity.kt +++ b/client/android/src/org/amnezia/vpn/AuthActivity.kt @@ -66,7 +66,7 @@ class AuthActivity : FragmentActivity() { object : BiometricPrompt.AuthenticationCallback() { override fun onAuthenticationSucceeded(result: AuthenticationResult) { super.onAuthenticationSucceeded(result) - Log.d(TAG, "Authentication succeeded") + Log.v(TAG, "Authentication succeeded") QtAndroidController.onAuthResult(true) finish() } diff --git a/client/android/src/org/amnezia/vpn/ImportConfigActivity.kt b/client/android/src/org/amnezia/vpn/ImportConfigActivity.kt index 9faa30d0..49823a36 100644 --- a/client/android/src/org/amnezia/vpn/ImportConfigActivity.kt +++ b/client/android/src/org/amnezia/vpn/ImportConfigActivity.kt @@ -29,20 +29,20 @@ class ImportConfigActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - Log.d(TAG, "Create Import Config Activity: $intent") + Log.v(TAG, "Create Import Config Activity: $intent") intent?.let(::readConfig) } override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) - Log.d(TAG, "onNewIntent: $intent") + Log.v(TAG, "onNewIntent: $intent") intent.let(::readConfig) } private fun readConfig(intent: Intent) { when (intent.action) { ACTION_SEND -> { - Log.d(TAG, "Process SEND action, type: ${intent.type}") + Log.v(TAG, "Process SEND action, type: ${intent.type}") when (intent.type) { "application/octet-stream" -> { intent.getUriCompat()?.let { uri -> @@ -60,7 +60,7 @@ class ImportConfigActivity : ComponentActivity() { } ACTION_VIEW -> { - Log.d(TAG, "Process VIEW action, scheme: ${intent.scheme}") + Log.v(TAG, "Process VIEW action, scheme: ${intent.scheme}") when (intent.scheme) { "file", "content" -> { intent.data?.let { uri -> diff --git a/client/android/src/org/amnezia/vpn/ServiceNotification.kt b/client/android/src/org/amnezia/vpn/ServiceNotification.kt index a991eafc..f2980df6 100644 --- a/client/android/src/org/amnezia/vpn/ServiceNotification.kt +++ b/client/android/src/org/amnezia/vpn/ServiceNotification.kt @@ -58,7 +58,7 @@ class ServiceNotification(private val context: Context) { fun buildNotification(serverName: String?, protocol: String?, state: ProtocolState): Notification { val speedString = if (state == CONNECTED) zeroSpeed else null - Log.d(TAG, "Build notification: $serverName, $state") + Log.v(TAG, "Build notification: $serverName, $state") return notificationBuilder .setSmallIcon(R.drawable.ic_amnezia_round) @@ -85,7 +85,7 @@ class ServiceNotification(private val context: Context) { @SuppressLint("MissingPermission") fun updateNotification(serverName: String?, protocol: String?, state: ProtocolState) { - Log.d(TAG, "Update notification: $serverName, $state") + Log.v(TAG, "Update notification: $serverName, $state") notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, protocol, state)) } diff --git a/client/android/utils/src/main/kotlin/LibraryLoader.kt b/client/android/utils/src/main/kotlin/LibraryLoader.kt index f1c6465e..8def18d0 100644 --- a/client/android/utils/src/main/kotlin/LibraryLoader.kt +++ b/client/android/utils/src/main/kotlin/LibraryLoader.kt @@ -46,7 +46,7 @@ object LibraryLoader { System.loadLibrary(libraryName) return } catch (_: UnsatisfiedLinkError) { - Log.d(TAG, "Failed to load library, try to extract it from apk") + Log.w(TAG, "Failed to load library, try to extract it from apk") } var tempFile: File? = null try { diff --git a/client/android/utils/src/main/kotlin/net/NetworkState.kt b/client/android/utils/src/main/kotlin/net/NetworkState.kt index 90988f38..51f674c0 100644 --- a/client/android/utils/src/main/kotlin/net/NetworkState.kt +++ b/client/android/utils/src/main/kotlin/net/NetworkState.kt @@ -40,11 +40,11 @@ class NetworkState( private val networkCallback: NetworkCallback by lazy(NONE) { object : NetworkCallback() { override fun onAvailable(network: Network) { - Log.d(TAG, "onAvailable: $network") + Log.v(TAG, "onAvailable: $network") } override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { - Log.d(TAG, "onCapabilitiesChanged: $network, $networkCapabilities") + Log.v(TAG, "onCapabilitiesChanged: $network, $networkCapabilities") handler.post { checkNetworkState(network, networkCapabilities) } @@ -67,11 +67,11 @@ class NetworkState( } override fun onBlockedStatusChanged(network: Network, blocked: Boolean) { - Log.d(TAG, "onBlockedStatusChanged: $network, $blocked") + Log.v(TAG, "onBlockedStatusChanged: $network, $blocked") } override fun onLost(network: Network) { - Log.d(TAG, "onLost: $network") + Log.v(TAG, "onLost: $network") } } } diff --git a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt index ac11374b..e93834f4 100644 --- a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt +++ b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt @@ -66,7 +66,7 @@ open class Wireguard : Protocol() { try { delay(1000) var log = getLogcat(time) - Log.d(TAG, "First waiting log: $log") + Log.v(TAG, "First waiting log: $log") // check that there is a connection log, // to avoid infinite connection if (!log.contains("Attaching to interface")) { diff --git a/client/android/xray/src/main/kotlin/Xray.kt b/client/android/xray/src/main/kotlin/Xray.kt index 6e37c9c2..08242525 100644 --- a/client/android/xray/src/main/kotlin/Xray.kt +++ b/client/android/xray/src/main/kotlin/Xray.kt @@ -130,8 +130,8 @@ class Xray : Protocol() { LibXray.initXray(assetsPath) val geoDir = File(assetsPath, "geo").absolutePath val configPath = File(context.cacheDir, "config.json") - Log.d(TAG, "xray.location.asset: $geoDir") - Log.d(TAG, "config: $configPath") + Log.v(TAG, "xray.location.asset: $geoDir") + Log.v(TAG, "config: $configPath") try { configPath.writeText(configJson) } catch (e: IOException) { diff --git a/client/cmake/android.cmake b/client/cmake/android.cmake index c96d9ab8..34ca5bff 100644 --- a/client/cmake/android.cmake +++ b/client/cmake/android.cmake @@ -1,6 +1,6 @@ message("Client android ${CMAKE_ANDROID_ARCH_ABI} build") -set(APP_ANDROID_MIN_SDK 24) +set(APP_ANDROID_MIN_SDK 26) set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING "The minimum API level supported by the application or library" FORCE) diff --git a/client/core/controllers/apiController.cpp b/client/core/controllers/apiController.cpp index 5cdaa7ae..31a561d8 100644 --- a/client/core/controllers/apiController.cpp +++ b/client/core/controllers/apiController.cpp @@ -12,6 +12,7 @@ #include "configurators/wireguard_configurator.h" #include "core/enums/apiEnums.h" #include "version.h" +#include "utilities.h" namespace { @@ -40,9 +41,10 @@ namespace constexpr char apiPayload[] = "api_payload"; constexpr char keyPayload[] = "key_payload"; - } - const QStringList proxyStorageUrl = { "" }; + constexpr char apiConfig[] = "api_config"; + constexpr char authData[] = "auth_data"; + } ErrorCode checkErrors(const QList &sslErrors, QNetworkReply *reply) { @@ -94,8 +96,8 @@ void ApiController::fillServerConfig(const QString &protocol, const ApiControlle configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey); } else if (protocol == configKey::awg) { configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey); - auto serverConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); - auto containers = serverConfig.value(config_key::containers).toArray(); + auto newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); + auto containers = newServerConfig.value(config_key::containers).toArray(); if (containers.isEmpty()) { return; // todo process error } @@ -114,25 +116,30 @@ void ApiController::fillServerConfig(const QString &protocol, const ApiControlle containerConfig[config_key::transportPacketMagicHeader] = protocolConfig.value(config_key::transportPacketMagicHeader); container[containerName] = containerConfig; containers.replace(0, container); - serverConfig[config_key::containers] = containers; - configStr = QString(QJsonDocument(serverConfig).toJson()); + newServerConfig[config_key::containers] = containers; + configStr = QString(QJsonDocument(newServerConfig).toJson()); } - QJsonObject apiConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); - serverConfig[config_key::dns1] = apiConfig.value(config_key::dns1); - serverConfig[config_key::dns2] = apiConfig.value(config_key::dns2); - serverConfig[config_key::containers] = apiConfig.value(config_key::containers); - serverConfig[config_key::hostName] = apiConfig.value(config_key::hostName); + QJsonObject newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); + serverConfig[config_key::dns1] = newServerConfig.value(config_key::dns1); + serverConfig[config_key::dns2] = newServerConfig.value(config_key::dns2); + serverConfig[config_key::containers] = newServerConfig.value(config_key::containers); + serverConfig[config_key::hostName] = newServerConfig.value(config_key::hostName); - if (apiConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) { - serverConfig[config_key::configVersion] = apiConfig.value(config_key::configVersion); - serverConfig[config_key::description] = apiConfig.value(config_key::description); - serverConfig[config_key::name] = apiConfig.value(config_key::name); + if (newServerConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) { + serverConfig[config_key::configVersion] = newServerConfig.value(config_key::configVersion); + serverConfig[config_key::description] = newServerConfig.value(config_key::description); + serverConfig[config_key::name] = newServerConfig.value(config_key::name); } - auto defaultContainer = apiConfig.value(config_key::defaultContainer).toString(); + auto defaultContainer = newServerConfig.value(config_key::defaultContainer).toString(); serverConfig[config_key::defaultContainer] = defaultContainer; + QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap(); + map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap()); + auto apiConfig = QJsonObject::fromVariantMap(map); + serverConfig[configKey::apiConfig] = apiConfig; + return; } @@ -146,6 +153,15 @@ QStringList ApiController::getProxyUrls() QList sslErrors; QNetworkReply *reply; + QStringList proxyStorageUrl; + if (m_isDevEnvironment) { + proxyStorageUrl = QStringList { DEV_S3_ENDPOINT }; + } else { + proxyStorageUrl = QStringList { PROD_S3_ENDPOINT }; + } + + QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY; + for (const auto &proxyStorageUrl : proxyStorageUrl) { request.setUrl(proxyStorageUrl); reply = amnApp->manager()->get(request); @@ -166,11 +182,23 @@ QStringList ApiController::getProxyUrls() EVP_PKEY *privateKey = nullptr; QByteArray responseBody; try { - QByteArray key = PROD_PROXY_STORAGE_KEY; - QSimpleCrypto::QRsa rsa; - privateKey = rsa.getPrivateKeyFromByteArray(key, ""); - responseBody = rsa.decrypt(encryptedResponseBody, privateKey, RSA_PKCS1_PADDING); + if (!m_isDevEnvironment) { + QCryptographicHash hash(QCryptographicHash::Sha512); + hash.addData(key); + QByteArray hashResult = hash.result().toHex(); + + QByteArray key = QByteArray::fromHex(hashResult.left(64)); + QByteArray iv = QByteArray::fromHex(hashResult.mid(64, 32)); + + QByteArray ba = QByteArray::fromBase64(encryptedResponseBody); + + QSimpleCrypto::QBlockCipher blockCipher; + responseBody = blockCipher.decryptAesBlockCipher(ba, key, iv); + } else { + responseBody = encryptedResponseBody; + } } catch (...) { + Utils::logException(); qCritical() << "error loading private key from environment variables or decrypting payload"; return {}; } @@ -316,7 +344,8 @@ ErrorCode ApiController::getServicesList(QByteArray &responseBody) } ErrorCode ApiController::getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType, - const QString &protocol, const QString &serverCountryCode, QJsonObject &serverConfig) + const QString &protocol, const QString &serverCountryCode, const QJsonObject &authData, + QJsonObject &serverConfig) { #ifdef Q_OS_IOS IosController::Instance()->requestInetAccess(); @@ -339,6 +368,7 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co } apiPayload[configKey::serviceType] = serviceType; apiPayload[configKey::uuid] = installationUuid; + apiPayload[configKey::authData] = authData; QSimpleCrypto::QBlockCipher blockCipher; QByteArray key = blockCipher.generatePrivateSalt(32); @@ -361,6 +391,7 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co QSimpleCrypto::QRsa rsa; publicKey = rsa.getPublicKeyFromByteArray(rsaKey); } catch (...) { + Utils::logException(); qCritical() << "error loading public key from environment variables"; return ErrorCode::ApiMissingAgwPublicKey; } @@ -370,7 +401,9 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), key, iv, "", salt); } catch (...) { // todo change error handling in QSimpleCrypto? + Utils::logException(); qCritical() << "error when encrypting the request body"; + return ErrorCode::ApiConfigDecryptionError; } QJsonObject requestBody; @@ -416,7 +449,9 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co auto responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt); fillServerConfig(protocol, apiPayloadData, responseBody, serverConfig); } catch (...) { // todo change error handling in QSimpleCrypto? + Utils::logException(); qCritical() << "error when decrypting the request body"; + return ErrorCode::ApiConfigDecryptionError; } return errorCode; diff --git a/client/core/controllers/apiController.h b/client/core/controllers/apiController.h index 1f811498..bcb25f96 100644 --- a/client/core/controllers/apiController.h +++ b/client/core/controllers/apiController.h @@ -21,7 +21,7 @@ public slots: ErrorCode getServicesList(QByteArray &responseBody); ErrorCode getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType, - const QString &protocol, const QString &serverCountryCode, QJsonObject &serverConfig); + const QString &protocol, const QString &serverCountryCode, const QJsonObject &authData, QJsonObject &serverConfig); signals: void errorOccurred(ErrorCode errorCode); diff --git a/client/core/defs.h b/client/core/defs.h index ebc07f4b..d00d347b 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -96,6 +96,7 @@ namespace amnezia // import and install errors ImportInvalidConfigError = 900, + ImportOpenConfigError = 901, // Android errors AndroidError = 1000, @@ -107,6 +108,7 @@ namespace amnezia ApiConfigTimeoutError = 1103, ApiConfigSslError = 1104, ApiMissingAgwPublicKey = 1105, + ApiConfigDecryptionError = 1106, // QFile errors OpenError = 1200, diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index 8c16d786..49534606 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -50,6 +50,7 @@ QString errorString(ErrorCode code) { case (ErrorCode::AddressPoolError): errorMessage = QObject::tr("VPN pool error: no available addresses"); break; case (ErrorCode::ImportInvalidConfigError): errorMessage = QObject::tr("The config does not contain any containers and credentials for connecting to the server"); break; + case (ErrorCode::ImportOpenConfigError): errorMessage = QObject::tr("Unable to open config file"); break; // Android errors case (ErrorCode::AndroidError): errorMessage = QObject::tr("VPN connection error"); break; @@ -61,6 +62,7 @@ QString errorString(ErrorCode code) { case (ErrorCode::ApiConfigSslError): errorMessage = QObject::tr("SSL error occurred"); break; case (ErrorCode::ApiConfigTimeoutError): errorMessage = QObject::tr("Server response timeout on api request"); break; case (ErrorCode::ApiMissingAgwPublicKey): errorMessage = QObject::tr("Missing AGW public key"); break; + case (ErrorCode::ApiConfigDecryptionError): errorMessage = QObject::tr("Failed to decrypt response payload"); 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 3e237e9c..a234860b 100644 --- a/client/daemon/daemon.cpp +++ b/client/daemon/daemon.cpp @@ -78,7 +78,7 @@ bool Daemon::activate(const InterfaceConfig& config) { return false; } - if (supportDnsUtils() && !dnsutils()->restoreResolvers()) { + if (!dnsutils()->restoreResolvers()) { return false; } @@ -165,10 +165,6 @@ bool Daemon::activate(const InterfaceConfig& config) { } bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) { - if (!supportDnsUtils()) { - return true; - } - if ((config.m_hopType == InterfaceConfig::MultiHopExit) || (config.m_hopType == InterfaceConfig::SingleHop)) { QList resolvers; @@ -423,13 +419,8 @@ bool Daemon::deactivate(bool emitSignals) { } // Cleanup DNS - if (supportDnsUtils() && !dnsutils()->restoreResolvers()) { - return false; - } - - if (!wgutils()->interfaceExists()) { - logger.warning() << "Wireguard interface does not exist."; - return false; + if (!dnsutils()->restoreResolvers()) { + logger.warning() << "Failed to restore DNS resolvers."; } // Cleanup peers and routing @@ -449,13 +440,9 @@ bool Daemon::deactivate(bool emitSignals) { } m_excludedAddrSet.clear(); - // Delete the interface - if (!wgutils()->deleteInterface()) { - return false; - } - m_connections.clear(); - return true; + // Delete the interface + return wgutils()->deleteInterface(); } QString Daemon::logs() { diff --git a/client/daemon/daemon.h b/client/daemon/daemon.h index d3d8c34d..3d418d70 100644 --- a/client/daemon/daemon.h +++ b/client/daemon/daemon.h @@ -69,7 +69,6 @@ class Daemon : public QObject { virtual WireguardUtils* wgutils() const = 0; virtual bool supportIPUtils() const { return false; } virtual IPUtils* iputils() { return nullptr; } - virtual bool supportDnsUtils() const { return false; } virtual DnsUtils* dnsutils() { return nullptr; } static bool parseStringList(const QJsonObject& obj, const QString& name, diff --git a/client/daemon/daemonlocalserverconnection.cpp b/client/daemon/daemonlocalserverconnection.cpp index 1a49b7e5..edbc4c9b 100644 --- a/client/daemon/daemonlocalserverconnection.cpp +++ b/client/daemon/daemonlocalserverconnection.cpp @@ -92,6 +92,17 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) { logger.debug() << "Command received:" << type; + // It is expected that sometimes the client will request backend logs + // before the first authentication. In these cases we just return empty + // logs. + if (type == "logs") { + QJsonObject obj; + obj.insert("type", "logs"); + obj.insert("logs", ""); + write(obj); + return; + } + if (type == "activate") { InterfaceConfig config; if (!Daemon::parseConfig(obj, config)) { @@ -115,8 +126,7 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) { if (type == "status") { QJsonObject obj = Daemon::instance()->getStatus(); obj.insert("type", "status"); - m_socket->write(QJsonDocument(obj).toJson(QJsonDocument::Compact)); - m_socket->write("\n"); + write(obj); return; } @@ -124,8 +134,7 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) { QJsonObject obj; obj.insert("type", "logs"); obj.insert("logs", Daemon::instance()->logs().replace("\n", "|")); - m_socket->write(QJsonDocument(obj).toJson(QJsonDocument::Compact)); - m_socket->write("\n"); + write(obj); return; } diff --git a/client/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index 4d040288..5e9f0f97 100644 --- a/client/mozilla/localsocketcontroller.cpp +++ b/client/mozilla/localsocketcontroller.cpp @@ -34,8 +34,8 @@ LocalSocketController::LocalSocketController() { m_socket = new QLocalSocket(this); connect(m_socket, &QLocalSocket::connected, this, &LocalSocketController::daemonConnected); - connect(m_socket, &QLocalSocket::disconnected, this, - &LocalSocketController::disconnected); + connect(m_socket, &QLocalSocket::disconnected, this, + [&] { errorOccurred(QLocalSocket::PeerClosedError); }); connect(m_socket, &QLocalSocket::errorOccurred, this, &LocalSocketController::errorOccurred); connect(m_socket, &QLocalSocket::readyRead, this, diff --git a/client/platforms/linux/daemon/linuxdaemon.h b/client/platforms/linux/daemon/linuxdaemon.h index 7f5d27b7..dbac8cee 100644 --- a/client/platforms/linux/daemon/linuxdaemon.h +++ b/client/platforms/linux/daemon/linuxdaemon.h @@ -22,7 +22,6 @@ class LinuxDaemon final : public Daemon { protected: WireguardUtils* wgutils() const override { return m_wgutils; } - bool supportDnsUtils() const override { return true; } DnsUtils* dnsutils() override { return m_dnsutils; } bool supportIPUtils() const override { return true; } IPUtils* iputils() override { return m_iputils; } diff --git a/client/platforms/macos/daemon/macosdaemon.h b/client/platforms/macos/daemon/macosdaemon.h index a48c326c..4181648e 100644 --- a/client/platforms/macos/daemon/macosdaemon.h +++ b/client/platforms/macos/daemon/macosdaemon.h @@ -21,7 +21,6 @@ class MacOSDaemon final : public Daemon { protected: WireguardUtils* wgutils() const override { return m_wgutils; } - bool supportDnsUtils() const override { return true; } DnsUtils* dnsutils() override { return m_dnsutils; } bool supportIPUtils() const override { return true; } IPUtils* iputils() override { return m_iputils; } diff --git a/client/platforms/windows/daemon/windowsdaemon.h b/client/platforms/windows/daemon/windowsdaemon.h index 9d051bae..7e38c41e 100644 --- a/client/platforms/windows/daemon/windowsdaemon.h +++ b/client/platforms/windows/daemon/windowsdaemon.h @@ -26,7 +26,6 @@ class WindowsDaemon final : public Daemon { protected: bool run(Op op, const InterfaceConfig& config) override; WireguardUtils* wgutils() const override { return m_wgutils; } - bool supportDnsUtils() const override { return true; } DnsUtils* dnsutils() override { return m_dnsutils; } private: diff --git a/client/platforms/windows/daemon/windowssplittunnel.cpp b/client/platforms/windows/daemon/windowssplittunnel.cpp index 39941933..c4e893b2 100644 --- a/client/platforms/windows/daemon/windowssplittunnel.cpp +++ b/client/platforms/windows/daemon/windowssplittunnel.cpp @@ -502,7 +502,7 @@ QString WindowsSplitTunnel::convertPath(const QString& path) { // device should contain : for e.g C: return ""; } - QByteArray buffer(2048, 0xFF); + QByteArray buffer(2048, 0xFFu); auto ok = QueryDosDeviceW(qUtf16Printable(driveLetter), (wchar_t*)buffer.data(), buffer.size() / 2); diff --git a/client/platforms/windows/windowscommons.cpp b/client/platforms/windows/windowscommons.cpp index c0a14dda..4c0d8176 100644 --- a/client/platforms/windows/windowscommons.cpp +++ b/client/platforms/windows/windowscommons.cpp @@ -21,7 +21,7 @@ #include "platforms/windows/windowsutils.h" constexpr const char* VPN_NAME = "AmneziaVPN"; -constexpr const char* WIREGUARD_DIR = "WireGuard"; +constexpr const char* WIREGUARD_DIR = "AmneziaWG"; constexpr const char* DATA_DIR = "Data"; namespace { diff --git a/client/ui/controllers/connectionController.cpp b/client/ui/controllers/connectionController.cpp index c7f95000..a51556a1 100644 --- a/client/ui/controllers/connectionController.cpp +++ b/client/ui/controllers/connectionController.cpp @@ -51,6 +51,9 @@ void ConnectionController::openConnection() if (configVersion == ApiConfigSources::Telegram && !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) { emit updateApiConfigFromTelegram(); + } else if (configVersion == ApiConfigSources::AmneziaGateway + && !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) { + emit updateApiConfigFromGateway(); } else if (configVersion && m_serversModel->isApiKeyExpired(serverIndex)) { qDebug() << "attempt to update api config by end_date event"; if (configVersion == ApiConfigSources::Telegram) { diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index 261551ea..f7e96bff 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -39,11 +39,12 @@ namespace const QString amneziaConfigPatternUserName = "userName"; const QString amneziaConfigPatternPassword = "password"; const QString amneziaFreeConfigPattern = "api_key"; + const QString amneziaPremiumConfigPattern = "auth_data"; const QString backupPattern = "Servers/serversList"; if (config.contains(backupPattern)) { return ConfigTypes::Backup; - } else if (config.contains(amneziaConfigPattern) || config.contains(amneziaFreeConfigPattern) + } else if (config.contains(amneziaConfigPattern) || config.contains(amneziaFreeConfigPattern) || config.contains(amneziaPremiumConfigPattern) || (config.contains(amneziaConfigPatternHostName) && config.contains(amneziaConfigPatternUserName) && config.contains(amneziaConfigPatternPassword))) { return ConfigTypes::Amnezia; @@ -84,7 +85,7 @@ bool ImportController::extractConfigFromFile(const QString &fileName) return extractConfigFromData(data); } - emit importErrorOccurred(tr("Unable to open file"), false); + emit importErrorOccurred(ErrorCode::ImportOpenConfigError, false); return false; } @@ -188,12 +189,12 @@ bool ImportController::extractConfigFromData(QString data) if (!m_serversModel->getServersCount()) { emit restoreAppConfig(config.toUtf8()); } else { - emit importErrorOccurred(tr("Invalid configuration file"), false); + emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false); } break; } case ConfigTypes::Invalid: { - emit importErrorOccurred(tr("Invalid configuration file"), false); + emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false); break; } } diff --git a/client/ui/controllers/importController.h b/client/ui/controllers/importController.h index 61205253..05e320a5 100644 --- a/client/ui/controllers/importController.h +++ b/client/ui/controllers/importController.h @@ -54,7 +54,6 @@ public slots: signals: void importFinished(); - void importErrorOccurred(const QString &errorMessage, bool goToPageHome); void importErrorOccurred(ErrorCode errorCode, bool goToPageHome); void qrDecodingFinished(); diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index c6f17057..537eedc0 100644 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -32,6 +32,7 @@ namespace constexpr char availableCountries[] = "available_countries"; constexpr char apiConfig[] = "api_config"; + constexpr char authData[] = "auth_data"; } #ifdef Q_OS_WINDOWS @@ -135,10 +136,10 @@ void InstallController::install(DockerContainer container, int port, TransportPr containerConfig[config_key::transportPacketMagicHeader] = transportPacketMagicHeader; } else if (container == DockerContainer::Sftp) { containerConfig.insert(config_key::userName, protocols::sftp::defaultUserName); - containerConfig.insert(config_key::password, Utils::getRandomString(10)); + containerConfig.insert(config_key::password, Utils::getRandomString(16)); } else if (container == DockerContainer::Socks5Proxy) { containerConfig.insert(config_key::userName, protocols::socks5Proxy::defaultUserName); - containerConfig.insert(config_key::password, Utils::getRandomString(10)); + containerConfig.insert(config_key::password, Utils::getRandomString(16)); } config.insert(config_key::container, ContainerProps::containerToString(container)); @@ -768,7 +769,7 @@ bool InstallController::checkSshConnection(QSharedPointer serv } else { if (output.contains(tr("Please login as the user"))) { output.replace("\n", ""); - emit installationErrorOccurred(output); + emit wrongInstallationUser(output); return false; } } @@ -826,7 +827,7 @@ bool InstallController::installServiceFromApi() ErrorCode errorCode = apiController.getConfigForService(m_settings->getInstallationUuid(true), m_apiServicesModel->getCountryCode(), m_apiServicesModel->getSelectedServiceType(), - m_apiServicesModel->getSelectedServiceProtocol(), "", serverConfig); + m_apiServicesModel->getSelectedServiceProtocol(), "", QJsonObject(), serverConfig); if (errorCode != ErrorCode::NoError) { emit installationErrorOccurred(errorCode); return false; @@ -853,19 +854,19 @@ bool InstallController::updateServiceFromApi(const int serverIndex, const QStrin auto serverConfig = m_serversModel->getServerConfig(serverIndex); auto apiConfig = serverConfig.value(configKey::apiConfig).toObject(); + auto authData = serverConfig.value(configKey::authData).toObject(); QJsonObject newServerConfig; - ErrorCode errorCode = - apiController.getConfigForService(m_settings->getInstallationUuid(true), apiConfig.value(configKey::userCountryCode).toString(), - apiConfig.value(configKey::serviceType).toString(), - apiConfig.value(configKey::serviceProtocol).toString(), newCountryCode, newServerConfig); + ErrorCode errorCode = apiController.getConfigForService( + m_settings->getInstallationUuid(true), apiConfig.value(configKey::userCountryCode).toString(), + apiConfig.value(configKey::serviceType).toString(), apiConfig.value(configKey::serviceProtocol).toString(), newCountryCode, + authData, newServerConfig); if (errorCode != ErrorCode::NoError) { emit installationErrorOccurred(errorCode); return false; } QJsonObject newApiConfig = newServerConfig.value(configKey::apiConfig).toObject(); - newApiConfig.insert(configKey::serviceInfo, apiConfig.value(configKey::serviceInfo)); newApiConfig.insert(configKey::userCountryCode, apiConfig.value(configKey::userCountryCode)); newApiConfig.insert(configKey::serviceType, apiConfig.value(configKey::serviceType)); newApiConfig.insert(configKey::serviceProtocol, apiConfig.value(configKey::serviceProtocol)); diff --git a/client/ui/controllers/installController.h b/client/ui/controllers/installController.h index 7eea216a..d7ab3553 100644 --- a/client/ui/controllers/installController.h +++ b/client/ui/controllers/installController.h @@ -75,8 +75,8 @@ signals: void removeAllContainersFinished(const QString &finishedMessage); void removeProcessedContainerFinished(const QString &finishedMessage); - void installationErrorOccurred(const QString &errorMessage); void installationErrorOccurred(ErrorCode errorCode); + void wrongInstallationUser(const QString &message); void serverAlreadyExists(int serverIndex); diff --git a/client/ui/models/clientManagementModel.cpp b/client/ui/models/clientManagementModel.cpp index 7d3be2cb..7445d60f 100644 --- a/client/ui/models/clientManagementModel.cpp +++ b/client/ui/models/clientManagementModel.cpp @@ -77,6 +77,7 @@ ErrorCode ClientManagementModel::updateModel(const DockerContainer container, co { beginResetModel(); m_clientsTable = QJsonArray(); + endResetModel(); ErrorCode error = ErrorCode::NoError; @@ -90,10 +91,10 @@ ErrorCode ClientManagementModel::updateModel(const DockerContainer container, co const QByteArray clientsTableString = serverController->getTextFileFromContainer(container, credentials, clientsTableFile, error); if (error != ErrorCode::NoError) { logger.error() << "Failed to get the clientsTable file from the server"; - endResetModel(); return error; } + beginResetModel(); m_clientsTable = QJsonDocument::fromJson(clientsTableString).array(); if (m_clientsTable.isEmpty()) { @@ -601,5 +602,6 @@ QHash ClientManagementModel::roleNames() const roles[LatestHandshakeRole] = "latestHandshake"; roles[DataReceivedRole] = "dataReceived"; roles[DataSentRole] = "dataSent"; + roles[AllowedIpsRole] = "allowedIps"; return roles; } diff --git a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml index 3aac1555..92048f36 100644 --- a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml +++ b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml @@ -37,7 +37,7 @@ PageType { Connections { target: ImportController - function onImportErrorOccurred(errorMessage, goToPageHome) { + function onImportErrorOccurred(error, goToPageHome) { if (goToPageHome) { PageController.goToStartPage() } else { diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index bb6663fb..640c61ef 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -123,6 +123,10 @@ PageType { } } + function onWrongInstallationUser(message) { + onInstallationErrorOccurred(message) + } + function onUpdateContainerFinished(message) { PageController.showNotificationMessage(message) PageController.closePage() diff --git a/client/utilities.cpp b/client/utilities.cpp index 4047365f..66aa6e7a 100644 --- a/client/utilities.cpp +++ b/client/utilities.cpp @@ -14,14 +14,13 @@ QString Utils::getRandomString(int len) { - const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - + const QString possibleCharacters = QStringLiteral("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); QString randomString; + for (int i = 0; i < len; ++i) { - quint32 index = QRandomGenerator::global()->generate() % possibleCharacters.length(); - QChar nextChar = possibleCharacters.at(index); - randomString.append(nextChar); + randomString.append(possibleCharacters.at(QRandomGenerator::system()->bounded(possibleCharacters.length()))); } + return randomString; } @@ -244,3 +243,22 @@ bool Utils::signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent) } #endif + +void Utils::logException(const std::exception &e) +{ + qCritical() << e.what(); + try { + std::rethrow_if_nested(e); + } catch (const std::exception &nested) { + logException(nested); + } catch (...) {} +} + +void Utils::logException(const std::exception_ptr &eptr) +{ + try { + if (eptr) std::rethrow_exception(eptr); + } catch (const std::exception &e) { + logException(e); + } catch (...) {} +} diff --git a/client/utilities.h b/client/utilities.h index 9bf8c82a..2a17a1f4 100644 --- a/client/utilities.h +++ b/client/utilities.h @@ -34,6 +34,9 @@ public: static QString certUtilPath(); static QString tun2socksPath(); + static void logException(const std::exception &e); + static void logException(const std::exception_ptr &eptr = std::current_exception()); + #ifdef Q_OS_WIN static bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent); #endif