diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 3ea9a177..7eeacb22 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' @@ -365,10 +377,13 @@ jobs: env: ANDROID_BUILD_PLATFORM: android-34 - QT_VERSION: 6.7.2 + QT_VERSION: 6.7.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 c3f1de5a..b6bed4c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT AmneziaVPN) -project(${PROJECT} VERSION 4.8.1.9 +project(${PROJECT} VERSION 4.8.2.3 DESCRIPTION "AmneziaVPN" HOMEPAGE_URL "https://amnezia.org/" ) @@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d") set(RELEASE_DATE "${CURRENT_DATE}") set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) -set(APP_ANDROID_VERSION_CODE 65) +set(APP_ANDROID_VERSION_CODE 2069) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(MZ_PLATFORM_NAME "linux") diff --git a/README.md b/README.md index e4a6bf0c..eed800f5 100644 --- a/README.md +++ b/README.md @@ -10,21 +10,17 @@ Amnezia is an open-source VPN client, with a key feature that enables you to dep
- - - - - -
- + +[Alternative download link (mirror)](https://storage.googleapis.com/kldscp/amnezia.org/downloads) [All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
+ ## Features @@ -37,7 +33,7 @@ Amnezia is an open-source VPN client, with a key feature that enables you to dep ## Links -- [https://amnezia.org](https://amnezia.org) - project website +- [https://amnezia.org](https://amnezia.org) - project website | [Alternative link (mirror)](https://storage.googleapis.com/kldscp/amnezia.org) - [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit - [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Telegram support channel (English) - [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Telegram support channel (Farsi) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index f5be0638..ca74e468 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -25,10 +25,11 @@ execute_process( 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 OR MACOS_NE) 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 b5c382be..6e682aa4 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 d5026425..b2c2ff71 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt @@ -21,6 +21,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 @@ -158,7 +159,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) @@ -200,7 +201,7 @@ class AmneziaActivity : QtActivity() { NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED ) ) { - Log.d( + Log.v( TAG, "Notification state changed: ${it?.action}, blocked = " + "${it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)}" ) @@ -214,7 +215,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) } @@ -403,7 +404,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 @@ -516,7 +517,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) } @@ -565,7 +566,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) @@ -720,6 +721,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 937127ee..8d108bc3 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt @@ -300,7 +300,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() @@ -317,7 +317,7 @@ open class AmneziaVpnService : VpnService() { ) ) { val state = it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false) - Log.d(TAG, "Notification state changed: ${it?.action}, blocked = $state") + Log.v(TAG, "Notification state changed: ${it?.action}, blocked = $state") if (state == false) { enableNotification() } else { @@ -450,7 +450,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 f4707731..47e8f263 100644 --- a/client/android/src/org/amnezia/vpn/ServiceNotification.kt +++ b/client/android/src/org/amnezia/vpn/ServiceNotification.kt @@ -62,7 +62,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) @@ -88,17 +88,15 @@ class ServiceNotification(private val context: Context) { fun isNotificationEnabled(): Boolean { if (!context.isNotificationPermissionGranted()) return false if (!notificationManager.areNotificationsEnabled()) return false - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - return notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) - ?.let { it.importance != NotificationManager.IMPORTANCE_NONE } ?: true - } - return true + return notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID)?.let { + it.importance != NotificationManager.IMPORTANCE_NONE + } ?: true } @SuppressLint("MissingPermission") fun updateNotification(serverName: String?, protocol: String?, state: ProtocolState) { if (context.isNotificationPermissionGranted()) { - 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/Log.kt b/client/android/utils/src/main/kotlin/Log.kt index a656b9ea..da11c200 100644 --- a/client/android/utils/src/main/kotlin/Log.kt +++ b/client/android/utils/src/main/kotlin/Log.kt @@ -1,8 +1,6 @@ package org.amnezia.vpn.util import android.content.Context -import android.icu.text.DateFormat -import android.icu.text.SimpleDateFormat import android.os.Build import android.os.Process import java.io.File @@ -12,8 +10,6 @@ import java.nio.channels.FileChannel import java.nio.channels.FileLock import java.time.LocalDateTime import java.time.format.DateTimeFormatter -import java.util.Date -import java.util.Locale import java.util.concurrent.locks.ReentrantLock import org.amnezia.vpn.util.Log.Priority.D import org.amnezia.vpn.util.Log.Priority.E @@ -41,11 +37,7 @@ private const val LOG_MAX_FILE_SIZE = 1024 * 1024 * | | | create a report and/or terminate the process | */ object Log { - private val dateTimeFormat: Any = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) DateTimeFormatter.ofPattern(DATE_TIME_PATTERN) - else object : ThreadLocal() { - override fun initialValue(): DateFormat = SimpleDateFormat(DATE_TIME_PATTERN, Locale.US) - } + private val dateTimeFormat: DateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN) private lateinit var logDir: File private val logFile: File by lazy { File(logDir, LOG_FILE_NAME) } @@ -143,12 +135,7 @@ object Log { } private fun formatLogMsg(tag: String, msg: String, priority: Priority): String { - val date = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - LocalDateTime.now().format(dateTimeFormat as DateTimeFormatter) - } else { - @Suppress("UNCHECKED_CAST") - (dateTimeFormat as ThreadLocal).get()?.format(Date()) - } + val date = LocalDateTime.now().format(dateTimeFormat) return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " + "$tag: $msg\n" } diff --git a/client/android/utils/src/main/kotlin/net/NetworkState.kt b/client/android/utils/src/main/kotlin/net/NetworkState.kt index b71bf393..1cab5535 100644 --- a/client/android/utils/src/main/kotlin/net/NetworkState.kt +++ b/client/android/utils/src/main/kotlin/net/NetworkState.kt @@ -42,18 +42,12 @@ 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") - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - checkNetworkState(network, networkCapabilities) - } else { - handler.post { - checkNetworkState(network, networkCapabilities) - } - } + Log.v(TAG, "onCapabilitiesChanged: $network, $networkCapabilities") + checkNetworkState(network, networkCapabilities) } private fun checkNetworkState(network: Network, networkCapabilities: NetworkCapabilities) { @@ -73,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") } } } @@ -87,7 +81,7 @@ class NetworkState( Log.d(TAG, "Bind network listener") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { connectivityManager.registerBestMatchingNetworkCallback(networkRequest, networkCallback, handler) - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + } else { val numberAttempts = 300 var attemptCount = 0 while(true) { @@ -108,8 +102,6 @@ class NetworkState( } } } - } else { - connectivityManager.requestNetwork(networkRequest, networkCallback) } isListenerBound = true } 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..75a3f93c 100644 --- a/client/core/controllers/apiController.cpp +++ b/client/core/controllers/apiController.cpp @@ -1,5 +1,8 @@ #include "apiController.h" +#include +#include + #include #include #include @@ -11,6 +14,7 @@ #include "amnezia_application.h" #include "configurators/wireguard_configurator.h" #include "core/enums/apiEnums.h" +#include "utilities.h" #include "version.h" namespace @@ -33,6 +37,7 @@ namespace constexpr char userCountryCode[] = "user_country_code"; constexpr char serverCountryCode[] = "server_country_code"; constexpr char serviceType[] = "service_type"; + constexpr char serviceInfo[] = "service_info"; constexpr char aesKey[] = "aes_key"; constexpr char aesIv[] = "aes_iv"; @@ -40,9 +45,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) { @@ -63,6 +69,28 @@ namespace return ErrorCode::ApiConfigDownloadError; } } + + bool shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key = "", + const QByteArray &iv = "", const QByteArray &salt = "") + { + if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError + || reply->error() == QNetworkReply::NetworkError::TimeoutError) { + qDebug() << "Timeout occurred"; + return true; + } else if (responseBody.contains("html")) { + qDebug() << "The response contains an html tag"; + return true; + } else if (checkEncryption) { + try { + QSimpleCrypto::QBlockCipher blockCipher; + static_cast(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt)); + } catch (...) { + qDebug() << "Failed to decrypt the data"; + return true; + } + } + return false; + } } ApiController::ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent) @@ -94,8 +122,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 +142,35 @@ 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); + + if (newServerConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) { + apiConfig.insert(configKey::serviceInfo, QJsonDocument::fromJson(apiResponseBody).object().value(configKey::serviceInfo).toObject()); + } + + serverConfig[configKey::apiConfig] = apiConfig; + return; } @@ -146,6 +184,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 +213,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 {}; } @@ -292,38 +351,44 @@ ErrorCode ApiController::getServicesList(QByteArray &responseBody) connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; }); wait.exec(); - if (reply->error() == QNetworkReply::NetworkError::TimeoutError || reply->error() == QNetworkReply::NetworkError::OperationCanceledError) { + responseBody = reply->readAll(); + + if (sslErrors.isEmpty() && shouldBypassProxy(reply, responseBody, false)) { m_proxyUrls = getProxyUrls(); + std::random_device randomDevice; + std::mt19937 generator(randomDevice()); + std::shuffle(m_proxyUrls.begin(), m_proxyUrls.end(), generator); for (const QString &proxyUrl : m_proxyUrls) { + qDebug() << "Go to the next endpoint"; request.setUrl(QString("%1v1/services").arg(proxyUrl)); + reply->deleteLater(); // delete the previous reply reply = amnApp->manager()->get(request); QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit); connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; }); wait.exec(); - if (reply->error() != QNetworkReply::NetworkError::TimeoutError - && reply->error() != QNetworkReply::NetworkError::OperationCanceledError) { + + responseBody = reply->readAll(); + if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, responseBody, false)) { break; } - reply->deleteLater(); } } - responseBody = reply->readAll(); auto errorCode = checkErrors(sslErrors, reply); reply->deleteLater(); return errorCode; } 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(); QThread::msleep(10); #endif - QNetworkAccessManager manager; QNetworkRequest request; request.setTransferTimeout(7000); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); @@ -339,6 +404,9 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co } apiPayload[configKey::serviceType] = serviceType; apiPayload[configKey::uuid] = installationUuid; + if (!authData.isEmpty()) { + apiPayload[configKey::authData] = authData; + } QSimpleCrypto::QBlockCipher blockCipher; QByteArray key = blockCipher.generatePrivateSalt(32); @@ -361,6 +429,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,14 +439,16 @@ 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; requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64()); requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64()); - QNetworkReply *reply = manager.post(request, QJsonDocument(requestBody).toJson()); + QNetworkReply *reply = amnApp->manager()->post(request, QJsonDocument(requestBody).toJson()); QEventLoop wait; connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit); @@ -386,37 +457,43 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; }); wait.exec(); - if (reply->error() == QNetworkReply::NetworkError::TimeoutError || reply->error() == QNetworkReply::NetworkError::OperationCanceledError) { - if (m_proxyUrls.isEmpty()) { - m_proxyUrls = getProxyUrls(); - } + auto encryptedResponseBody = reply->readAll(); + + if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) { + m_proxyUrls = getProxyUrls(); + std::random_device randomDevice; + std::mt19937 generator(randomDevice()); + std::shuffle(m_proxyUrls.begin(), m_proxyUrls.end(), generator); for (const QString &proxyUrl : m_proxyUrls) { + qDebug() << "Go to the next endpoint"; request.setUrl(QString("%1v1/config").arg(proxyUrl)); - reply = manager.post(request, QJsonDocument(requestBody).toJson()); + reply->deleteLater(); // delete the previous reply + reply = amnApp->manager()->post(request, QJsonDocument(requestBody).toJson()); QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit); connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList &errors) { sslErrors = errors; }); wait.exec(); - if (reply->error() != QNetworkReply::NetworkError::TimeoutError - && reply->error() != QNetworkReply::NetworkError::OperationCanceledError) { + + encryptedResponseBody = reply->readAll(); + if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) { break; } - reply->deleteLater(); } } auto errorCode = checkErrors(sslErrors, reply); + reply->deleteLater(); if (errorCode) { return errorCode; } - auto encryptedResponseBody = reply->readAll(); - reply->deleteLater(); try { 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/daemon/wireguardutilswindows.cpp b/client/platforms/windows/daemon/wireguardutilswindows.cpp index a68551d7..1a220235 100644 --- a/client/platforms/windows/daemon/wireguardutilswindows.cpp +++ b/client/platforms/windows/daemon/wireguardutilswindows.cpp @@ -248,7 +248,7 @@ bool WireguardUtilsWindows::updateRoutePrefix(const IPAddress& prefix) { } if (result != NO_ERROR) { logger.error() << "Failed to create route to" - << logger.sensitive(prefix.toString()) + << prefix.toString() << "result:" << result; } return result == NO_ERROR; @@ -265,7 +265,7 @@ bool WireguardUtilsWindows::deleteRoutePrefix(const IPAddress& prefix) { } if (result != NO_ERROR) { logger.error() << "Failed to delete route to" - << logger.sensitive(prefix.toString()) + << prefix.toString() << "result:" << result; } return result == NO_ERROR; 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/translations/amneziavpn_ar_EG.ts b/client/translations/amneziavpn_ar_EG.ts index 0aa5e32d..29135f65 100644 --- a/client/translations/amneziavpn_ar_EG.ts +++ b/client/translations/amneziavpn_ar_EG.ts @@ -89,60 +89,60 @@ ConnectionController - - - + + + Connect اتصل - + VPN Protocols is not installed. Please install VPN container at first لم يتم تثبيت بروتوكولات VPN, من فضلك قم بتنزيل حاوية VPN اولاً - + Connecting... اتصال... - + Connected تم الاتصال - + Reconnecting... إعادة الاتصال... - + Disconnecting... إنهاء الاتصال... - + Preparing... جاري التحضير... - + Settings updated successfully, reconnnection... تم تحديث الاعدادات بنجاح, جاري إعادة الاتصال... - + Settings updated successfully تم تحديث الاعدادات بنجاح - + The selected protocol is not supported on the current platform البروتوكول المحدد غير مدعوم علي المنصة الحالية - + unable to create configuration غير قادر علي إنشاء تكوين @@ -253,23 +253,20 @@ Can't be disabled for current server ImportController - Unable to open file - غير قادر علي فتح الملف + غير قادر علي فتح الملف - - Invalid configuration file - ملف تكوين غير صحيح + ملف تكوين غير صحيح - + Scanned %1 of %2. تم فحص%1 من %2. - + In the imported configuration, potentially dangerous lines were found: في التكوين المستورد، تم العثور على سطور يحتمل أن تكون خطرة: @@ -277,24 +274,24 @@ Can't be disabled for current server InstallController - + %1 installed successfully. %1 تم التثبيت بنجاح. - + %1 is already installed on the server. %1 بالفعل مٌثبت علي الخادم. - + Added containers that were already installed on the server تمت إضافة الحاويات التي كانت مٌثبتة بالفعل علي الخادم - + Already installed containers were found on the server. All installed containers have been added to the application @@ -302,62 +299,62 @@ Already installed containers were found on the server. All installed containers تمت إضافة جميع الحاويات المٌثبتة إلي التطبيق - + Settings updated successfully تم تحديث الاعدادات بنجاح - + Server '%1' was rebooted تمت إعادة تشغيل الخادم%1 - + Server '%1' was removed تمت إزالة الخادم '%1' - + All containers from server '%1' have been removed قد تم حذفها '%1' جميع الحاويات من الخادم - + %1 has been removed from the server '%2' %1 تم حدف '%2' اسم الخادم - + Api config removed تم حذف تكوين Api - + %1 cached profile cleared تم مسح ملف تعريف %1 المخزن مؤقتًا - + Please login as the user من فضلك قم بتسجيل الدخول كمستخدم - + Server added successfully تمت إضافة الخادم بنجاح - + %1 installed successfully. تم تحميل %1 بنجاح - + API config reloaded تمت إعادة تحميل تكوين API - + Successfully changed the country of connection to %1 تم تغيير بلد الاتصال بنجاح إلى %1 @@ -2318,97 +2315,107 @@ Already installed containers were found on the server. All installed containers PageSetupWizardConfigSource - + Connection الاتصال - + Settings إعدادات - + Enable logs + Support tag + علامة الدعم + + + + Copied + + + + Insert the key, add a configuration file or scan the QR-code أدخل المفتاح، أضف ملف تكوين أو امسح رمز الاستجابة السريعة - + Insert key أدخل مفتاح - + Insert أدخل - + Continue واصل - + Other connection options اختيارات اتصال اخري - + VPN by Amnezia VPN بواسطة Amnezia - + Connect to classic paid and free VPN services from Amnezia اتصل بخدمات VPN الكلاسيكية المدفوعة والمجانية من Amnezia - + Self-hosted VPN VPN ذاتية الاستضافة - + Configure Amnezia VPN on your own server قم بتكوين Amnezia VPN على الخادم الخاص بك - + Restore from backup استرجاع من ملف يحتوي علي نسخة احتياطية - + Open backup file افتح ملف نسخ احتياطي - + Backup files (*.backup) ملفات نٌسخ احتياطية (*.backup) - + File with connection settings ملف إعدادات اتصال - + Open config file افتح ملف تكوين - + QR code رمز QR - + I have nothing ليس لدي اي شئ @@ -2771,67 +2778,67 @@ Already installed containers were found on the server. All installed containers ابحث - + Creation date: %1 تاريخ الإنشاء: %1 - + Latest handshake: %1 اخر تصافح: %1 - + Data received: %1 البيانات المستلمة: %1 - + Data sent: %1 البيانات المٌرسلة: %1 - + Allowed IPs: %1 - + Rename إعادة التسمية - + Client name اسم العميل - + Save احفظ - + Revoke سحب وإبطال - + Revoke the config for a user - %1? سحب وإبطال للمستخدم - %1? - + The user will no longer be able to connect to your server. المستخدم لن يكون قادر علي الاتصال بعد الان. - + Continue واصل - + Cancel إلغاء @@ -2944,17 +2951,17 @@ Already installed containers were found on the server. All installed containers PageStart - + Logging was disabled after 14 days, log files were deleted تم تعطيل التسجيل بعد 14 يومًا، وتم حذف ملفات السجل - + Settings restored from backup file تم تحميل الإعدادات من ملف نسخة احتياطية - + Logging is enabled. Note that logs will be automaticallydisabled after 14 days, and all log files will be deleted. @@ -3293,22 +3300,22 @@ Already installed containers were found on the server. All installed containers انتهت مدة الاتصال بالخادم - + VPN connection error - + Error when retrieving configuration from API خطأ عند استرداد التكوين من API - + This config has already been added to the application هذا التكوين بالفعل تمت إضافتة للبرنامج - + ErrorCode: %1. @@ -3383,57 +3390,67 @@ Already installed containers were found on the server. All installed containers التكوين لا يحتوي علي اي حاويات و اعتماد للأتصال بالخادم - + + Unable to open config file + + + + In the response from the server, an empty config was received في الاستجابة من الخادم، تم تلقي تكوين فارغ - + SSL error occurred حدث خطأ SSL - + Server response timeout on api request انتهت مهلة استجابة الخادم عند طلب واجهة برمجة التطبيقات - + Missing AGW public key مفتاح AGW عام مفقود - + + Failed to decrypt response payload + + + + QFile error: The file could not be opened خطأ QFile: لا يمكن فتح الملف - + QFile error: An error occurred when reading from the file خطأ QFile: ظهر خطأ اثناء القراءه من الملف - + QFile error: The file could not be accessed خطأ QFile: لا يمكن الوصول للملف - + QFile error: An unspecified error occurred خطأ QFile: ظهر خطأ غير محدد - + QFile error: A fatal error occurred خطأ QFile: حدث خطأ فادح - + QFile error: The operation was aborted خطأ QFile: تم إحباط العملية - + Internal error خطأ داخلي @@ -4031,7 +4048,7 @@ While it offers a blend of security, stability, and speed, it's essential t VpnConnection - + Mbps diff --git a/client/translations/amneziavpn_fa_IR.ts b/client/translations/amneziavpn_fa_IR.ts index f3ed01be..a01eaba9 100644 --- a/client/translations/amneziavpn_fa_IR.ts +++ b/client/translations/amneziavpn_fa_IR.ts @@ -88,63 +88,63 @@ ConnectionController - + VPN Protocols is not installed. Please install VPN container at first پروتکل وی‎پی‎ان نصب نشده است لطفا کانتینر وی‎پی‎ان را نصب کنید - + Connecting... در حال ارتباط... - + Connected متصل - + Preparing... در حال آماده‌سازی... - + Settings updated successfully, reconnnection... تنظیمات به روز رسانی شد در حال اتصال دوباره... - + Settings updated successfully تنظیمات با موفقیت به‎روز‎رسانی شدند - + The selected protocol is not supported on the current platform پروتکل انتخاب‌شده در پلتفرم فعلی پشتیبانی نمی‌شود. - + unable to create configuration نمی‌توان پیکربندی را ایجاد کرد. - + Reconnecting... اتصال دوباره... - - - + + + Connect اتصال - + Disconnecting... قطع ارتباط... @@ -258,23 +258,20 @@ Can't be disabled for current server ImportController - Unable to open file - نمی‌توان فایل را باز کرد. + نمی‌توان فایل را باز کرد. - - Invalid configuration file - فایل پیکربندی نامعتبر است. + فایل پیکربندی نامعتبر است. - + Scanned %1 of %2. ارزیابی %1 از %2. - + In the imported configuration, potentially dangerous lines were found: در پیکربندی وارد شده، خطوطی که ممکن است خطرناک باشند، یافت شدند: @@ -282,86 +279,86 @@ Can't be disabled for current server InstallController - + %1 installed successfully. %1 با موفقیت نصب شد. - + %1 is already installed on the server. %1 در حال حاضر بر روی سرور نصب شده است. - + Added containers that were already installed on the server کانتینرهایی که بر روی سرور موجود بودند اضافه شدند - + Already installed containers were found on the server. All installed containers have been added to the application کانتینرهای نصب شده بر روی سرور شناسایی شدند. تمام کانتینترهای نصب شده به نرم افزار اضافه شدند - + Settings updated successfully تنظیمات با موفقیت به‎روز‎رسانی شدند - + Server '%1' was rebooted سرور %1 راه اندازی مجدد شد - + Server '%1' was removed سرور %1 حذف شد - + All containers from server '%1' have been removed تمام کانتینترها از سرور %1 حذف شدند - + %1 has been removed from the server '%2' %1 از سرور %2 حذف شد - + Api config removed پیکربندی API حذف شد. - + %1 cached profile cleared %1 پروفایل ذخیره شده پاک شد. - + Please login as the user لطفا به عنوان کاربر وارد شوید - + Server added successfully سرور با موفقیت اضافه شد - + %1 installed successfully. %1 با موفقیت نصب شد. - + API config reloaded پیکربندی API دوباره بارگذاری شد. - + Successfully changed the country of connection to %1 کشور اتصال با موفقیت به %1 تغییر یافت. @@ -2433,7 +2430,7 @@ It's okay as long as it's from someone you trust. چی داری؟ - + File with connection settings فایل شامل تنظیمات اتصال @@ -2442,92 +2439,102 @@ It's okay as long as it's from someone you trust. فایل شامل تنظیمات اتصال یا بک‎آپ - + Connection ارتباط - + Settings تنظیمات - + Enable logs + Support tag + + + + + Copied + کپی شد + + + Insert the key, add a configuration file or scan the QR-code کلید را وارد کنید، فایل پیکربندی را اضافه کنید یا کد QR را اسکن کنید - + Insert key کلید را وارد کنید - + Insert وارد کردن - + Continue ادامه دهید - + Other connection options گزینه‌های اتصال دیگر - + VPN by Amnezia VPN توسط Amnezia - + Connect to classic paid and free VPN services from Amnezia اتصال به سرویس‌های VPN کلاسیک پولی و رایگان از Amnezia - + Self-hosted VPN Self-hosted VPN - + Configure Amnezia VPN on your own server پیکربندی VPN Amnezia بر روی سرور خودتان - + Restore from backup بازیابی از پشتیبان - + Open backup file باز کردن فایل پشتیبان - + Backup files (*.backup) Backup files (*.backup) - + Open config file باز کردن فایل تنظیمات - + QR code QR-Code - + I have nothing من هیچی ندارم @@ -2939,27 +2946,27 @@ It's okay as long as it's from someone you trust. جستجو - + Creation date: %1 تاریخ ایجاد: %1 - + Latest handshake: %1 آخرین ارتباط: %1 - + Data received: %1 داده‌های دریافت شده: %1 - + Data sent: %1 داده‌های ارسال شده: %1 - + Allowed IPs: %1 @@ -2968,42 +2975,42 @@ It's okay as long as it's from someone you trust. تاریخ ایجاد: - + Rename تغییر نام - + Client name نام کلاینت - + Save ذخیره - + Revoke ابطال - + Revoke the config for a user - %1? لغو پیکربندی برای یک کاربر - %1? - + The user will no longer be able to connect to your server. کاربر دیگر نمی‎تواند به سرور وصل شود. - + Continue ادامه - + Cancel کنسل @@ -3090,17 +3097,17 @@ It's okay as long as it's from someone you trust. PageStart - + Logging was disabled after 14 days, log files were deleted ثبت وقایع پس از ۱۴ روز غیرفعال شد و فایل‌های ثبت وقایع حذف شدند - + Settings restored from backup file تنظیمات از فایل پشتیبان بازیابی شد - + Logging is enabled. Note that logs will be automaticallydisabled after 14 days, and all log files will be deleted. @@ -3484,22 +3491,22 @@ It's okay as long as it's from someone you trust. تنظیمات شامل هیچ کانتینر یا اعتبارنامه‎ای برای اتصال به سرور نیست - + VPN connection error خطای اتصال VPN - + Error when retrieving configuration from API خطا هنگام بازیابی پیکربندی از API - + This config has already been added to the application این پیکربندی قبلاً به برنامه اضافه شده است - + ErrorCode: %1. کد خطا: %1. @@ -3569,57 +3576,67 @@ It's okay as long as it's from someone you trust. VPN pool error: no available addresses - + + Unable to open config file + + + + In the response from the server, an empty config was received در پاسخ از سرور، پیکربندی خالی دریافت شد - + SSL error occurred SSL error occurred - + Server response timeout on api request Server response timeout on api request - + Missing AGW public key - - QFile error: The file could not be opened - - - - - QFile error: An error occurred when reading from the file + + Failed to decrypt response payload - QFile error: The file could not be accessed + QFile error: The file could not be opened - QFile error: An unspecified error occurred + QFile error: An error occurred when reading from the file - QFile error: A fatal error occurred + QFile error: The file could not be accessed + QFile error: An unspecified error occurred + + + + + QFile error: A fatal error occurred + + + + QFile error: The operation was aborted - + Internal error Internal error @@ -4239,7 +4256,7 @@ For more detailed information, you can VpnConnection - + Mbps Mbps diff --git a/client/translations/amneziavpn_hi_IN.ts b/client/translations/amneziavpn_hi_IN.ts index 036c0d7e..7281ebd5 100644 --- a/client/translations/amneziavpn_hi_IN.ts +++ b/client/translations/amneziavpn_hi_IN.ts @@ -89,61 +89,61 @@ ConnectionController - - - + + + Connect कनेक्ट - + VPN Protocols is not installed. Please install VPN container at first पीएन प्रोटोकॉल स्थापित नहीं है. कृपया पहले वीपीएन कंटेनर स्थापित करें - + Connected जुड़ा हुआ - + The selected protocol is not supported on the current platform चयनित प्रोटोकॉल वर्तमान प्लेटफ़ॉर्म पर समर्थित नहीं है - + unable to create configuration कॉन्फ़िगरेशन बनाने में असमर्थ - + Connecting... कनेक्ट... - + Reconnecting... पुनः कनेक्ट हो रहा है... - + Disconnecting... डिस्कनेक्ट हो रहा है... - + Preparing... तैयार कर रहे हैं... - + Settings updated successfully, reconnnection... सेटिंग्स सफलतापूर्वक अपडेट हो गईं... - + Settings updated successfully सेटिंग्स सफलतापूर्वक अपडेट हो गईं @@ -254,23 +254,20 @@ Can't be disabled for current server ImportController - Unable to open file - फाइल खोलने में असमर्थ + फाइल खोलने में असमर्थ - - Invalid configuration file - अमान्य कॉन्फ़िगरेशन फ़ाइल + अमान्य कॉन्फ़िगरेशन फ़ाइल - + Scanned %1 of %2. %2 में से %1 स्कैन किया गया. - + In the imported configuration, potentially dangerous lines were found: @@ -278,86 +275,86 @@ Can't be disabled for current server InstallController - + %1 installed successfully. %1 सफलतापूर्वक स्थापित हुआ. - + %1 is already installed on the server. %1 पहले से ही सर्वर पर स्थापित है. - + Added containers that were already installed on the server सर्वर पर पहले से स्थापित कंटेनर जोड़े गए - + Already installed containers were found on the server. All installed containers have been added to the application सर्वर पर पहले से स्थापित कंटेनर पाए गए। सभी स्थापित कंटेनरों को एप्लिकेशन में जोड़ दिया गया है - + Settings updated successfully सेटिंग्स सफलतापूर्वक अपडेट हो गईं - + Server '%1' was rebooted सर्वर '%1' रीबूट किया गया था - + Server '%1' was removed सर्वर '%1' रीबूट किया गया था - + All containers from server '%1' have been removed सर्वर '%1' से सभी कंटेनर हटा दिए गए हैं - + %1 has been removed from the server '%2' %1 को सर्वर '%2' से हटा दिया गया है - + Api config removed - + %1 cached profile cleared %1 कैश्ड प्रोफ़ाइल साफ़ की गई - + Please login as the user कृपया उपयोगकर्ता के रूप में लॉगिन करें - + Server added successfully सर्वर सफलतापूर्वक जोड़ा गया - + %1 installed successfully. - + API config reloaded - + Successfully changed the country of connection to %1 @@ -2382,97 +2379,107 @@ Already installed containers were found on the server. All installed containers कनेक्शन सेटिंग्स वाली फ़ाइल - + Connection कनेक्शन - + Settings समायोजन - + Enable logs - Insert the key, add a configuration file or scan the QR-code - - - - - Insert key + Support tag + Copied + कॉपी किया गया + + + + Insert the key, add a configuration file or scan the QR-code + + + + + Insert key + + + + Insert डालना - + Continue जारी रखना - + Other connection options - + VPN by Amnezia - + Connect to classic paid and free VPN services from Amnezia - + Self-hosted VPN - + Configure Amnezia VPN on your own server - + Restore from backup बैकअप से बहाल करना - + Open backup file बैकअप फ़ाइल खोलें - + Backup files (*.backup) बैकअप फ़ाइलें (*.backup) - + File with connection settings कनेक्शन सेटिंग्स वाली फ़ाइल - + Open config file कॉन्फ़िग फ़ाइल खोलें - + QR code क्यू आर संहिता - + I have nothing मेरे पास कुछ नहीं है @@ -2857,27 +2864,27 @@ Already installed containers were found on the server. All installed containers खोज - + Creation date: %1 निर्माण दिनांक: %1 - + Latest handshake: %1 नवीनतम हाथ मिलाना: %1 - + Data received: %1 प्राप्त डेटा: %1 - + Data sent: %1 डेटा भेजा गया: %1 - + Allowed IPs: %1 @@ -2886,42 +2893,42 @@ Already installed containers were found on the server. All installed containers निर्माण तिथि: - + Rename नाम बदलें - + Client name ग्राहक नाम - + Save सहेजें - + Revoke निरस्त करें - + Revoke the config for a user - %1? किसी उपयोक्ता के लिए कॉन्फ़िगरेशन निरस्त करें - %1? - + The user will no longer be able to connect to your server. उपयोगकर्ता अब आपके सर्वर से कनेक्ट नहीं हो पाएगा. - + Continue जारी रखना - + Cancel रद्द करना @@ -3039,17 +3046,17 @@ Already installed containers were found on the server. All installed containers PageStart - + Logging was disabled after 14 days, log files were deleted 14 दिनों के बाद लॉगिंग अक्षम कर दी गई, लॉग फ़ाइलें हटा दी गईं - + Settings restored from backup file बैकअप फ़ाइल से सेटिंग्स पुनर्स्थापित की गईं - + Logging is enabled. Note that logs will be automaticallydisabled after 14 days, and all log files will be deleted. @@ -3398,42 +3405,52 @@ Already installed containers were found on the server. All installed containers सर्वर से कनेक्ट होने का समय समाप्त - + + Unable to open config file + + + + VPN connection error VPN कनेक्शन त्रुटि - + Error when retrieving configuration from API एपीआई से कॉन्फ़िगरेशन पुनर्प्राप्त करते समय त्रुटि - + This config has already been added to the application यह कॉन्फ़िगरेशन पहले ही एप्लिकेशन में जोड़ा जा चुका है - + In the response from the server, an empty config was received - + SSL error occurred - + Server response timeout on api request - + Missing AGW public key - + + Failed to decrypt response payload + + + + ErrorCode: %1. ErrorCode: %1. @@ -3498,37 +3515,37 @@ Already installed containers were found on the server. All installed containers कॉन्फ़िगरेशन में सर्वर से कनेक्ट करने के लिए कोई कंटेनर और क्रेडेंशियल नहीं है - + QFile error: The file could not be opened Qफ़ाइल त्रुटि: फ़ाइल खोली नहीं जा सकी - + QFile error: An error occurred when reading from the file Qफ़ाइल त्रुटि: फ़ाइल से पढ़ते समय एक त्रुटि उत्पन्न हुई - + QFile error: The file could not be accessed Qफ़ाइल त्रुटि: फ़ाइल तक नहीं पहुंचा जा सका - + QFile error: An unspecified error occurred Qफ़ाइल त्रुटि: एक अनिर्दिष्ट त्रुटि उत्पन्न हुई - + QFile error: A fatal error occurred Qफ़ाइल त्रुटि: एक घातक त्रुटि उत्पन्न हुई - + QFile error: The operation was aborted Qफ़ाइल त्रुटि: ऑपरेशन निरस्त कर दिया गया था - + Internal error आंतरिक त्रुटि @@ -4127,7 +4144,7 @@ While it offers a blend of security, stability, and speed, it's essential t VpnConnection - + Mbps diff --git a/client/translations/amneziavpn_my_MM.ts b/client/translations/amneziavpn_my_MM.ts index 1e391587..66a81863 100644 --- a/client/translations/amneziavpn_my_MM.ts +++ b/client/translations/amneziavpn_my_MM.ts @@ -88,62 +88,62 @@ ConnectionController - + VPN Protocols is not installed. Please install VPN container at first VPN ပရိုတိုကောများကို မထည့်သွင်းရသေးပါ။ ကျေးဇူးပြု၍ VPN ကွန်တိန်နာကို အရင်ထည့်သွင်းပါ။ - + Connecting... ချိတ်ဆက်နေပါပြီ... - + Connected ချိတ်ဆက်ပြီးသွားပါပြီ - + Preparing... ပြင်ဆင်နေသည်... - + Settings updated successfully, reconnnection... ဆက်တင်များကို အောင်မြင်စွာ အပ်ဒိတ်လုပ်ပြီးပါပြီ၊ ပြန်လည်ချိတ်ဆက်နေပါသည်... - + Settings updated successfully ဆက်တင်များကို အောင်မြင်စွာ အပ်ဒိတ်လုပ်ပြီးပါပြီ - + The selected protocol is not supported on the current platform ရွေးချယ်ထားသော ပရိုတိုကောကို လက်ရှိပလက်ဖောင်းပေါ်တွင် အ‌ထောက်အပံ့မပေးထားပါ - + unable to create configuration configuration ဖန်တီး၍မရပါ - + Reconnecting... ပြန်လည်ချိတ်ဆက်နေပါသည်... - - - + + + Connect ချိတ်ဆက်မည် - + Disconnecting... အဆက်အသွယ်ဖြတ်နေပါသည်... @@ -254,23 +254,20 @@ Can't be disabled for current server ImportController - Unable to open file - ဖိုင်ကိုဖွင့်၍မရပါ + ဖိုင်ကိုဖွင့်၍မရပါ - - Invalid configuration file - Configuration ဖိုင် မမှန်ကန်ပါ + Configuration ဖိုင် မမှန်ကန်ပါ - + Scanned %1 of %2. %2 ၏ %1 ကို စကင်န်ဖတ်ထားသည်. - + In the imported configuration, potentially dangerous lines were found: တင်သွင်းသည့် configuration တွင်၊ အန္တရာယ်ရှိနိုင်သည့်စာလိုင်းများကို တွေ့ရှိခဲ့သည်: @@ -278,86 +275,86 @@ Can't be disabled for current server InstallController - + %1 installed successfully. %1 ကို အောင်မြင်စွာ ထည့်သွင်းပြီးပါပြီ. - + %1 is already installed on the server. %1 ကို ဆာဗာတွင် ထည့်သွင်းပြီးဖြစ်သည်. - + Added containers that were already installed on the server ဆာဗာတွင် ထည့်သွင်းပြီးသား ကွန်တိန်နာများကို ပေါင်းထည့်ပြီးပါပြီ။ - + Already installed containers were found on the server. All installed containers have been added to the application ထည့်သွင်းပြီးသား ကွန်တိန်နာများကို ဆာဗာပေါ်တွင် တွေ့ရှိခဲ့သည်။ ထည့်သွင်းထားသည့် ကွန်တိန်နာအားလုံးကို အပလီကေးရှင်းထဲသို့ ပေါင်းထည့်ပြီးပါပြီ။ - + Settings updated successfully ဆက်တင်များကို အောင်မြင်စွာ အပ်ဒိတ်လုပ်ပြီးပါပြီ။ - + Server '%1' was rebooted ဆာဗာ '%1' ကို ပြန်လည်စတင်ခဲ့သည်။ - + Server '%1' was removed ဆာဗာ '%1' ကို ဖယ်ရှားခဲ့သည်။ - + All containers from server '%1' have been removed ဆာဗာ '%1' မှ ကွန်တိန်နာအားလုံးကို ဖယ်ရှားလိုက်ပါပြီ။ - + %1 has been removed from the server '%2' %1 ကို ဆာဗာ '%2' မှ ဖယ်ရှားလိုက်ပါပြီ - + Api config removed Api config ကိုဖယ်ရှားလိုက်သည် - + %1 cached profile cleared ကက်ရှ်လုပ်ထားတဲ့ ပရိုဖိုင် %1 ခုကို ရှင်းပြီးပါပြီ - + Please login as the user အသုံးပြုသူအဖြစ် log in ဝင်ရောက်ပါ - + Server added successfully ဆာဗာကို အောင်မြင်စွာ ထည့်သွင်းပြီးပါပြီ - + %1 installed successfully. %1 ခုကို အောင်မြင်စွာ ထည့်သွင်းပြီးပါပြီ. - + API config reloaded API config ကို ပြန်လည်စတင်လိုက်ပါပြီ - + Successfully changed the country of connection to %1 ချိတ်ဆက်မှုနိုင်ငံကို %1 သို့ အောင်မြင်စွာ ပြောင်းလဲလိုက်ပါပြီ @@ -2327,97 +2324,107 @@ Already installed containers were found on the server. All installed containers PageSetupWizardConfigSource - + File with connection settings ချိတ်ဆက်မှုဆက်တင်များပါဝင်သောဖိုင် - + Connection ချိတ်ဆက်မှု - + Settings ဆက်တင်များ - + Enable logs + Support tag + ကူညီပံ့ပိုးမှု tag + + + + Copied + ကူးယူပြီးပါပြီ + + + Insert the key, add a configuration file or scan the QR-code Key ကိုထည့်မည်၊ ဖွဲ့စည်းမှုဖိုင်တစ်ခုကိုထည့်မည် သို့မဟုတ် QR-ကုဒ်ကို စကင်န်ဖတ်မည် - + Insert key Key ကိုထည့်သွင်းမည် - + Insert ထည့်သွင်းမည် - + Continue ဆက်လက်လုပ်ဆောင်မည် - + Other connection options အခြားချိတ်ဆက်မှုရွေးချယ်စရာများ - + VPN by Amnezia Amnezia မှ VPN - + Connect to classic paid and free VPN services from Amnezia Amnezia မှ အခပေးနှင့် အခမဲ့ မူလ VPN ဝန်ဆောင်မှုများသို့ ချိတ်ဆက်မည် - + Self-hosted VPN ကိုယ်တိုင် host လုပ်ထားသော VPN - + Configure Amnezia VPN on your own server Amnezia VPN ကို သင်၏ကိုယ်ပိုင်ဆာဗာပေါ်တွင် စီစဥ်ချိန်ညှိမည် - + Restore from backup အရံဖိုင်မှ ပြန်လည်ရယူမည် - + Open backup file အရံဖိုင်ကို ဖွင့်မည် - + Backup files (*.backup) အရံဖိုင်များ (*.backup) - + Open config file config ဖိုင်ကိုဖွင့်မည် - + QR code QR-ကုဒ် - + I have nothing ကျွန်ုပ်တွင်ဘာမှမရှိပါ @@ -2805,67 +2812,67 @@ Already installed containers were found on the server. All installed containers ရှာဖွေမည် - + Creation date: %1 ဖန်တီးပြုလုပ်သည့်ရက်စွဲ: %1 - + Latest handshake: %1 နောက်ဆုံး handshake လုပ်ခြင်း: %1 - + Data received: %1 လက်ခံရရှိသည့်ဒေတာ: %1 - + Data sent: %1 ပေးပို့လိုက်သည့်ဒေတာ: %1 - + Allowed IPs: %1 - + Rename အမည်ပြောင်းမည် - + Client name ကလိုင်းရင့်အမည် - + Save သိမ်းဆည်းမည် - + Revoke ပြန်ရုပ်သိမ်းမည် - + Revoke the config for a user - %1? အသုံးပြုသူ %1 အတွက် config ကို ပြန်လည်ရုပ်သိမ်းမည်လား? - + The user will no longer be able to connect to your server. ဤအသုံးပြုသူသည် သင့်ဆာဗာသို့ ချိတ်ဆက်နိုင်တော့မည်မဟုတ်ပါ. - + Continue ဆက်လက်လုပ်ဆောင်မည် - + Cancel ပယ်ဖျက်မည် @@ -2952,17 +2959,17 @@ Already installed containers were found on the server. All installed containers PageStart - + Logging was disabled after 14 days, log files were deleted ၁၄ ရက်အကြာတွင် Logging ကို ပိတ်ခဲ့သည်၊ မှတ်တမ်းဖိုင်များကို ဖျက်ပစ်လိုက်ပြီဖြစ်သည် - + Settings restored from backup file ဆက်တင်များကို အရံဖိုင်မှ ပြန်လည်ရယူပြီးပါပြီ - + Logging is enabled. Note that logs will be automaticallydisabled after 14 days, and all log files will be deleted. @@ -3294,17 +3301,17 @@ Already installed containers were found on the server. All installed containers Config တွင် ဆာဗာသို့ချိတ်ဆက်ရန်အတွက် ကွန်တိန်နာများနှင့် အထောက်အထားများ မပါဝင်ပါ - + Error when retrieving configuration from API API မှ စီစဉ်သတ်မှတ်မှုကို ရယူသည့်အခါ အမှားအယွင်းဖြစ်ပေါ်နေသည် - + This config has already been added to the application ဤ config ကို အပလီကေးရှင်းထဲသို့ ထည့်သွင်းပြီးဖြစ်သည် - + ErrorCode: %1. မှားယွင်းမှုကုတ်: %1. @@ -3374,62 +3381,72 @@ Already installed containers were found on the server. All installed containers VPN pool မှားယွင်းမှု: ရရှိနိုင်သောလိပ်စာများမရှိပါ - + + Unable to open config file + + + + VPN connection error VPN ချိတ်ဆက်မှုမှားယွင်းနေပါသည် - + In the response from the server, an empty config was received ဆာဗာမှ တုံ့ပြန်မှုတွင်၊ config အလွတ်တစ်ခုကို လက်ခံရရှိခဲ့သည် - + SSL error occurred SSL မှားယွင်းမှုဖြစ်သွားသည် - + Server response timeout on api request Api တောင်းဆိုမှုတွင် ဆာဗာတုံ့ပြန်မှု အချိန်ကုန်သွားသည် - + Missing AGW public key AGW public key ပျောက်ဆုံးနေသည် - + + Failed to decrypt response payload + + + + QFile error: The file could not be opened QFile မှားယွင်းမှု: ဖိုင်ကို ဖွင့်၍မရပါ - + QFile error: An error occurred when reading from the file QFile မှားယွင်းမှု: ဖိုင်ကိုဖတ်နေစဥ်အတွင်း မှားယွင်းမှုဖြစ်သွားသည် - + QFile error: The file could not be accessed QFile မှားယွင်းမှု: ဖိုင်ကို ဝင်၍မရပါ - + QFile error: An unspecified error occurred QFile မှားယွင်းမှု: သတ်မှတ်မထားသော မှားယွင်းမှုတစ်ခု ဖြစ်ပွားခဲ့သည် - + QFile error: A fatal error occurred QFile မှားယွင်းမှု: ကြီးမားသော မှားယွင်းမှုတစ်ခု ဖြစ်ပွားခဲ့သည် - + QFile error: The operation was aborted QFile မှားယွင်းမှု: လုပ်ငန်းစဥ်ကို ဖျက်သိမ်းလိုက်ရသည် - + Internal error စက်တွင်းဖြစ်သော မှားယွင်းမှု @@ -4036,7 +4053,7 @@ For more detailed information, you can VpnConnection - + Mbps Mbps diff --git a/client/translations/amneziavpn_ru_RU.ts b/client/translations/amneziavpn_ru_RU.ts index c059f186..e48df0fa 100644 --- a/client/translations/amneziavpn_ru_RU.ts +++ b/client/translations/amneziavpn_ru_RU.ts @@ -88,62 +88,62 @@ ConnectionController - + VPN Protocols is not installed. Please install VPN container at first VPN-протоколы не установлены. Пожалуйста, установите протокол - + Connecting... Подключение... - + Connected Подключено - + Preparing... Подготовка... - + Settings updated successfully, reconnnection... Настройки успешно обновлены, переподключение... - + Settings updated successfully Настройки успешно обновлены - + The selected protocol is not supported on the current platform Выбранный протокол не поддерживается на данном устройстве - + unable to create configuration не удалось создать конфигурацию - + Reconnecting... Переподключение... - - - + + + Connect Подключиться - + Disconnecting... Отключение... @@ -258,23 +258,20 @@ Can't be disabled for current server ImportController - Unable to open file - Невозможно открыть файл + Невозможно открыть файл - - Invalid configuration file - Неверный файл конфигурации + Неверный файл конфигурации - + Scanned %1 of %2. Отсканировано %1 из %2. - + In the imported configuration, potentially dangerous lines were found: В импортированной конфигурации были обнаружены потенциально опасные строки: @@ -282,86 +279,86 @@ Can't be disabled for current server InstallController - + %1 installed successfully. %1 успешно установлен. - + %1 is already installed on the server. %1 уже установлен на сервер. - + Added containers that were already installed on the server Добавлены сервисы и протоколы, которые были ранее установлены на сервер - + Already installed containers were found on the server. All installed containers have been added to the application На сервере обнаружены установленные протоколы и сервисы. Все они были добавлены в приложение - + Settings updated successfully Настройки успешно обновлены - + Server '%1' was rebooted Сервер '%1' был перезагружен - + Server '%1' was removed Сервер '%1' был удален - + All containers from server '%1' have been removed Все протоколы и сервисы были удалены с сервера '%1' - + %1 has been removed from the server '%2' %1 был удален с сервера '%2' - + Api config removed Конфигурация API удалена - + %1 cached profile cleared %1 закэшированный профиль очищен - + Please login as the user Пожалуйста, войдите в систему от имени пользователя - + Server added successfully Сервер успешно добавлен - + %1 installed successfully. %1 успешно установлен. - + API config reloaded Конфигурация API перезагружена - + Successfully changed the country of connection to %1 Изменение страны подключения на %1 @@ -2505,7 +2502,7 @@ It's okay as long as it's from someone you trust. Что у вас есть? - + File with connection settings Файл с настройками подключения @@ -2514,92 +2511,102 @@ It's okay as long as it's from someone you trust. Файл с настройками подключения или резервной копией - + Connection Соединение - + Settings Настройки - + Enable logs + Support tag + + + + + Copied + Скопировано + + + Insert the key, add a configuration file or scan the QR-code Вставьте ключ, добавьте файл конфигурации или отсканируйте QR-код - + Insert key Вставьте ключ - + Insert Вставить - + Continue Продолжить - + Other connection options Другие варианты подключения - + VPN by Amnezia VPN от Amnezia - + Connect to classic paid and free VPN services from Amnezia Подключайтесь к классическим платным и бесплатным VPN-сервисам от Amnezia - + Self-hosted VPN Self-hosted VPN - + Configure Amnezia VPN on your own server Настроить VPN на собственном сервере - + Restore from backup Восстановить из резервной копии - + Open backup file Открыть резервную копию - + Backup files (*.backup) Файлы резервных копий (*.backup) - + Open config file Открыть файл с конфигурацией - + QR code QR-код - + I have nothing У меня ничего нет @@ -3068,27 +3075,27 @@ and will not be shared or disclosed to the Amnezia or any third parties Поиск - + Creation date: %1 Дата создания: %1 - + Latest handshake: %1 Последнее рукопожатие: %1 - + Data received: %1 Получено данных: %1 - + Data sent: %1 Отправлено данных: %1 - + Allowed IPs: %1 @@ -3097,42 +3104,42 @@ and will not be shared or disclosed to the Amnezia or any third parties Дата создания: - + Rename Переименовать - + Client name Имя клиента - + Save Сохранить - + Revoke Отозвать - + Revoke the config for a user - %1? Отозвать конфигурацию для пользователя - %1? - + The user will no longer be able to connect to your server. Пользователь больше не сможет подключаться к вашему серверу. - + Continue Продолжить - + Cancel Отменить @@ -3223,17 +3230,17 @@ and will not be shared or disclosed to the Amnezia or any third parties PageStart - + Logging was disabled after 14 days, log files were deleted Логирование было отключено по прошествии 14 дней, файлы логов были удалены. - + Settings restored from backup file Настройки восстановлены из бэкап файла - + Logging is enabled. Note that logs will be automaticallydisabled after 14 days, and all log files will be deleted. @@ -3630,17 +3637,17 @@ and will not be shared or disclosed to the Amnezia or any third parties Конфигурация не содержит каких-либо контейнеров и учетных данных для подключения к серверу - + Error when retrieving configuration from API Ошибка при получении конфигурации из API - + This config has already been added to the application Данная конфигурация уже была добавлена в приложение - + ErrorCode: %1. Код ошибки: %1. @@ -3699,62 +3706,72 @@ and will not be shared or disclosed to the Amnezia or any third parties Ошибка пула VPN: нет доступных адресов - + + Unable to open config file + + + + VPN connection error Ошибка VPN-соединения - + In the response from the server, an empty config was received В ответе от сервера была получена пустая конфигурация - + SSL error occurred Произошла ошибка SSL - + Server response timeout on api request Тайм-аут ответа сервера на запрос API - + Missing AGW public key - + + Failed to decrypt response payload + + + + QFile error: The file could not be opened Ошибка QFile: не удалось открыть файл - + QFile error: An error occurred when reading from the file Ошибка QFile: произошла ошибка при чтении из файла - + QFile error: The file could not be accessed Ошибка QFile: не удалось получить доступ к файлу - + QFile error: An unspecified error occurred Ошибка QFile: произошла неизвестная ошибка - + QFile error: A fatal error occurred Ошибка QFile: произошла фатальная ошибка - + QFile error: The operation was aborted Ошибка QFile: операция была прервана - + Internal error Внутренняя ошибка @@ -4454,7 +4471,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin VpnConnection - + Mbps Мбит/с diff --git a/client/translations/amneziavpn_uk_UA.ts b/client/translations/amneziavpn_uk_UA.ts index 1acb34f9..b34cc88f 100644 --- a/client/translations/amneziavpn_uk_UA.ts +++ b/client/translations/amneziavpn_uk_UA.ts @@ -111,62 +111,62 @@ ConnectionController - + VPN Protocols is not installed. Please install VPN container at first VPN протоколи не встановлено. Будь-ласка, встановіть VPN контейнер - + unable to create configuration Неможливо створити конфігурацію - + Connecting... Підключення... - + Connected Підключено - + Preparing... Підготовка... - + Settings updated successfully, reconnnection... Налаштування оновлено, підключення... - + Settings updated successfully Налаштування оновлено - + The selected protocol is not supported on the current platform Вибраний протокол не підтримується на цьому пристрої - + Reconnecting... Перепідключення... - - - + + + Connect Підключитись - + Disconnecting... Відключаємось... @@ -285,23 +285,20 @@ Can't be disabled for current server ImportController - Unable to open file - Неможливо відкрити файл + Неможливо відкрити файл - - Invalid configuration file - Недійсний файл конфігурації + Недійсний файл конфігурації - + Scanned %1 of %2. Відскановано %1 з %2. - + In the imported configuration, potentially dangerous lines were found: У імпортованій конфігурації знайдено потенційно небезпечні рядки: @@ -309,85 +306,85 @@ Can't be disabled for current server InstallController - + %1 installed successfully. %1 встановлено. - + %1 is already installed on the server. %1 вже встановлено на сервері. - + Added containers that were already installed on the server Додані сервіси і протоколи, які були раніше встановлені на сервері - + Already installed containers were found on the server. All installed containers have been added to the application На сервері знайдені сервіси та протоколи, всі вони додані в застосунок - + Settings updated successfully Налаштування оновлено - + Server '%1' was rebooted Сервер '%1' перезавантажено - + Server '%1' was removed Сервер '%1' був видалений - + All containers from server '%1' have been removed Всі сервіси та протоколи були видалені з сервера '%1' - + %1 has been removed from the server '%2' %1 був видалений з сервера '%2' - + Api config removed Конфігурацію API видалено - + %1 cached profile cleared Кешований профіль %1 очищено - + Please login as the user Буль-ласка, увійдіть в систему від імені користувача - + Server added successfully Сервер додано - + %1 installed successfully. %1 встановлено успішно. - + API config reloaded Конфігурацію API перезавантажено - + Successfully changed the country of connection to %1 Успішно змінено країну підключення на %1 @@ -2591,7 +2588,7 @@ It's okay as long as it's from someone you trust. Виберіть що у вас є - + File with connection settings Файл з налаштуваннями підключення @@ -2600,92 +2597,102 @@ It's okay as long as it's from someone you trust. Файл з налаштуваннями підключення або бекап - + Connection Підключення - + Settings Налаштування - + Enable logs + Support tag + + + + + Copied + Скопійовано + + + Insert the key, add a configuration file or scan the QR-code Вставте ключ, додайте файл конфігурації або відскануйте QR-код - + Insert key Вставити ключ - + Insert Вставити - + Continue Продовжити - + Other connection options Інші параметри підключення - + VPN by Amnezia VPN від Amnezia - + Connect to classic paid and free VPN services from Amnezia Підключайтеся до звичайних платних та безкоштовних VPN-сервісів від Amnezia - + Self-hosted VPN Self-hosted VPN - + Configure Amnezia VPN on your own server Налаштуйте Amnezia VPN на власному сервері - + Restore from backup Відновити із бекапа - + Open backup file Відкрити бекап файл - + Backup files (*.backup) Файли резервної копії (*.backup) - + Open config file Відкрити файл з конфігурацією - + QR code QR-код - + I have nothing У мене нічого нема @@ -3163,27 +3170,27 @@ and will not be shared or disclosed to the Amnezia or any third parties Пошук - + Creation date: %1 Дата створення: %1 - + Latest handshake: %1 Останнє з'єднання: %1 - + Data received: %1 Отримано даних: %1 - + Data sent: %1 Відправлено даних: %1 - + Allowed IPs: %1 @@ -3192,42 +3199,42 @@ and will not be shared or disclosed to the Amnezia or any third parties Дата створення: - + Rename Перейменувати - + Client name Назва клієнта - + Save Зберегти - + Revoke Відкликати - + Revoke the config for a user - %1? Відкликати доступ для користувача - %1? - + The user will no longer be able to connect to your server. Користувач більше не зможе підключатись до вашого сервера - + Continue Продовжити - + Cancel Відмінити @@ -3317,17 +3324,17 @@ and will not be shared or disclosed to the Amnezia or any third parties PageStart - + Logging was disabled after 14 days, log files were deleted Логування було вимкнене через 14 днів, файли журналів були видалені - + Settings restored from backup file Відновлення налаштувань із бекап файлу - + Logging is enabled. Note that logs will be automaticallydisabled after 14 days, and all log files will be deleted. @@ -3726,17 +3733,17 @@ and will not be shared or disclosed to the Amnezia or any third parties Конфігурація не містить контейнерів і облікових даних для підключення до серверу - + Error when retrieving configuration from API - + This config has already been added to the application Ця конфігурація вже була додана в застосунок - + ErrorCode: %1. @@ -3795,62 +3802,72 @@ and will not be shared or disclosed to the Amnezia or any third parties VPN pool error: no available addresses - + + Unable to open config file + + + + VPN connection error - + In the response from the server, an empty config was received - + SSL error occurred - + Server response timeout on api request - + Missing AGW public key - - QFile error: The file could not be opened - - - - - QFile error: An error occurred when reading from the file + + Failed to decrypt response payload - QFile error: The file could not be accessed + QFile error: The file could not be opened - QFile error: An unspecified error occurred + QFile error: An error occurred when reading from the file - QFile error: A fatal error occurred + QFile error: The file could not be accessed + QFile error: An unspecified error occurred + + + + + QFile error: A fatal error occurred + + + + QFile error: The operation was aborted - + Internal error Internal error @@ -4531,7 +4548,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin VpnConnection - + Mbps Mbps diff --git a/client/translations/amneziavpn_ur_PK.ts b/client/translations/amneziavpn_ur_PK.ts index 495aa6ac..0eeddd04 100644 --- a/client/translations/amneziavpn_ur_PK.ts +++ b/client/translations/amneziavpn_ur_PK.ts @@ -89,60 +89,60 @@ ConnectionController - - - + + + Connect جوڑنا - + The selected protocol is not supported on the current platform منتخب کردہ پروٹوکول موجودہ پلیٹ فارم پر تعاون یافتہ نہیں ہے - + VPN Protocols is not installed. Please install VPN container at first وی پی این پروٹوکول انسٹال نہیں ہے,براہ کرم پہلےوی پی این کنٹینر انسٹال کریں - + unable to create configuration تشکیل تیار کرنے میں ناکام - + Connecting... جوڑاجارھاھے.... - + Connected جوڑاجارھاھے - + Reconnecting... دوبارہ جوڑنےکی کوشش... - + Disconnecting... منقطع کرنا... - + Preparing... تیاری کیا جا رہا ہے... - + Settings updated successfully, reconnnection... ترتیب ک ھوگی،دوبارہ جوڑنےکی کوشش... - + Settings updated successfully دوبارہ ترتیب تاذہ کامیاب @@ -252,23 +252,20 @@ Can't be disabled for current server ImportController - Unable to open file - فائل کو کھولنے سے قاصر ہے + فائل کو کھولنے سے قاصر ہے - - Invalid configuration file - غلط کنفیگریشن فائل + غلط کنفیگریشن فائل - + Scanned %1 of %2. سکین%1 کی%2. - + In the imported configuration, potentially dangerous lines were found: @@ -276,86 +273,86 @@ Can't be disabled for current server InstallController - + %1 installed successfully. %1 کامیابی سےنصب. - + %1 is already installed on the server. %1 پہلے ہی سرور پر انسٹال ہے. - + Added containers that were already installed on the server وہ کنٹینرز شامل کیے گئے جو پہلے سے سرور پر نصب تھے - + Already installed containers were found on the server. All installed containers have been added to the application سرور پر پہلے سے نصب کنٹینرز پائے گئے۔ تمام نصب کنٹینرز کو ایپلی کیشن میں شامل کر دیا گیا ہے - + Settings updated successfully ترتیب کامیابی کے ساتھ اپ ڈیٹ ہو گئی - + Server '%1' was rebooted سرور %1 دوبارہ چالو کیا گیا تھا - + Server '%1' was removed سرور %1 ہٹا دیا گیا تھا - + All containers from server '%1' have been removed سرور '%1' سے تمام کنٹینرز ہٹا دیے گئے ہیں - + %1 has been removed from the server '%2' سرور '%2' سے %1 ہٹا دیا گیا ہے - + Api config removed - + %1 cached profile cleared %1 کیش کردہ پروفائل ختم کر دی گئی - + Please login as the user براہ کرم صارف کے طور پر لاگ ان کریں - + Server added successfully سرور کامیابی سے شامل کیا گیا - + %1 installed successfully. - + API config reloaded - + Successfully changed the country of connection to %1 @@ -2386,97 +2383,107 @@ Already installed containers were found on the server. All installed containers کنکشن کی ترتیبات یا بیک اپ والی فائل - + Connection کنکشن - + Settings ترتیبات - + Enable logs - Insert the key, add a configuration file or scan the QR-code - - - - - Insert key + Support tag + Copied + + + + + Insert the key, add a configuration file or scan the QR-code + + + + + Insert key + + + + Insert داخل کریں - + Continue - + Other connection options - + VPN by Amnezia - + Connect to classic paid and free VPN services from Amnezia - + Self-hosted VPN - + Configure Amnezia VPN on your own server - + Restore from backup بیک اپ سے بحال کریں - + Open backup file بیک اپ فائل کو کھولیں - + Backup files (*.backup) بیک اپ فائلیں (*.backup) - + File with connection settings کنکشن کی ترتیبات والی فائل - + Open config file کنفیگ فائل کو کھولیں - + QR code QR کوڈ - + I have nothing میرے پاس کچھ نہیں ہے @@ -2861,27 +2868,27 @@ Already installed containers were found on the server. All installed containers تلاش - + Creation date: %1 - + Latest handshake: %1 - + Data received: %1 - + Data sent: %1 - + Allowed IPs: %1 @@ -2890,42 +2897,42 @@ Already installed containers were found on the server. All installed containers تخلیق کی تاریخ: - + Rename نام تبدیل - + Client name کلائنٹ کا نام - + Save محفوظ - + Revoke واپس لین - + Revoke the config for a user - %1? کیا آپ مستعمل کے لئے کنفیگ کو واپس لینا چاہتے ہیں - %1؟ - + The user will no longer be able to connect to your server. صارف آپ کے سرور سے متصل ہونے کا اختیار نہیں رہے گا. - + Continue جاری رکھیں - + Cancel منسوخ @@ -3043,17 +3050,17 @@ Already installed containers were found on the server. All installed containers PageStart - + Logging was disabled after 14 days, log files were deleted لاگنگ کو 14 دنوں کے بعد غیر فعال کر دیا گیا، لاگ فائلوں کو حذف کر دیا گیا - + Settings restored from backup file ترتیبات بیک اپ فائل سے بحال کردی گئی ہیں - + Logging is enabled. Note that logs will be automaticallydisabled after 14 days, and all log files will be deleted. @@ -3392,22 +3399,22 @@ Already installed containers were found on the server. All installed containers سرور سے منسلک ہونے کا ٹائم آؤٹ - + VPN connection error VPN کنکشن کی خرابی - + Error when retrieving configuration from API آپی سے کنفیگریشن بازیافت کرتے وقت خرابی - + This config has already been added to the application یہ تشکیل پہلے ہی ایپلی کیشن میں شامل کی جا چکی ہے - + ErrorCode: %1. ایرر کوڈ: %1. @@ -3482,57 +3489,67 @@ Already installed containers were found on the server. All installed containers ترتیب میں سرور سے منسلک ہونے کے لیے کوئی کنٹینرز اور اسناد نہیں ہیں - - In the response from the server, an empty config was received + + Unable to open config file - SSL error occurred + In the response from the server, an empty config was received - Server response timeout on api request + SSL error occurred + Server response timeout on api request + + + + Missing AGW public key - + + Failed to decrypt response payload + + + + QFile error: The file could not be opened QFile کی خرابی: فائل کو نہیں کھولا جا سکا - + QFile error: An error occurred when reading from the file کیو فائل کی خرابی: فائل سے پڑھتے وقت ایک خرابی پیش آگئی - + QFile error: The file could not be accessed QFile کی خرابی: فائل تک رسائی نہیں ہو سکی - + QFile error: An unspecified error occurred کیو فائل میں خرابی: ایک غیر متعینہ خرابی پیش آگئی - + QFile error: A fatal error occurred کیو فائل میں خرابی: ایک مہلک خرابی پیش آگئی - + QFile error: The operation was aborted کیو فائل کی خرابی: آپریشن روک دیا گیا تھا - + Internal error داخلی خامی @@ -4073,7 +4090,7 @@ While it offers a blend of security, stability, and speed, it's essential t VpnConnection - + Mbps ایم بی پی ایس diff --git a/client/translations/amneziavpn_zh_CN.ts b/client/translations/amneziavpn_zh_CN.ts index c3f15afb..563089a9 100644 --- a/client/translations/amneziavpn_zh_CN.ts +++ b/client/translations/amneziavpn_zh_CN.ts @@ -89,60 +89,60 @@ ConnectionController - - - + + + Connect 连接 - + VPN Protocols is not installed. Please install VPN container at first 请先安装VPN协议 - + Connecting... 连接中 - + Connected 已连接 - + Reconnecting... 重连中 - + Disconnecting... 断开中 - + Preparing... - + Settings updated successfully, reconnnection... 配置已更新, 重连中... - + Settings updated successfully 配置更新成功 - + The selected protocol is not supported on the current platform 当前平台不支持所选协议 - + unable to create configuration @@ -257,23 +257,12 @@ Can't be disabled for current server ImportController - - Unable to open file - - - - - - Invalid configuration file - - - - + Scanned %1 of %2. 扫描 %1 of %2. - + In the imported configuration, potentially dangerous lines were found: @@ -289,75 +278,75 @@ Can't be disabled for current server 已安装在服务器上 - + %1 installed successfully. %1 安装成功。 - + %1 is already installed on the server. 服务器上已经安装 %1。 - + Added containers that were already installed on the server 添加已安装在服务器上的容器 - + Already installed containers were found on the server. All installed containers have been added to the application 在服务上发现已经安装协议并添加至应用 - + Settings updated successfully 配置更新成功 - + Server '%1' was rebooted 服务器 '%1' 已重新启动 - + Server '%1' was removed 已移除服务器 '%1' - + All containers from server '%1' have been removed 服务器 '%1' 的所有容器已移除 - + %1 has been removed from the server '%2' %1 已从服务器 '%2' 上移除 - + Api config removed - + %1 cached profile cleared - + %1 installed successfully. - + API config reloaded - + Successfully changed the country of connection to %1 @@ -378,12 +367,12 @@ Already installed containers were found on the server. All installed containers 协议已从 - + Please login as the user 请以用户身份登录 - + Server added successfully 增加服务器成功 @@ -2549,97 +2538,107 @@ It's okay as long as it's from someone you trust. 包含连接配置或备份的文件 - + Connection 连接 - + Settings 设置 - + Enable logs - Insert the key, add a configuration file or scan the QR-code - - - - - Insert key + Support tag + Copied + + + + + Insert the key, add a configuration file or scan the QR-code + + + + + Insert key + + + + Insert 插入 - + Continue 继续 - + Other connection options - + VPN by Amnezia - + Connect to classic paid and free VPN services from Amnezia - + Self-hosted VPN - + Configure Amnezia VPN on your own server - + Restore from backup 从备份还原 - + Open backup file 打开备份文件 - + Backup files (*.backup) - + File with connection settings 包含连接配置的文件 - + Open config file 打开配置文件 - + QR code 二维码 - + I have nothing 我没有 @@ -3067,27 +3066,27 @@ and will not be shared or disclosed to the Amnezia or any third parties 搜索 - + Creation date: %1 - + Latest handshake: %1 - + Data received: %1 - + Data sent: %1 - + Allowed IPs: %1 @@ -3096,42 +3095,42 @@ and will not be shared or disclosed to the Amnezia or any third parties 创建日期: - + Rename 重新命名 - + Client name 客户名称 - + Save 保存 - + Revoke 撤销 - + Revoke the config for a user - %1? 撤销用户的配置- %1? - + The user will no longer be able to connect to your server. 该用户将无法再连接到您的服务器. - + Continue 继续 - + Cancel 取消 @@ -3288,17 +3287,17 @@ and will not be shared or disclosed to the Amnezia or any third parties PageStart - + Logging was disabled after 14 days, log files were deleted - + Settings restored from backup file 从备份文件还原配置 - + Logging is enabled. Note that logs will be automaticallydisabled after 14 days, and all log files will be deleted. @@ -3651,6 +3650,11 @@ and will not be shared or disclosed to the Amnezia or any third parties SCP error: Generic failure + + + Unable to open config file + + Sftp error: End-of-file encountered Sftp错误: End-of-file encountered @@ -3704,72 +3708,77 @@ and will not be shared or disclosed to the Amnezia or any third parties Sftp 错误: 远程驱动器中没有媒介 - + VPN connection error VPN 连接错误 - + Error when retrieving configuration from API 从 API 检索配置时出错 - + This config has already been added to the application 该配置已添加到应用程序中 - + In the response from the server, an empty config was received - + SSL error occurred - + Server response timeout on api request - + Missing AGW public key - - QFile error: The file could not be opened - - - - - QFile error: An error occurred when reading from the file + + Failed to decrypt response payload - QFile error: The file could not be accessed + QFile error: The file could not be opened - QFile error: An unspecified error occurred + QFile error: An error occurred when reading from the file - QFile error: A fatal error occurred + QFile error: The file could not be accessed + QFile error: An unspecified error occurred + + + + + QFile error: A fatal error occurred + + + + QFile error: The operation was aborted - + ErrorCode: %1. 错误代码: %1. @@ -3837,7 +3846,7 @@ and will not be shared or disclosed to the Amnezia or any third parties 该配置不包含任何用于连接到服务器的容器和凭据。 - + Internal error @@ -4517,7 +4526,7 @@ While it offers a blend of security, stability, and speed, it's essential t VpnConnection - + Mbps diff --git a/client/ui/controllers/connectionController.cpp b/client/ui/controllers/connectionController.cpp index c7f95000..f8516f6e 100644 --- a/client/ui/controllers/connectionController.cpp +++ b/client/ui/controllers/connectionController.cpp @@ -34,13 +34,13 @@ ConnectionController::ConnectionController(const QSharedPointer &s void ConnectionController::openConnection() { -// #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) -// if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true)) -// { -// emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning); -// return; -// } -// #endif +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true)) + { + emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning); + return; + } +#endif int serverIndex = m_serversModel->getDefaultServerIndex(); QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex); @@ -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 old mode 100644 new mode 100755 index c6f17057..306e7f38 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -32,32 +32,8 @@ namespace constexpr char availableCountries[] = "available_countries"; constexpr char apiConfig[] = "api_config"; + constexpr char authData[] = "auth_data"; } - -#ifdef Q_OS_WINDOWS - QString getNextDriverLetter() - { - QProcess drivesProc; - drivesProc.start("wmic logicaldisk get caption"); - drivesProc.waitForFinished(); - QString drives = drivesProc.readAll(); - qDebug() << drives; - - QString letters = "CFGHIJKLMNOPQRSTUVWXYZ"; - QString letter; - for (int i = letters.size() - 1; i > 0; i--) { - letter = letters.at(i); - if (!drives.contains(letter + ":")) - break; - } - if (letter == "C:") { - // set err info - qDebug() << "Can't find free drive letter"; - return ""; - } - return letter; - } -#endif } InstallController::InstallController(const QSharedPointer &serversModel, const QSharedPointer &containersModel, @@ -135,10 +111,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)); @@ -667,7 +643,7 @@ void InstallController::mountSftpDrive(const QString &port, const QString &passw QString hostname = serverCredentials.hostName; #ifdef Q_OS_WINDOWS - mountPath = getNextDriverLetter() + ":"; + mountPath = Utils::getNextDriverLetter() + ":"; // QString cmd = QString("net use \\\\sshfs\\%1@x.x.x.x!%2 /USER:%1 %3") // .arg(labelTftpUserNameText()) // .arg(labelTftpPortText()) @@ -768,7 +744,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 +802,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,24 +829,26 @@ 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)); newServerConfig.insert(configKey::apiConfig, newApiConfig); + newServerConfig.insert(configKey::authData, authData); + newServerConfig.insert(config_key::crc, serverConfig.value(config_key::crc)); m_serversModel->editServer(newServerConfig, serverIndex); if (reloadServiceConfig) { 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/apiCountryModel.cpp b/client/ui/models/apiCountryModel.cpp index ae58329f..922a9d56 100644 --- a/client/ui/models/apiCountryModel.cpp +++ b/client/ui/models/apiCountryModel.cpp @@ -39,6 +39,9 @@ QVariant ApiCountryModel::data(const QModelIndex &index, int role) const case CountryNameRole: { return countryInfo.value(configKey::serverCountryName).toString(); } + case CountryImageCodeRole: { + return countryInfo.value(configKey::serverCountryCode).toString().toUpper(); + } } return QVariant(); @@ -76,5 +79,6 @@ QHash ApiCountryModel::roleNames() const QHash roles; roles[CountryNameRole] = "countryName"; roles[CountryCodeRole] = "countryCode"; + roles[CountryImageCodeRole] = "countryImageCode"; return roles; } diff --git a/client/ui/models/apiCountryModel.h b/client/ui/models/apiCountryModel.h index 8789158b..b9e243d0 100644 --- a/client/ui/models/apiCountryModel.h +++ b/client/ui/models/apiCountryModel.h @@ -11,7 +11,8 @@ class ApiCountryModel : public QAbstractListModel public: enum Roles { CountryNameRole = Qt::UserRole + 1, - CountryCodeRole + CountryCodeRole, + CountryImageCodeRole }; explicit ApiCountryModel(QObject *parent = nullptr); 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/models/servers_model.cpp b/client/ui/models/servers_model.cpp index 85e5dae2..c87499a7 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -771,5 +771,5 @@ const QString ServersModel::getDefaultServerImagePathCollapsed() if (countryCode.isEmpty()) { return ""; } - return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode); + return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode.toUpper()); } diff --git a/client/ui/qml/Pages2/PageSettingsApiLanguageList.qml b/client/ui/qml/Pages2/PageSettingsApiLanguageList.qml index 234e5142..120313cd 100644 --- a/client/ui/qml/Pages2/PageSettingsApiLanguageList.qml +++ b/client/ui/qml/Pages2/PageSettingsApiLanguageList.qml @@ -90,7 +90,7 @@ PageType { Layout.rightMargin: 32 Layout.alignment: Qt.AlignRight - source: "qrc:/countriesFlags/images/flagKit/" + countryCode + ".svg" + source: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg" } } diff --git a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml index 7f7cf9e1..7c031997 100644 --- a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml +++ b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml @@ -49,6 +49,8 @@ PageType { HeaderType { + property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible() + Layout.fillWidth: true Layout.topMargin: 24 Layout.rightMargin: 16 @@ -56,7 +58,7 @@ PageType { headerText: qsTr("Connection") - actionButtonImage: PageController.isStartPageVisible() ? "qrc:/images/controls/more-vertical.svg" : "" + actionButtonImage: isVisible ? "qrc:/images/controls/more-vertical.svg" : "" actionButtonFunction: function() { moreActionsDrawer.open() } @@ -67,18 +69,19 @@ PageType { parent: root anchors.fill: parent - expandedHeight: root.height * 0.35 + expandedHeight: root.height * 0.5 expandedContent: ColumnLayout { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right - anchors.leftMargin: 16 - anchors.rightMargin: 16 + spacing: 0 HeaderType { Layout.fillWidth: true Layout.topMargin: 32 + Layout.leftMargin: 16 + Layout.rightMargin: 16 headerText: qsTr("Settings") } @@ -87,9 +90,12 @@ PageType { id: switcher Layout.fillWidth: true Layout.topMargin: 16 + Layout.leftMargin: 16 + Layout.rightMargin: 16 text: qsTr("Enable logs") + visible: PageController.isStartPageVisible() checked: SettingsController.isLoggingEnabled onCheckedChanged: { if (checked !== SettingsController.isLoggingEnabled) { @@ -98,6 +104,28 @@ PageType { } } + LabelWithButtonType { + id: supportUuid + Layout.fillWidth: true + Layout.topMargin: 16 + + text: qsTr("Support tag") + descriptionText: SettingsController.getInstallationUuid() + + descriptionOnTop: true + + rightImageSource: "qrc:/images/controls/copy.svg" + rightImageColor: AmneziaStyle.color.paleGray + + visible: SettingsController.getInstallationUuid() !== "" + clickedFunction: function() { + GC.copyToClipBoard(descriptionText) + PageController.showNotificationMessage(qsTr("Copied")) + if (!GC.isMobile()) { + this.rightButton.forceActiveFocus() + } + } + } } } } 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/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 6640df36..617b1091 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -772,7 +772,8 @@ PageType { } } - anchors.fill: parent + width: root.width + height: root.height expandedContent: ColumnLayout { id: expandedContent @@ -783,8 +784,6 @@ PageType { anchors.leftMargin: 16 anchors.rightMargin: 16 - spacing: 8 - onImplicitHeightChanged: { clientInfoDrawer.expandedHeight = expandedContent.implicitHeight + 32 } @@ -797,57 +796,54 @@ PageType { } } - Header2Type { - Layout.fillWidth: true - - headerText: clientName - } - - ColumnLayout - { - id: textColumn - property string textColor: AmneziaStyle.color.mutedGray + Header2TextType { + Layout.maximumWidth: parent.width Layout.bottomMargin: 24 - ParagraphTextType { - color: textColumn.textColor - visible: creationDate - Layout.fillWidth: true + text: clientName + maximumLineCount: 2 + wrapMode: Text.Wrap + elide: Qt.ElideRight + } - text: qsTr("Creation date: %1").arg(creationDate) - } + ParagraphTextType { + color: AmneziaStyle.color.mutedGray + visible: creationDate + Layout.fillWidth: true - ParagraphTextType { - color: textColumn.textColor - visible: latestHandshake - Layout.fillWidth: true + text: qsTr("Creation date: %1").arg(creationDate) + } - text: qsTr("Latest handshake: %1").arg(latestHandshake) - } + ParagraphTextType { + color: AmneziaStyle.color.mutedGray + visible: latestHandshake + Layout.fillWidth: true - ParagraphTextType { - color: textColumn.textColor - visible: dataReceived - Layout.fillWidth: true + text: qsTr("Latest handshake: %1").arg(latestHandshake) + } - text: qsTr("Data received: %1").arg(dataReceived) - } + ParagraphTextType { + color: AmneziaStyle.color.mutedGray + visible: dataReceived + Layout.fillWidth: true - ParagraphTextType { - color: textColumn.textColor - visible: dataSent - Layout.fillWidth: true + text: qsTr("Data received: %1").arg(dataReceived) + } - text: qsTr("Data sent: %1").arg(dataSent) - } + ParagraphTextType { + color: AmneziaStyle.color.mutedGray + visible: dataSent + Layout.fillWidth: true - ParagraphTextType { - color: textColumn.textColor - visible: allowedIps - Layout.fillWidth: true + text: qsTr("Data sent: %1").arg(dataSent) + } - text: qsTr("Allowed IPs: %1").arg(allowedIps) - } + ParagraphTextType { + color: AmneziaStyle.color.mutedGray + visible: allowedIps + Layout.fillWidth: true + + text: qsTr("Allowed IPs: %1").arg(allowedIps) } Item { @@ -952,6 +948,7 @@ PageType { BasicButtonType { id: revokeButton Layout.fillWidth: true + Layout.topMargin: 8 defaultColor: AmneziaStyle.color.transparent hoveredColor: AmneziaStyle.color.translucentWhite 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 old mode 100644 new mode 100755 index 4047365f..1cc69aeb --- a/client/utilities.cpp +++ b/client/utilities.cpp @@ -10,18 +10,72 @@ #include #include "utilities.h" -#include "version.h" + +#ifdef Q_OS_WINDOWS +QString printErrorMessage(DWORD errorCode) { + LPVOID lpMsgBuf; + + DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS; + + DWORD dwLanguageId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US); + + FormatMessageW( + dwFlags, + NULL, + errorCode, + dwLanguageId, + (LPWSTR)&lpMsgBuf, + 0, + NULL + ); + + QString errorMsg = QString::fromWCharArray((LPCWSTR)lpMsgBuf); + LocalFree(lpMsgBuf); + return errorMsg.trimmed(); +} + +QString Utils::getNextDriverLetter() +{ + DWORD drivesBitmask = GetLogicalDrives(); + if (drivesBitmask == 0) { + DWORD error = GetLastError(); + qDebug() << "GetLogicalDrives failed. Error code:" << error; + return ""; + } + + QString letters = "FGHIJKLMNOPQRSTUVWXYZ"; + QString availableLetter; + + for (int i = letters.size() - 1; i >= 0; --i) { + QChar letterChar = letters.at(i); + int driveIndex = letterChar.toLatin1() - 'A'; + + if ((drivesBitmask & (1 << driveIndex)) == 0) { + availableLetter = letterChar; + break; + } + } + + if (availableLetter.isEmpty()) { + qDebug() << "Can't find free drive letter"; + return ""; + } + + return availableLetter; +} +#endif 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; } @@ -109,30 +163,34 @@ QString Utils::usrExecutable(const QString &baseName) bool Utils::processIsRunning(const QString &fileName, const bool fullFlag) { #ifdef Q_OS_WIN - QProcess process; - process.setReadChannel(QProcess::StandardOutput); - process.setProcessChannelMode(QProcess::MergedChannels); - process.start("wmic.exe", - QStringList() << "/OUTPUT:STDOUT" - << "PROCESS" - << "get" - << "Caption"); - process.waitForStarted(); - process.waitForFinished(); - QString processData(process.readAll()); - QStringList processList = processData.split(QRegularExpression("[\r\n]"), Qt::SkipEmptyParts); - foreach (const QString &rawLine, processList) { - const QString line = rawLine.simplified(); - if (line.isEmpty()) { - continue; - } + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnapshot == INVALID_HANDLE_VALUE) { + qWarning() << "Utils::processIsRunning error CreateToolhelp32Snapshot"; + return false; + } - if (line == fileName) { + PROCESSENTRY32W pe32; + pe32.dwSize = sizeof(PROCESSENTRY32W); + + if (!Process32FirstW(hSnapshot, &pe32)) { + CloseHandle(hSnapshot); + qWarning() << "Utils::processIsRunning error Process32FirstW"; + return false; + } + + do { + QString exeFile = QString::fromWCharArray(pe32.szExeFile); + + if (exeFile.compare(fileName, Qt::CaseInsensitive) == 0) { + CloseHandle(hSnapshot); return true; } - } + } while (Process32NextW(hSnapshot, &pe32)); + + CloseHandle(hSnapshot); return false; -#elif defined(Q_OS_IOS) + +#elif defined(Q_OS_IOS) || defined(Q_OS_ANDROID) return false; #else QProcess process; @@ -150,13 +208,45 @@ bool Utils::processIsRunning(const QString &fileName, const bool fullFlag) #endif } -void Utils::killProcessByName(const QString &name) +bool Utils::killProcessByName(const QString &name) { qDebug().noquote() << "Kill process" << name; #ifdef Q_OS_WIN - QProcess::execute("taskkill", QStringList() << "/IM" << name << "/F"); -#elif defined Q_OS_IOS - return; + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnapshot == INVALID_HANDLE_VALUE) + return false; + + PROCESSENTRY32W pe32; + pe32.dwSize = sizeof(PROCESSENTRY32W); + + bool success = false; + + if (Process32FirstW(hSnapshot, &pe32)) { + do { + QString exeFile = QString::fromWCharArray(pe32.szExeFile); + + if (exeFile.compare(name, Qt::CaseInsensitive) == 0) { + HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pe32.th32ProcessID); + if (hProcess != NULL) { + if (TerminateProcess(hProcess, 0)) { + success = true; + } else { + DWORD error = GetLastError(); + qCritical() << "Can't terminate process" << exeFile << "(PID:" << pe32.th32ProcessID << "). Error:" << printErrorMessage(error); + } + CloseHandle(hProcess); + } else { + DWORD error = GetLastError(); + qCritical() << "Can't open process for termination" << exeFile << "(PID:" << pe32.th32ProcessID << "). Error:" << printErrorMessage(error); + } + } + } while (Process32NextW(hSnapshot, &pe32)); + } + + CloseHandle(hSnapshot); + return success; +#elif defined Q_OS_IOS || defined(Q_OS_ANDROID) + return false; #else QProcess::execute(QString("pkill %1").arg(name)); #endif @@ -244,3 +334,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 old mode 100644 new mode 100755 index 9bf8c82a..4a1985b1 --- a/client/utilities.h +++ b/client/utilities.h @@ -7,7 +7,8 @@ #include #ifdef Q_OS_WIN - #include "Windows.h" +#include +#include #endif class Utils : public QObject @@ -27,15 +28,19 @@ public: static bool initializePath(const QString &path); static bool processIsRunning(const QString &fileName, const bool fullFlag = false); - static void killProcessByName(const QString &name); + static bool killProcessByName(const QString &name); static QString openVpnExecPath(); static QString wireguardExecPath(); 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); + static QString getNextDriverLetter(); #endif }; diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 591e396f..ac881bd7 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -56,14 +56,15 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) { #ifdef AMNEZIA_DESKTOP - QString proto = m_settings->defaultContainerName(m_settings->defaultServerIndex()); + auto container = m_settings->defaultContainer(m_settings->defaultServerIndex()); if (IpcClient::Interface()) { if (state == Vpn::ConnectionState::Connected) { IpcClient::Interface()->resetIpStack(); IpcClient::Interface()->flushDns(); - if (!m_vpnConfiguration.value(config_key::configVersion).toInt()) { + if (!m_vpnConfiguration.value(config_key::configVersion).toInt() && container != DockerContainer::Awg + && container != DockerContainer::WireGuard) { QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString(); QString dns2 = m_vpnConfiguration.value(config_key::dns2).toString(); diff --git a/metadata/img-readme/andr.png b/metadata/img-readme/andr.png deleted file mode 100644 index a39cd52f..00000000 Binary files a/metadata/img-readme/andr.png and /dev/null differ diff --git a/metadata/img-readme/download.png b/metadata/img-readme/download.png new file mode 100644 index 00000000..0e6a8850 Binary files /dev/null and b/metadata/img-readme/download.png differ diff --git a/metadata/img-readme/lin.png b/metadata/img-readme/lin.png deleted file mode 100644 index 352eae5a..00000000 Binary files a/metadata/img-readme/lin.png and /dev/null differ diff --git a/metadata/img-readme/mac.png b/metadata/img-readme/mac.png deleted file mode 100644 index 2cbb32ae..00000000 Binary files a/metadata/img-readme/mac.png and /dev/null differ diff --git a/metadata/img-readme/testiny.png b/metadata/img-readme/testiny.png new file mode 100644 index 00000000..4f38a3a9 Binary files /dev/null and b/metadata/img-readme/testiny.png differ diff --git a/metadata/img-readme/win.png b/metadata/img-readme/win.png deleted file mode 100644 index 5a35cf49..00000000 Binary files a/metadata/img-readme/win.png and /dev/null differ