Merge branch 'dev' into android-7

# Conflicts:
#	.github/workflows/deploy.yml
#	CMakeLists.txt
#	client/android/src/org/amnezia/vpn/AmneziaActivity.kt
#	client/android/src/org/amnezia/vpn/AmneziaVpnService.kt
#	client/android/src/org/amnezia/vpn/ServiceNotification.kt
#	client/android/utils/src/main/kotlin/Log.kt
#	client/android/utils/src/main/kotlin/net/NetworkState.kt
This commit is contained in:
albexk 2024-10-22 12:51:10 +03:00
commit 5e80223e7a
40 changed files with 252 additions and 112 deletions

View file

@ -16,7 +16,10 @@ jobs:
QT_VERSION: 6.6.2 QT_VERSION: 6.6.2
QIF_VERSION: 4.7 QIF_VERSION: 4.7
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} 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_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps: steps:
- name: 'Install Qt' - name: 'Install Qt'
@ -83,7 +86,10 @@ jobs:
QIF_VERSION: 4.7 QIF_VERSION: 4.7
BUILD_ARCH: 64 BUILD_ARCH: 64
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} 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_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps: steps:
- name: 'Get sources' - name: 'Get sources'
@ -146,7 +152,10 @@ jobs:
CC: cc CC: cc
CXX: c++ CXX: c++
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} 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_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps: steps:
- name: 'Setup xcode' - name: 'Setup xcode'
@ -238,7 +247,10 @@ jobs:
QT_VERSION: 6.4.3 QT_VERSION: 6.4.3
QIF_VERSION: 4.6 QIF_VERSION: 4.6
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} 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_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps: steps:
- name: 'Setup xcode' - name: 'Setup xcode'
@ -304,7 +316,10 @@ jobs:
QT_VERSION: 6.6.3 QT_VERSION: 6.6.3
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools' QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} 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_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps: steps:
- name: 'Install desktop Qt' - name: 'Install desktop Qt'

View file

@ -16,7 +16,10 @@ jobs:
QT_VERSION: 6.4.1 QT_VERSION: 6.4.1
QIF_VERSION: 4.5 QIF_VERSION: 4.5
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} 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_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
steps: steps:
- name: 'Install desktop Qt' - name: 'Install desktop Qt'

View file

@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}") set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 1066) set(APP_ANDROID_VERSION_CODE 1067)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux") set(MZ_PLATFORM_NAME "linux")

View file

@ -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_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}")
add_definitions(-DPROD_PROXY_STORAGE_KEY="$ENV{PROD_PROXY_STORAGE_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_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}") add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}")
if(IOS) if(IOS)
set(PACKAGES ${PACKAGES} Multimedia) set(PACKAGES ${PACKAGES} Multimedia)

View file

@ -111,10 +111,11 @@ void AmneziaApplication::init()
qFatal("Android controller initialization failed"); qFatal("Android controller initialization failed");
} }
connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, [this](QString data) { connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, this, [this](QString data) {
m_pageController->goToPageHome(); emit m_pageController->goToPageHome();
m_importController->extractConfigFromData(data); m_importController->extractConfigFromData(data);
m_pageController->goToPageViewConfig(); data.clear();
emit m_pageController->goToPageViewConfig();
}); });
m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider); m_engine->addImageProvider(QLatin1String("installedAppImage"), new InstalledAppsImageProvider);
@ -122,16 +123,16 @@ void AmneziaApplication::init()
#ifdef Q_OS_IOS #ifdef Q_OS_IOS
IosController::Instance()->initialize(); IosController::Instance()->initialize();
connect(IosController::Instance(), &IosController::importConfigFromOutside, [this](QString data) { connect(IosController::Instance(), &IosController::importConfigFromOutside, this, [this](QString data) {
m_pageController->goToPageHome(); emit m_pageController->goToPageHome();
m_importController->extractConfigFromData(data); m_importController->extractConfigFromData(data);
m_pageController->goToPageViewConfig(); emit m_pageController->goToPageViewConfig();
}); });
connect(IosController::Instance(), &IosController::importBackupFromOutside, [this](QString filePath) { connect(IosController::Instance(), &IosController::importBackupFromOutside, this, [this](QString filePath) {
m_pageController->goToPageHome(); emit m_pageController->goToPageHome();
m_pageController->goToPageSettingsBackup(); m_pageController->goToPageSettingsBackup();
m_settingsController->importBackupFromOutside(filePath); emit m_settingsController->importBackupFromOutside(filePath);
}); });
QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); }); QTimer::singleShot(0, this, [this]() { AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); });

View file

@ -33,7 +33,7 @@ android.library.defaults.buildfeatures.androidresources=false
# For development copy and set local values for these parameters in local.properties # For development copy and set local values for these parameters in local.properties
#androidCompileSdkVersion=android-34 #androidCompileSdkVersion=android-34
#androidBuildToolsVersion=34.0.0 #androidBuildToolsVersion=34.0.0
#qtMinSdkVersion=24 #qtMinSdkVersion=26
#qtTargetSdkVersion=34 #qtTargetSdkVersion=34
#androidNdkVersion=26.1.10909125 #androidNdkVersion=26.1.10909125
#qtTargetAbiList=x86_64 #qtTargetAbiList=x86_64

View file

@ -1,6 +1,5 @@
package org.amnezia.vpn.protocol package org.amnezia.vpn.protocol
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.net.IpPrefix import android.net.IpPrefix
import android.net.VpnService import android.net.VpnService
@ -8,9 +7,6 @@ import android.net.VpnService.Builder
import android.os.Build import android.os.Build
import android.system.OsConstants import android.system.OsConstants
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import java.io.File
import java.io.FileOutputStream
import java.util.zip.ZipFile
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import org.amnezia.vpn.util.Log import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.net.InetNetwork import org.amnezia.vpn.util.net.InetNetwork

View file

@ -19,6 +19,7 @@ import android.os.Looper
import android.os.Message import android.os.Message
import android.os.Messenger import android.os.Messenger
import android.provider.Settings import android.provider.Settings
import android.view.MotionEvent
import android.view.WindowManager.LayoutParams import android.view.WindowManager.LayoutParams
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import android.widget.Toast import android.widget.Toast
@ -155,7 +156,7 @@ class AmneziaActivity : QtActivity() {
*/ */
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Log.d(TAG, "Create Amnezia activity: $intent") Log.d(TAG, "Create Amnezia activity")
loadLibs() loadLibs()
window.apply { window.apply {
addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
@ -190,7 +191,7 @@ class AmneziaActivity : QtActivity() {
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent) super.onNewIntent(intent)
Log.d(TAG, "onNewIntent: $intent") Log.v(TAG, "onNewIntent: $intent")
intent?.let(::processIntent) intent?.let(::processIntent)
} }
@ -377,7 +378,7 @@ class AmneziaActivity : QtActivity() {
@MainThread @MainThread
private fun startVpn(vpnConfig: String) { private fun startVpn(vpnConfig: String) {
getVpnProto(vpnConfig)?.let { proto -> 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 (isServiceConnected) {
if (proto.serviceClass == vpnProto?.serviceClass) { if (proto.serviceClass == vpnProto?.serviceClass) {
vpnProto = proto vpnProto = proto
@ -490,7 +491,7 @@ class AmneziaActivity : QtActivity() {
startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler( startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler(
onSuccess = { onSuccess = {
it?.data?.let { uri -> it?.data?.let { uri ->
Log.d(TAG, "Save file to $uri") Log.v(TAG, "Save file to $uri")
try { try {
contentResolver.openOutputStream(uri)?.use { os -> contentResolver.openOutputStream(uri)?.use { os ->
os.bufferedWriter().use { it.write(data) } os.bufferedWriter().use { it.write(data) }
@ -539,7 +540,7 @@ class AmneziaActivity : QtActivity() {
startActivityForResult(it, OPEN_FILE_ACTION_CODE, ActivityResultHandler( startActivityForResult(it, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
onAny = { onAny = {
val uri = it?.data?.toString() ?: "" val uri = it?.data?.toString() ?: ""
Log.d(TAG, "Open file: $uri") Log.v(TAG, "Open file: $uri")
mainScope.launch { mainScope.launch {
qtInitialized.await() qtInitialized.await()
QtAndroidController.onFileOpened(uri) 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 * Utils methods
*/ */

View file

@ -285,7 +285,7 @@ open class AmneziaVpnService : VpnService() {
arrayOf(ACTION_CONNECT, ACTION_DISCONNECT), ContextCompat.RECEIVER_NOT_EXPORTED arrayOf(ACTION_CONNECT, ACTION_DISCONNECT), ContextCompat.RECEIVER_NOT_EXPORTED
) { ) {
it?.action?.let { action -> it?.action?.let { action ->
Log.d(TAG, "Broadcast request received: $action") Log.v(TAG, "Broadcast request received: $action")
when (action) { when (action) {
ACTION_CONNECT -> connect() ACTION_CONNECT -> connect()
ACTION_DISCONNECT -> disconnect() ACTION_DISCONNECT -> disconnect()
@ -416,7 +416,7 @@ open class AmneziaVpnService : VpnService() {
serviceNotification.isNotificationEnabled() && serviceNotification.isNotificationEnabled() &&
getSystemService<PowerManager>()?.isInteractive != false getSystemService<PowerManager>()?.isInteractive != false
) { ) {
Log.d(TAG, "Launch traffic stats update") Log.v(TAG, "Launch traffic stats update")
trafficStats.reset() trafficStats.reset()
startTrafficStatsUpdateJob() startTrafficStatsUpdateJob()
} }

View file

@ -66,7 +66,7 @@ class AuthActivity : FragmentActivity() {
object : BiometricPrompt.AuthenticationCallback() { object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: AuthenticationResult) { override fun onAuthenticationSucceeded(result: AuthenticationResult) {
super.onAuthenticationSucceeded(result) super.onAuthenticationSucceeded(result)
Log.d(TAG, "Authentication succeeded") Log.v(TAG, "Authentication succeeded")
QtAndroidController.onAuthResult(true) QtAndroidController.onAuthResult(true)
finish() finish()
} }

View file

@ -29,20 +29,20 @@ class ImportConfigActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Log.d(TAG, "Create Import Config Activity: $intent") Log.v(TAG, "Create Import Config Activity: $intent")
intent?.let(::readConfig) intent?.let(::readConfig)
} }
override fun onNewIntent(intent: Intent) { override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent) super.onNewIntent(intent)
Log.d(TAG, "onNewIntent: $intent") Log.v(TAG, "onNewIntent: $intent")
intent.let(::readConfig) intent.let(::readConfig)
} }
private fun readConfig(intent: Intent) { private fun readConfig(intent: Intent) {
when (intent.action) { when (intent.action) {
ACTION_SEND -> { ACTION_SEND -> {
Log.d(TAG, "Process SEND action, type: ${intent.type}") Log.v(TAG, "Process SEND action, type: ${intent.type}")
when (intent.type) { when (intent.type) {
"application/octet-stream" -> { "application/octet-stream" -> {
intent.getUriCompat()?.let { uri -> intent.getUriCompat()?.let { uri ->
@ -60,7 +60,7 @@ class ImportConfigActivity : ComponentActivity() {
} }
ACTION_VIEW -> { ACTION_VIEW -> {
Log.d(TAG, "Process VIEW action, scheme: ${intent.scheme}") Log.v(TAG, "Process VIEW action, scheme: ${intent.scheme}")
when (intent.scheme) { when (intent.scheme) {
"file", "content" -> { "file", "content" -> {
intent.data?.let { uri -> intent.data?.let { uri ->

View file

@ -58,7 +58,7 @@ class ServiceNotification(private val context: Context) {
fun buildNotification(serverName: String?, protocol: String?, state: ProtocolState): Notification { fun buildNotification(serverName: String?, protocol: String?, state: ProtocolState): Notification {
val speedString = if (state == CONNECTED) zeroSpeed else null val speedString = if (state == CONNECTED) zeroSpeed else null
Log.d(TAG, "Build notification: $serverName, $state") Log.v(TAG, "Build notification: $serverName, $state")
return notificationBuilder return notificationBuilder
.setSmallIcon(R.drawable.ic_amnezia_round) .setSmallIcon(R.drawable.ic_amnezia_round)
@ -85,7 +85,7 @@ class ServiceNotification(private val context: Context) {
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
fun updateNotification(serverName: String?, protocol: String?, state: ProtocolState) { 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)) notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, protocol, state))
} }

View file

@ -46,7 +46,7 @@ object LibraryLoader {
System.loadLibrary(libraryName) System.loadLibrary(libraryName)
return return
} catch (_: UnsatisfiedLinkError) { } 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 var tempFile: File? = null
try { try {

View file

@ -40,11 +40,11 @@ class NetworkState(
private val networkCallback: NetworkCallback by lazy(NONE) { private val networkCallback: NetworkCallback by lazy(NONE) {
object : NetworkCallback() { object : NetworkCallback() {
override fun onAvailable(network: Network) { override fun onAvailable(network: Network) {
Log.d(TAG, "onAvailable: $network") Log.v(TAG, "onAvailable: $network")
} }
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
Log.d(TAG, "onCapabilitiesChanged: $network, $networkCapabilities") Log.v(TAG, "onCapabilitiesChanged: $network, $networkCapabilities")
handler.post { handler.post {
checkNetworkState(network, networkCapabilities) checkNetworkState(network, networkCapabilities)
} }
@ -67,11 +67,11 @@ class NetworkState(
} }
override fun onBlockedStatusChanged(network: Network, blocked: Boolean) { override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
Log.d(TAG, "onBlockedStatusChanged: $network, $blocked") Log.v(TAG, "onBlockedStatusChanged: $network, $blocked")
} }
override fun onLost(network: Network) { override fun onLost(network: Network) {
Log.d(TAG, "onLost: $network") Log.v(TAG, "onLost: $network")
} }
} }
} }

View file

@ -66,7 +66,7 @@ open class Wireguard : Protocol() {
try { try {
delay(1000) delay(1000)
var log = getLogcat(time) 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, // check that there is a connection log,
// to avoid infinite connection // to avoid infinite connection
if (!log.contains("Attaching to interface")) { if (!log.contains("Attaching to interface")) {

View file

@ -130,8 +130,8 @@ class Xray : Protocol() {
LibXray.initXray(assetsPath) LibXray.initXray(assetsPath)
val geoDir = File(assetsPath, "geo").absolutePath val geoDir = File(assetsPath, "geo").absolutePath
val configPath = File(context.cacheDir, "config.json") val configPath = File(context.cacheDir, "config.json")
Log.d(TAG, "xray.location.asset: $geoDir") Log.v(TAG, "xray.location.asset: $geoDir")
Log.d(TAG, "config: $configPath") Log.v(TAG, "config: $configPath")
try { try {
configPath.writeText(configJson) configPath.writeText(configJson)
} catch (e: IOException) { } catch (e: IOException) {

View file

@ -1,6 +1,6 @@
message("Client android ${CMAKE_ANDROID_ARCH_ABI} build") 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 set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING
"The minimum API level supported by the application or library" FORCE) "The minimum API level supported by the application or library" FORCE)

View file

@ -12,6 +12,7 @@
#include "configurators/wireguard_configurator.h" #include "configurators/wireguard_configurator.h"
#include "core/enums/apiEnums.h" #include "core/enums/apiEnums.h"
#include "version.h" #include "version.h"
#include "utilities.h"
namespace namespace
{ {
@ -40,9 +41,10 @@ namespace
constexpr char apiPayload[] = "api_payload"; constexpr char apiPayload[] = "api_payload";
constexpr char keyPayload[] = "key_payload"; constexpr char keyPayload[] = "key_payload";
}
const QStringList proxyStorageUrl = { "" }; constexpr char apiConfig[] = "api_config";
constexpr char authData[] = "auth_data";
}
ErrorCode checkErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply) ErrorCode checkErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply)
{ {
@ -94,8 +96,8 @@ void ApiController::fillServerConfig(const QString &protocol, const ApiControlle
configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey); configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey);
} else if (protocol == configKey::awg) { } else if (protocol == configKey::awg) {
configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey); configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
auto serverConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); auto newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
auto containers = serverConfig.value(config_key::containers).toArray(); auto containers = newServerConfig.value(config_key::containers).toArray();
if (containers.isEmpty()) { if (containers.isEmpty()) {
return; // todo process error 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); containerConfig[config_key::transportPacketMagicHeader] = protocolConfig.value(config_key::transportPacketMagicHeader);
container[containerName] = containerConfig; container[containerName] = containerConfig;
containers.replace(0, container); containers.replace(0, container);
serverConfig[config_key::containers] = containers; newServerConfig[config_key::containers] = containers;
configStr = QString(QJsonDocument(serverConfig).toJson()); configStr = QString(QJsonDocument(newServerConfig).toJson());
} }
QJsonObject apiConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); QJsonObject newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
serverConfig[config_key::dns1] = apiConfig.value(config_key::dns1); serverConfig[config_key::dns1] = newServerConfig.value(config_key::dns1);
serverConfig[config_key::dns2] = apiConfig.value(config_key::dns2); serverConfig[config_key::dns2] = newServerConfig.value(config_key::dns2);
serverConfig[config_key::containers] = apiConfig.value(config_key::containers); serverConfig[config_key::containers] = newServerConfig.value(config_key::containers);
serverConfig[config_key::hostName] = apiConfig.value(config_key::hostName); serverConfig[config_key::hostName] = newServerConfig.value(config_key::hostName);
if (apiConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) { if (newServerConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) {
serverConfig[config_key::configVersion] = apiConfig.value(config_key::configVersion); serverConfig[config_key::configVersion] = newServerConfig.value(config_key::configVersion);
serverConfig[config_key::description] = apiConfig.value(config_key::description); serverConfig[config_key::description] = newServerConfig.value(config_key::description);
serverConfig[config_key::name] = apiConfig.value(config_key::name); 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; 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; return;
} }
@ -146,6 +153,15 @@ QStringList ApiController::getProxyUrls()
QList<QSslError> sslErrors; QList<QSslError> sslErrors;
QNetworkReply *reply; 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) { for (const auto &proxyStorageUrl : proxyStorageUrl) {
request.setUrl(proxyStorageUrl); request.setUrl(proxyStorageUrl);
reply = amnApp->manager()->get(request); reply = amnApp->manager()->get(request);
@ -166,11 +182,23 @@ QStringList ApiController::getProxyUrls()
EVP_PKEY *privateKey = nullptr; EVP_PKEY *privateKey = nullptr;
QByteArray responseBody; QByteArray responseBody;
try { try {
QByteArray key = PROD_PROXY_STORAGE_KEY; if (!m_isDevEnvironment) {
QSimpleCrypto::QRsa rsa; QCryptographicHash hash(QCryptographicHash::Sha512);
privateKey = rsa.getPrivateKeyFromByteArray(key, ""); hash.addData(key);
responseBody = rsa.decrypt(encryptedResponseBody, privateKey, RSA_PKCS1_PADDING); 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 (...) { } catch (...) {
Utils::logException();
qCritical() << "error loading private key from environment variables or decrypting payload"; qCritical() << "error loading private key from environment variables or decrypting payload";
return {}; return {};
} }
@ -316,7 +344,8 @@ ErrorCode ApiController::getServicesList(QByteArray &responseBody)
} }
ErrorCode ApiController::getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType, 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 #ifdef Q_OS_IOS
IosController::Instance()->requestInetAccess(); IosController::Instance()->requestInetAccess();
@ -339,6 +368,7 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
} }
apiPayload[configKey::serviceType] = serviceType; apiPayload[configKey::serviceType] = serviceType;
apiPayload[configKey::uuid] = installationUuid; apiPayload[configKey::uuid] = installationUuid;
apiPayload[configKey::authData] = authData;
QSimpleCrypto::QBlockCipher blockCipher; QSimpleCrypto::QBlockCipher blockCipher;
QByteArray key = blockCipher.generatePrivateSalt(32); QByteArray key = blockCipher.generatePrivateSalt(32);
@ -361,6 +391,7 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
QSimpleCrypto::QRsa rsa; QSimpleCrypto::QRsa rsa;
publicKey = rsa.getPublicKeyFromByteArray(rsaKey); publicKey = rsa.getPublicKeyFromByteArray(rsaKey);
} catch (...) { } catch (...) {
Utils::logException();
qCritical() << "error loading public key from environment variables"; qCritical() << "error loading public key from environment variables";
return ErrorCode::ApiMissingAgwPublicKey; return ErrorCode::ApiMissingAgwPublicKey;
} }
@ -370,7 +401,9 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), key, iv, "", salt); encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), key, iv, "", salt);
} catch (...) { // todo change error handling in QSimpleCrypto? } catch (...) { // todo change error handling in QSimpleCrypto?
Utils::logException();
qCritical() << "error when encrypting the request body"; qCritical() << "error when encrypting the request body";
return ErrorCode::ApiConfigDecryptionError;
} }
QJsonObject requestBody; QJsonObject requestBody;
@ -416,7 +449,9 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
auto responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt); auto responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
fillServerConfig(protocol, apiPayloadData, responseBody, serverConfig); fillServerConfig(protocol, apiPayloadData, responseBody, serverConfig);
} catch (...) { // todo change error handling in QSimpleCrypto? } catch (...) { // todo change error handling in QSimpleCrypto?
Utils::logException();
qCritical() << "error when decrypting the request body"; qCritical() << "error when decrypting the request body";
return ErrorCode::ApiConfigDecryptionError;
} }
return errorCode; return errorCode;

View file

@ -21,7 +21,7 @@ public slots:
ErrorCode getServicesList(QByteArray &responseBody); ErrorCode getServicesList(QByteArray &responseBody);
ErrorCode getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType, 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: signals:
void errorOccurred(ErrorCode errorCode); void errorOccurred(ErrorCode errorCode);

View file

@ -96,6 +96,7 @@ namespace amnezia
// import and install errors // import and install errors
ImportInvalidConfigError = 900, ImportInvalidConfigError = 900,
ImportOpenConfigError = 901,
// Android errors // Android errors
AndroidError = 1000, AndroidError = 1000,
@ -107,6 +108,7 @@ namespace amnezia
ApiConfigTimeoutError = 1103, ApiConfigTimeoutError = 1103,
ApiConfigSslError = 1104, ApiConfigSslError = 1104,
ApiMissingAgwPublicKey = 1105, ApiMissingAgwPublicKey = 1105,
ApiConfigDecryptionError = 1106,
// QFile errors // QFile errors
OpenError = 1200, OpenError = 1200,

View file

@ -50,6 +50,7 @@ QString errorString(ErrorCode code) {
case (ErrorCode::AddressPoolError): errorMessage = QObject::tr("VPN pool error: no available addresses"); break; 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::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 // Android errors
case (ErrorCode::AndroidError): errorMessage = QObject::tr("VPN connection error"); break; 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::ApiConfigSslError): errorMessage = QObject::tr("SSL error occurred"); break;
case (ErrorCode::ApiConfigTimeoutError): errorMessage = QObject::tr("Server response timeout on api request"); 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::ApiMissingAgwPublicKey): errorMessage = QObject::tr("Missing AGW public key"); break;
case (ErrorCode::ApiConfigDecryptionError): errorMessage = QObject::tr("Failed to decrypt response payload"); break;
// QFile errors // QFile errors
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break; case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;

View file

@ -78,7 +78,7 @@ bool Daemon::activate(const InterfaceConfig& config) {
return false; return false;
} }
if (supportDnsUtils() && !dnsutils()->restoreResolvers()) { if (!dnsutils()->restoreResolvers()) {
return false; return false;
} }
@ -165,10 +165,6 @@ bool Daemon::activate(const InterfaceConfig& config) {
} }
bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) { bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) {
if (!supportDnsUtils()) {
return true;
}
if ((config.m_hopType == InterfaceConfig::MultiHopExit) || if ((config.m_hopType == InterfaceConfig::MultiHopExit) ||
(config.m_hopType == InterfaceConfig::SingleHop)) { (config.m_hopType == InterfaceConfig::SingleHop)) {
QList<QHostAddress> resolvers; QList<QHostAddress> resolvers;
@ -423,13 +419,8 @@ bool Daemon::deactivate(bool emitSignals) {
} }
// Cleanup DNS // Cleanup DNS
if (supportDnsUtils() && !dnsutils()->restoreResolvers()) { if (!dnsutils()->restoreResolvers()) {
return false; logger.warning() << "Failed to restore DNS resolvers.";
}
if (!wgutils()->interfaceExists()) {
logger.warning() << "Wireguard interface does not exist.";
return false;
} }
// Cleanup peers and routing // Cleanup peers and routing
@ -449,13 +440,9 @@ bool Daemon::deactivate(bool emitSignals) {
} }
m_excludedAddrSet.clear(); m_excludedAddrSet.clear();
// Delete the interface
if (!wgutils()->deleteInterface()) {
return false;
}
m_connections.clear(); m_connections.clear();
return true; // Delete the interface
return wgutils()->deleteInterface();
} }
QString Daemon::logs() { QString Daemon::logs() {

View file

@ -69,7 +69,6 @@ class Daemon : public QObject {
virtual WireguardUtils* wgutils() const = 0; virtual WireguardUtils* wgutils() const = 0;
virtual bool supportIPUtils() const { return false; } virtual bool supportIPUtils() const { return false; }
virtual IPUtils* iputils() { return nullptr; } virtual IPUtils* iputils() { return nullptr; }
virtual bool supportDnsUtils() const { return false; }
virtual DnsUtils* dnsutils() { return nullptr; } virtual DnsUtils* dnsutils() { return nullptr; }
static bool parseStringList(const QJsonObject& obj, const QString& name, static bool parseStringList(const QJsonObject& obj, const QString& name,

View file

@ -92,6 +92,17 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
logger.debug() << "Command received:" << type; 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") { if (type == "activate") {
InterfaceConfig config; InterfaceConfig config;
if (!Daemon::parseConfig(obj, config)) { if (!Daemon::parseConfig(obj, config)) {
@ -115,8 +126,7 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
if (type == "status") { if (type == "status") {
QJsonObject obj = Daemon::instance()->getStatus(); QJsonObject obj = Daemon::instance()->getStatus();
obj.insert("type", "status"); obj.insert("type", "status");
m_socket->write(QJsonDocument(obj).toJson(QJsonDocument::Compact)); write(obj);
m_socket->write("\n");
return; return;
} }
@ -124,8 +134,7 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
QJsonObject obj; QJsonObject obj;
obj.insert("type", "logs"); obj.insert("type", "logs");
obj.insert("logs", Daemon::instance()->logs().replace("\n", "|")); obj.insert("logs", Daemon::instance()->logs().replace("\n", "|"));
m_socket->write(QJsonDocument(obj).toJson(QJsonDocument::Compact)); write(obj);
m_socket->write("\n");
return; return;
} }

View file

@ -34,8 +34,8 @@ LocalSocketController::LocalSocketController() {
m_socket = new QLocalSocket(this); m_socket = new QLocalSocket(this);
connect(m_socket, &QLocalSocket::connected, this, connect(m_socket, &QLocalSocket::connected, this,
&LocalSocketController::daemonConnected); &LocalSocketController::daemonConnected);
connect(m_socket, &QLocalSocket::disconnected, this, connect(m_socket, &QLocalSocket::disconnected, this,
&LocalSocketController::disconnected); [&] { errorOccurred(QLocalSocket::PeerClosedError); });
connect(m_socket, &QLocalSocket::errorOccurred, this, connect(m_socket, &QLocalSocket::errorOccurred, this,
&LocalSocketController::errorOccurred); &LocalSocketController::errorOccurred);
connect(m_socket, &QLocalSocket::readyRead, this, connect(m_socket, &QLocalSocket::readyRead, this,

View file

@ -22,7 +22,6 @@ class LinuxDaemon final : public Daemon {
protected: protected:
WireguardUtils* wgutils() const override { return m_wgutils; } WireguardUtils* wgutils() const override { return m_wgutils; }
bool supportDnsUtils() const override { return true; }
DnsUtils* dnsutils() override { return m_dnsutils; } DnsUtils* dnsutils() override { return m_dnsutils; }
bool supportIPUtils() const override { return true; } bool supportIPUtils() const override { return true; }
IPUtils* iputils() override { return m_iputils; } IPUtils* iputils() override { return m_iputils; }

View file

@ -21,7 +21,6 @@ class MacOSDaemon final : public Daemon {
protected: protected:
WireguardUtils* wgutils() const override { return m_wgutils; } WireguardUtils* wgutils() const override { return m_wgutils; }
bool supportDnsUtils() const override { return true; }
DnsUtils* dnsutils() override { return m_dnsutils; } DnsUtils* dnsutils() override { return m_dnsutils; }
bool supportIPUtils() const override { return true; } bool supportIPUtils() const override { return true; }
IPUtils* iputils() override { return m_iputils; } IPUtils* iputils() override { return m_iputils; }

View file

@ -26,7 +26,6 @@ class WindowsDaemon final : public Daemon {
protected: protected:
bool run(Op op, const InterfaceConfig& config) override; bool run(Op op, const InterfaceConfig& config) override;
WireguardUtils* wgutils() const override { return m_wgutils; } WireguardUtils* wgutils() const override { return m_wgutils; }
bool supportDnsUtils() const override { return true; }
DnsUtils* dnsutils() override { return m_dnsutils; } DnsUtils* dnsutils() override { return m_dnsutils; }
private: private:

View file

@ -502,7 +502,7 @@ QString WindowsSplitTunnel::convertPath(const QString& path) {
// device should contain : for e.g C: // device should contain : for e.g C:
return ""; return "";
} }
QByteArray buffer(2048, 0xFF); QByteArray buffer(2048, 0xFFu);
auto ok = QueryDosDeviceW(qUtf16Printable(driveLetter), auto ok = QueryDosDeviceW(qUtf16Printable(driveLetter),
(wchar_t*)buffer.data(), buffer.size() / 2); (wchar_t*)buffer.data(), buffer.size() / 2);

View file

@ -21,7 +21,7 @@
#include "platforms/windows/windowsutils.h" #include "platforms/windows/windowsutils.h"
constexpr const char* VPN_NAME = "AmneziaVPN"; constexpr const char* VPN_NAME = "AmneziaVPN";
constexpr const char* WIREGUARD_DIR = "WireGuard"; constexpr const char* WIREGUARD_DIR = "AmneziaWG";
constexpr const char* DATA_DIR = "Data"; constexpr const char* DATA_DIR = "Data";
namespace { namespace {

View file

@ -51,6 +51,9 @@ void ConnectionController::openConnection()
if (configVersion == ApiConfigSources::Telegram if (configVersion == ApiConfigSources::Telegram
&& !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) { && !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
emit updateApiConfigFromTelegram(); emit updateApiConfigFromTelegram();
} else if (configVersion == ApiConfigSources::AmneziaGateway
&& !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
emit updateApiConfigFromGateway();
} else if (configVersion && m_serversModel->isApiKeyExpired(serverIndex)) { } else if (configVersion && m_serversModel->isApiKeyExpired(serverIndex)) {
qDebug() << "attempt to update api config by end_date event"; qDebug() << "attempt to update api config by end_date event";
if (configVersion == ApiConfigSources::Telegram) { if (configVersion == ApiConfigSources::Telegram) {

View file

@ -39,11 +39,12 @@ namespace
const QString amneziaConfigPatternUserName = "userName"; const QString amneziaConfigPatternUserName = "userName";
const QString amneziaConfigPatternPassword = "password"; const QString amneziaConfigPatternPassword = "password";
const QString amneziaFreeConfigPattern = "api_key"; const QString amneziaFreeConfigPattern = "api_key";
const QString amneziaPremiumConfigPattern = "auth_data";
const QString backupPattern = "Servers/serversList"; const QString backupPattern = "Servers/serversList";
if (config.contains(backupPattern)) { if (config.contains(backupPattern)) {
return ConfigTypes::Backup; 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(amneziaConfigPatternHostName) && config.contains(amneziaConfigPatternUserName)
&& config.contains(amneziaConfigPatternPassword))) { && config.contains(amneziaConfigPatternPassword))) {
return ConfigTypes::Amnezia; return ConfigTypes::Amnezia;
@ -84,7 +85,7 @@ bool ImportController::extractConfigFromFile(const QString &fileName)
return extractConfigFromData(data); return extractConfigFromData(data);
} }
emit importErrorOccurred(tr("Unable to open file"), false); emit importErrorOccurred(ErrorCode::ImportOpenConfigError, false);
return false; return false;
} }
@ -188,12 +189,12 @@ bool ImportController::extractConfigFromData(QString data)
if (!m_serversModel->getServersCount()) { if (!m_serversModel->getServersCount()) {
emit restoreAppConfig(config.toUtf8()); emit restoreAppConfig(config.toUtf8());
} else { } else {
emit importErrorOccurred(tr("Invalid configuration file"), false); emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false);
} }
break; break;
} }
case ConfigTypes::Invalid: { case ConfigTypes::Invalid: {
emit importErrorOccurred(tr("Invalid configuration file"), false); emit importErrorOccurred(ErrorCode::ImportInvalidConfigError, false);
break; break;
} }
} }

View file

@ -54,7 +54,6 @@ public slots:
signals: signals:
void importFinished(); void importFinished();
void importErrorOccurred(const QString &errorMessage, bool goToPageHome);
void importErrorOccurred(ErrorCode errorCode, bool goToPageHome); void importErrorOccurred(ErrorCode errorCode, bool goToPageHome);
void qrDecodingFinished(); void qrDecodingFinished();

View file

@ -32,6 +32,7 @@ namespace
constexpr char availableCountries[] = "available_countries"; constexpr char availableCountries[] = "available_countries";
constexpr char apiConfig[] = "api_config"; constexpr char apiConfig[] = "api_config";
constexpr char authData[] = "auth_data";
} }
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
@ -135,10 +136,10 @@ void InstallController::install(DockerContainer container, int port, TransportPr
containerConfig[config_key::transportPacketMagicHeader] = transportPacketMagicHeader; containerConfig[config_key::transportPacketMagicHeader] = transportPacketMagicHeader;
} else if (container == DockerContainer::Sftp) { } else if (container == DockerContainer::Sftp) {
containerConfig.insert(config_key::userName, protocols::sftp::defaultUserName); 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) { } else if (container == DockerContainer::Socks5Proxy) {
containerConfig.insert(config_key::userName, protocols::socks5Proxy::defaultUserName); 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)); config.insert(config_key::container, ContainerProps::containerToString(container));
@ -768,7 +769,7 @@ bool InstallController::checkSshConnection(QSharedPointer<ServerController> serv
} else { } else {
if (output.contains(tr("Please login as the user"))) { if (output.contains(tr("Please login as the user"))) {
output.replace("\n", ""); output.replace("\n", "");
emit installationErrorOccurred(output); emit wrongInstallationUser(output);
return false; return false;
} }
} }
@ -826,7 +827,7 @@ bool InstallController::installServiceFromApi()
ErrorCode errorCode = apiController.getConfigForService(m_settings->getInstallationUuid(true), m_apiServicesModel->getCountryCode(), ErrorCode errorCode = apiController.getConfigForService(m_settings->getInstallationUuid(true), m_apiServicesModel->getCountryCode(),
m_apiServicesModel->getSelectedServiceType(), m_apiServicesModel->getSelectedServiceType(),
m_apiServicesModel->getSelectedServiceProtocol(), "", serverConfig); m_apiServicesModel->getSelectedServiceProtocol(), "", QJsonObject(), serverConfig);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(errorCode); emit installationErrorOccurred(errorCode);
return false; return false;
@ -853,19 +854,19 @@ bool InstallController::updateServiceFromApi(const int serverIndex, const QStrin
auto serverConfig = m_serversModel->getServerConfig(serverIndex); auto serverConfig = m_serversModel->getServerConfig(serverIndex);
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject(); auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
auto authData = serverConfig.value(configKey::authData).toObject();
QJsonObject newServerConfig; QJsonObject newServerConfig;
ErrorCode errorCode = ErrorCode errorCode = apiController.getConfigForService(
apiController.getConfigForService(m_settings->getInstallationUuid(true), apiConfig.value(configKey::userCountryCode).toString(), m_settings->getInstallationUuid(true), apiConfig.value(configKey::userCountryCode).toString(),
apiConfig.value(configKey::serviceType).toString(), apiConfig.value(configKey::serviceType).toString(), apiConfig.value(configKey::serviceProtocol).toString(), newCountryCode,
apiConfig.value(configKey::serviceProtocol).toString(), newCountryCode, newServerConfig); authData, newServerConfig);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(errorCode); emit installationErrorOccurred(errorCode);
return false; return false;
} }
QJsonObject newApiConfig = newServerConfig.value(configKey::apiConfig).toObject(); 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::userCountryCode, apiConfig.value(configKey::userCountryCode));
newApiConfig.insert(configKey::serviceType, apiConfig.value(configKey::serviceType)); newApiConfig.insert(configKey::serviceType, apiConfig.value(configKey::serviceType));
newApiConfig.insert(configKey::serviceProtocol, apiConfig.value(configKey::serviceProtocol)); newApiConfig.insert(configKey::serviceProtocol, apiConfig.value(configKey::serviceProtocol));

View file

@ -75,8 +75,8 @@ signals:
void removeAllContainersFinished(const QString &finishedMessage); void removeAllContainersFinished(const QString &finishedMessage);
void removeProcessedContainerFinished(const QString &finishedMessage); void removeProcessedContainerFinished(const QString &finishedMessage);
void installationErrorOccurred(const QString &errorMessage);
void installationErrorOccurred(ErrorCode errorCode); void installationErrorOccurred(ErrorCode errorCode);
void wrongInstallationUser(const QString &message);
void serverAlreadyExists(int serverIndex); void serverAlreadyExists(int serverIndex);

View file

@ -77,6 +77,7 @@ ErrorCode ClientManagementModel::updateModel(const DockerContainer container, co
{ {
beginResetModel(); beginResetModel();
m_clientsTable = QJsonArray(); m_clientsTable = QJsonArray();
endResetModel();
ErrorCode error = ErrorCode::NoError; ErrorCode error = ErrorCode::NoError;
@ -90,10 +91,10 @@ ErrorCode ClientManagementModel::updateModel(const DockerContainer container, co
const QByteArray clientsTableString = serverController->getTextFileFromContainer(container, credentials, clientsTableFile, error); const QByteArray clientsTableString = serverController->getTextFileFromContainer(container, credentials, clientsTableFile, error);
if (error != ErrorCode::NoError) { if (error != ErrorCode::NoError) {
logger.error() << "Failed to get the clientsTable file from the server"; logger.error() << "Failed to get the clientsTable file from the server";
endResetModel();
return error; return error;
} }
beginResetModel();
m_clientsTable = QJsonDocument::fromJson(clientsTableString).array(); m_clientsTable = QJsonDocument::fromJson(clientsTableString).array();
if (m_clientsTable.isEmpty()) { if (m_clientsTable.isEmpty()) {
@ -601,5 +602,6 @@ QHash<int, QByteArray> ClientManagementModel::roleNames() const
roles[LatestHandshakeRole] = "latestHandshake"; roles[LatestHandshakeRole] = "latestHandshake";
roles[DataReceivedRole] = "dataReceived"; roles[DataReceivedRole] = "dataReceived";
roles[DataSentRole] = "dataSent"; roles[DataSentRole] = "dataSent";
roles[AllowedIpsRole] = "allowedIps";
return roles; return roles;
} }

View file

@ -37,7 +37,7 @@ PageType {
Connections { Connections {
target: ImportController target: ImportController
function onImportErrorOccurred(errorMessage, goToPageHome) { function onImportErrorOccurred(error, goToPageHome) {
if (goToPageHome) { if (goToPageHome) {
PageController.goToStartPage() PageController.goToStartPage()
} else { } else {

View file

@ -123,6 +123,10 @@ PageType {
} }
} }
function onWrongInstallationUser(message) {
onInstallationErrorOccurred(message)
}
function onUpdateContainerFinished(message) { function onUpdateContainerFinished(message) {
PageController.showNotificationMessage(message) PageController.showNotificationMessage(message)
PageController.closePage() PageController.closePage()

View file

@ -14,14 +14,13 @@
QString Utils::getRandomString(int len) QString Utils::getRandomString(int len)
{ {
const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); const QString possibleCharacters = QStringLiteral("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
QString randomString; QString randomString;
for (int i = 0; i < len; ++i) { for (int i = 0; i < len; ++i) {
quint32 index = QRandomGenerator::global()->generate() % possibleCharacters.length(); randomString.append(possibleCharacters.at(QRandomGenerator::system()->bounded(possibleCharacters.length())));
QChar nextChar = possibleCharacters.at(index);
randomString.append(nextChar);
} }
return randomString; return randomString;
} }
@ -244,3 +243,22 @@ bool Utils::signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent)
} }
#endif #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 (...) {}
}

View file

@ -34,6 +34,9 @@ public:
static QString certUtilPath(); static QString certUtilPath();
static QString tun2socksPath(); 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 #ifdef Q_OS_WIN
static bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent); static bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent);
#endif #endif