From bb7b64fb96a6703075612b1b4bbaec6547730df3 Mon Sep 17 00:00:00 2001 From: isamnezia <156459471+isamnezia@users.noreply.github.com> Date: Mon, 4 Mar 2024 02:28:10 +0300 Subject: [PATCH 01/28] Fix QML glitches and crash on iOS (#658) Fix QML glitches and crash on iOS and Android --- CMakeLists.txt | 2 +- client/ui/qml/Pages2/PageHome.qml | 4 ++-- client/ui/qml/Pages2/PageSettingsServerInfo.qml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c1a1934..0825cc45 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.4.0.0 +project(${PROJECT} VERSION 4.4.1.1 DESCRIPTION "AmneziaVPN" HOMEPAGE_URL "https://amnezia.org/" ) diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index 1ca74f47..06ce907a 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -165,7 +165,7 @@ PageType { expandedContent: Item { id: serverMenuContainer - implicitHeight: root.height * 0.9 + implicitHeight: Qt.platform.os !== "ios" ? root.height * 0.9 : screen.height * 0.77 Component.onCompleted: { drawer.expandedHeight = serverMenuContainer.implicitHeight @@ -252,7 +252,7 @@ PageType { model: SortFilterProxyModel { id: proxyDefaultServerContainersModel sourceModel: DefaultServerContainersModel - + sorters: [ RoleSorter { roleName: "isInstalled"; sortOrder: Qt.DescendingOrder } ] diff --git a/client/ui/qml/Pages2/PageSettingsServerInfo.qml b/client/ui/qml/Pages2/PageSettingsServerInfo.qml index 01db5695..dc17d98d 100644 --- a/client/ui/qml/Pages2/PageSettingsServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsServerInfo.qml @@ -130,7 +130,7 @@ PageType { } Component.onCompleted: { - if (header.itemAt(0)) { + if (header.itemAt(0) && !GC.isMobile()) { defaultActiveFocusItem = serverName.textField } } From ca633ae882eae765abe7c3bdc283c3c6e6deb8ef Mon Sep 17 00:00:00 2001 From: isamnezia <156459471+isamnezia@users.noreply.github.com> Date: Mon, 4 Mar 2024 15:25:49 +0300 Subject: [PATCH 02/28] Remove VPN configurations after app reset on iOS (#661) --- CMakeLists.txt | 2 +- client/platforms/ios/LogController.swift | 24 +++++++++++++++++++ .../platforms/ios/ios_controller_wrapper.mm | 2 +- client/ui/controllers/settingsController.cpp | 5 ++++ 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0825cc45..77ec33b3 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.4.1.1 +project(${PROJECT} VERSION 4.4.1.2 DESCRIPTION "AmneziaVPN" HOMEPAGE_URL "https://amnezia.org/" ) diff --git a/client/platforms/ios/LogController.swift b/client/platforms/ios/LogController.swift index 7f32ef43..e10b9ce2 100644 --- a/client/platforms/ios/LogController.swift +++ b/client/platforms/ios/LogController.swift @@ -1,4 +1,5 @@ import Foundation +import NetworkExtension public func swiftUpdateLogData(_ qtString: std.string) -> std.string { let qtLog = Log(String(describing: qtString)) @@ -24,3 +25,26 @@ public func swiftDeleteLog() { public func toggleLogging(_ isEnabled: Bool) { Log.isLoggingEnabled = isEnabled } + +public func clearSettings() { + NETunnelProviderManager.loadAllFromPreferences { managers, error in + if let error { + NSLog("clearSettings removeFromPreferences error: \(error.localizedDescription)") + return + } + + managers?.forEach { manager in + manager.removeFromPreferences { error in + if let error { + NSLog("NE removeFromPreferences error: \(error.localizedDescription)") + } else { + manager.loadFromPreferences { error in + if let error { + NSLog("NE loadFromPreferences after remove error: \(error.localizedDescription)") + } + } + } + } + } + } +} diff --git a/client/platforms/ios/ios_controller_wrapper.mm b/client/platforms/ios/ios_controller_wrapper.mm index 4bbe24dd..79f3bcf9 100644 --- a/client/platforms/ios/ios_controller_wrapper.mm +++ b/client/platforms/ios/ios_controller_wrapper.mm @@ -21,7 +21,7 @@ } - (void) vpnConfigurationDidChange:(NSNotification *)notification { - cppController->vpnStatusDidChange(notification); +// cppController->vpnStatusDidChange(notification); } diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index 6ec55321..4aa64533 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -152,7 +152,12 @@ void SettingsController::clearSettings() m_languageModel->changeLanguage( static_cast(m_languageModel->getCurrentLanguageIndex())); m_sitesModel->setRouteMode(Settings::RouteMode::VpnAllSites); + emit changeSettingsFinished(tr("All settings have been reset to default values")); + +#ifdef Q_OS_IOS + AmneziaVPN::clearSettings(); +#endif } void SettingsController::clearCachedProfiles() From 080e1d98c66ca4a22b376302b606dd5269a5cfbb Mon Sep 17 00:00:00 2001 From: albexk Date: Mon, 4 Mar 2024 18:08:55 +0300 Subject: [PATCH 03/28] Add Quick Settings tile (#660) * Add Quick Settings tile - Add multi-client support to AmneziaVpnService - Make AmneziaActivity permanently connected to AmneziaVpnService while it is running - Refactor processing of connection state changes on qt side - Add VpnState DataStore - Add check if AmneziaVpnService is running * Add tile reset when the server is removed from the application --- client/amnezia_application.cpp | 9 +- client/android/AndroidManifest.xml | 20 ++ client/android/build.gradle.kts | 1 + client/android/gradle/libs.versions.toml | 2 + .../src/main/kotlin/ProtocolState.kt | 2 +- .../protocolApi/src/main/kotlin/Status.kt | 4 + client/android/res/values-ru/strings.xml | 5 + client/android/res/values/strings.xml | 5 + .../src/org/amnezia/vpn/AmneziaActivity.kt | 88 +++--- .../src/org/amnezia/vpn/AmneziaApplication.kt | 1 + .../src/org/amnezia/vpn/AmneziaTileService.kt | 272 ++++++++++++++++++ .../src/org/amnezia/vpn/AmneziaVpnService.kt | 143 ++++++--- .../android/src/org/amnezia/vpn/IpcMessage.kt | 5 +- .../src/org/amnezia/vpn/IpcMessenger.kt | 28 +- .../android/src/org/amnezia/vpn/VpnState.kt | 75 +++++ .../org/amnezia/vpn/qt/QtAndroidController.kt | 11 +- .../platforms/android/android_controller.cpp | 55 +--- client/platforms/android/android_controller.h | 11 +- client/protocols/protocols_defs.h | 1 + client/settings.cpp | 2 + client/settings.h | 2 + client/vpnconnection.cpp | 14 +- 22 files changed, 602 insertions(+), 154 deletions(-) create mode 100644 client/android/res/values-ru/strings.xml create mode 100644 client/android/res/values/strings.xml create mode 100644 client/android/src/org/amnezia/vpn/AmneziaTileService.kt create mode 100644 client/android/src/org/amnezia/vpn/VpnState.kt diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 0831b1a2..1ac179fd 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -95,7 +95,14 @@ void AmneziaApplication::init() qFatal("Android logging initialization failed"); } AndroidController::instance()->setSaveLogs(m_settings->isSaveLogs()); - connect(m_settings.get(), &Settings::saveLogsChanged, AndroidController::instance(), &AndroidController::setSaveLogs); + connect(m_settings.get(), &Settings::saveLogsChanged, + AndroidController::instance(), &AndroidController::setSaveLogs); + + connect(m_settings.get(), &Settings::serverRemoved, + AndroidController::instance(), &AndroidController::resetLastServer); + + connect(m_settings.get(), &Settings::settingsCleared, + [](){ AndroidController::instance()->resetLastServer(-1); }); connect(AndroidController::instance(), &AndroidController::initConnectionState, this, [this](Vpn::ConnectionState state) { diff --git a/client/android/AndroidManifest.xml b/client/android/AndroidManifest.xml index 22eed003..548c9bb9 100644 --- a/client/android/AndroidManifest.xml +++ b/client/android/AndroidManifest.xml @@ -56,6 +56,10 @@ + + + + @@ -146,6 +150,22 @@ + + + + + + + + + + + Подключение + Отключение + \ No newline at end of file diff --git a/client/android/res/values/strings.xml b/client/android/res/values/strings.xml new file mode 100644 index 00000000..3fdd9844 --- /dev/null +++ b/client/android/res/values/strings.xml @@ -0,0 +1,5 @@ + + + Connecting + Disconnecting + \ No newline at end of file diff --git a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt index 9a813626..f01e8df6 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt @@ -26,9 +26,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel -import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import org.amnezia.vpn.protocol.ProtocolState import org.amnezia.vpn.protocol.getStatistics import org.amnezia.vpn.protocol.getStatus import org.amnezia.vpn.qt.QtAndroidController @@ -36,11 +34,11 @@ import org.amnezia.vpn.util.Log import org.qtproject.qt.android.bindings.QtActivity private const val TAG = "AmneziaActivity" +const val ACTIVITY_MESSENGER_NAME = "Activity" private const val CHECK_VPN_PERMISSION_ACTION_CODE = 1 private const val CREATE_FILE_ACTION_CODE = 2 private const val OPEN_FILE_ACTION_CODE = 3 -private const val BIND_SERVICE_TIMEOUT = 1000L class AmneziaActivity : QtActivity() { @@ -58,25 +56,17 @@ class AmneziaActivity : QtActivity() { val event = msg.extractIpcMessage() Log.d(TAG, "Handle event: $event") when (event) { - ServiceEvent.CONNECTED -> { - QtAndroidController.onVpnConnected() - } - - ServiceEvent.DISCONNECTED -> { - QtAndroidController.onVpnDisconnected() - doUnbindService() - } - - ServiceEvent.RECONNECTING -> { - QtAndroidController.onVpnReconnecting() + ServiceEvent.STATUS_CHANGED -> { + msg.data?.getStatus()?.let { (state) -> + Log.d(TAG, "Handle protocol state: $state") + QtAndroidController.onVpnStateChanged(state.ordinal) + } } ServiceEvent.STATUS -> { if (isWaitingStatus) { isWaitingStatus = false - msg.data?.getStatus()?.let { (state) -> - QtAndroidController.onStatus(state.ordinal) - } + msg.data?.getStatus()?.let { QtAndroidController.onStatus(it) } } } @@ -87,7 +77,7 @@ class AmneziaActivity : QtActivity() { } ServiceEvent.ERROR -> { - msg.data?.getString(ERROR_MSG)?.let { error -> + msg.data?.getString(MSG_ERROR)?.let { error -> Log.e(TAG, "From VpnService: $error") } // todo: add error reporting to Qt @@ -109,14 +99,15 @@ class AmneziaActivity : QtActivity() { // get a messenger from the service to send actions to the service vpnServiceMessenger.set(Messenger(service)) // send a messenger to the service to process service events - vpnServiceMessenger.send { - Action.REGISTER_CLIENT.packToMessage().apply { - replyTo = activityMessenger - } - } + vpnServiceMessenger.send( + Action.REGISTER_CLIENT.packToMessage { + putString(MSG_CLIENT_NAME, ACTIVITY_MESSENGER_NAME) + }, + replyTo = activityMessenger + ) isServiceConnected = true if (isWaitingStatus) { - vpnServiceMessenger.send(Action.REQUEST_STATUS) + vpnServiceMessenger.send(Action.REQUEST_STATUS, replyTo = activityMessenger) } } @@ -126,6 +117,7 @@ class AmneziaActivity : QtActivity() { vpnServiceMessenger.reset() isWaitingStatus = true QtAndroidController.onServiceDisconnected() + doBindService() } override fun onBindingDied(name: ComponentName?) { @@ -148,8 +140,11 @@ class AmneziaActivity : QtActivity() { Log.d(TAG, "Create Amnezia activity: $intent") mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) vpnServiceMessenger = IpcMessenger( - onDeadObjectException = ::doUnbindService, - messengerName = "VpnService" + "VpnService", + onDeadObjectException = { + doUnbindService() + doBindService() + } ) intent?.let(::processIntent) } @@ -244,10 +239,9 @@ class AmneziaActivity : QtActivity() { private fun doBindService() { Log.d(TAG, "Bind service") Intent(this, AmneziaVpnService::class.java).also { - bindService(it, serviceConnection, BIND_ABOVE_CLIENT) + bindService(it, serviceConnection, BIND_ABOVE_CLIENT and BIND_AUTO_CREATE) } isInBoundState = true - handleBindTimeout() } @MainThread @@ -256,26 +250,14 @@ class AmneziaActivity : QtActivity() { Log.d(TAG, "Unbind service") isWaitingStatus = true QtAndroidController.onServiceDisconnected() - vpnServiceMessenger.reset() isServiceConnected = false + vpnServiceMessenger.send(Action.UNREGISTER_CLIENT, activityMessenger) + vpnServiceMessenger.reset() isInBoundState = false unbindService(serviceConnection) } } - private fun handleBindTimeout() { - mainScope.launch { - if (isWaitingStatus) { - delay(BIND_SERVICE_TIMEOUT) - if (isWaitingStatus && !isServiceConnected) { - Log.d(TAG, "Bind timeout, reset connection status") - isWaitingStatus = false - QtAndroidController.onStatus(ProtocolState.DISCONNECTED.ordinal) - } - } - } - } - /** * Methods of starting and stopping VpnService */ @@ -312,7 +294,7 @@ class AmneziaActivity : QtActivity() { Log.d(TAG, "Connect to VPN") vpnServiceMessenger.send { Action.CONNECT.packToMessage { - putString(VPN_CONFIG, vpnConfig) + putString(MSG_VPN_CONFIG, vpnConfig) } } } @@ -320,7 +302,7 @@ class AmneziaActivity : QtActivity() { private fun startVpnService(vpnConfig: String) { Log.d(TAG, "Start VPN service") Intent(this, AmneziaVpnService::class.java).apply { - putExtra(VPN_CONFIG, vpnConfig) + putExtra(MSG_VPN_CONFIG, vpnConfig) }.also { ContextCompat.startForegroundService(this, it) } @@ -369,6 +351,22 @@ class AmneziaActivity : QtActivity() { } } + @Suppress("unused") + fun resetLastServer(index: Int) { + Log.v(TAG, "Reset server: $index") + mainScope.launch { + VpnStateStore.store { + if (index == -1 || it.serverIndex == index) { + VpnState.defaultState + } else if (it.serverIndex > index) { + it.copy(serverIndex = it.serverIndex - 1) + } else { + it + } + } + } + } + @Suppress("unused") fun saveFile(fileName: String, data: String) { Log.d(TAG, "Save file $fileName") @@ -438,7 +436,7 @@ class AmneziaActivity : QtActivity() { Log.saveLogs = enabled vpnServiceMessenger.send { Action.SET_SAVE_LOGS.packToMessage { - putBoolean(SAVE_LOGS, enabled) + putBoolean(MSG_SAVE_LOGS, enabled) } } } diff --git a/client/android/src/org/amnezia/vpn/AmneziaApplication.kt b/client/android/src/org/amnezia/vpn/AmneziaApplication.kt index 33182887..d8c87bd6 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaApplication.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaApplication.kt @@ -18,6 +18,7 @@ class AmneziaApplication : QtApplication(), CameraXConfig.Provider { super.onCreate() Prefs.init(this) Log.init(this) + VpnStateStore.init(this) Log.d(TAG, "Create Amnezia application") createNotificationChannel() } diff --git a/client/android/src/org/amnezia/vpn/AmneziaTileService.kt b/client/android/src/org/amnezia/vpn/AmneziaTileService.kt new file mode 100644 index 00000000..5ad872a0 --- /dev/null +++ b/client/android/src/org/amnezia/vpn/AmneziaTileService.kt @@ -0,0 +1,272 @@ +package org.amnezia.vpn + +import android.annotation.SuppressLint +import android.app.PendingIntent +import android.content.ComponentName +import android.content.Intent +import android.content.ServiceConnection +import android.net.VpnService +import android.os.Build +import android.os.IBinder +import android.os.Messenger +import android.service.quicksettings.Tile +import android.service.quicksettings.TileService +import androidx.core.content.ContextCompat +import kotlin.LazyThreadSafetyMode.NONE +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch +import org.amnezia.vpn.protocol.ProtocolState +import org.amnezia.vpn.protocol.ProtocolState.CONNECTED +import org.amnezia.vpn.protocol.ProtocolState.CONNECTING +import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED +import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTING +import org.amnezia.vpn.protocol.ProtocolState.RECONNECTING +import org.amnezia.vpn.protocol.ProtocolState.UNKNOWN +import org.amnezia.vpn.util.Log + +private const val TAG = "AmneziaTileService" +private const val DEFAULT_TILE_LABEL = "AmneziaVPN" + +class AmneziaTileService : TileService() { + + private lateinit var scope: CoroutineScope + private var vpnStateListeningJob: Job? = null + private lateinit var vpnServiceMessenger: IpcMessenger + + @Volatile + private var isServiceConnected = false + private var isInBoundState = false + @Volatile + private var isVpnConfigExists = false + + private val serviceConnection: ServiceConnection by lazy(NONE) { + object : ServiceConnection { + override fun onServiceConnected(name: ComponentName?, service: IBinder?) { + Log.d(TAG, "Service ${name?.flattenToString()} was connected") + vpnServiceMessenger.set(Messenger(service)) + isServiceConnected = true + } + + override fun onServiceDisconnected(name: ComponentName?) { + Log.w(TAG, "Service ${name?.flattenToString()} was unexpectedly disconnected") + isServiceConnected = false + vpnServiceMessenger.reset() + updateVpnState(DISCONNECTED) + } + + override fun onBindingDied(name: ComponentName?) { + Log.w(TAG, "Binding to the ${name?.flattenToString()} unexpectedly died") + doUnbindService() + doBindService() + } + } + } + + override fun onCreate() { + super.onCreate() + Log.d(TAG, "Create Amnezia Tile Service") + scope = CoroutineScope(SupervisorJob()) + vpnServiceMessenger = IpcMessenger( + "VpnService", + onDeadObjectException = ::doUnbindService + ) + } + + override fun onDestroy() { + Log.d(TAG, "Destroy Amnezia Tile Service") + doUnbindService() + scope.cancel() + super.onDestroy() + } + + // Workaround for some bugs + override fun onBind(intent: Intent?): IBinder? = + try { + super.onBind(intent) + } catch (e: Throwable) { + Log.e(TAG, "Failed to bind AmneziaTileService: $e") + null + } + + override fun onStartListening() { + super.onStartListening() + Log.d(TAG, "Start listening") + if (AmneziaVpnService.isRunning(applicationContext)) { + Log.d(TAG, "Vpn service is running") + doBindService() + } else { + Log.d(TAG, "Vpn service is not running") + isServiceConnected = false + updateVpnState(DISCONNECTED) + } + vpnStateListeningJob = launchVpnStateListening() + } + + override fun onStopListening() { + Log.d(TAG, "Stop listening") + vpnStateListeningJob?.cancel() + vpnStateListeningJob = null + doUnbindService() + super.onStopListening() + } + + override fun onClick() { + Log.d(TAG, "onClick") + if (isLocked) { + unlockAndRun { onClickInternal() } + } else { + onClickInternal() + } + } + + private fun onClickInternal() { + if (isVpnConfigExists) { + Log.d(TAG, "Change VPN state") + if (qsTile.state == Tile.STATE_INACTIVE) { + Log.d(TAG, "Start VPN") + updateVpnState(CONNECTING) + startVpn() + } else if (qsTile.state == Tile.STATE_ACTIVE) { + Log.d(TAG, "Stop vpn") + updateVpnState(DISCONNECTING) + stopVpn() + } + } else { + Log.d(TAG, "Start Activity") + Intent(this, AmneziaActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + }.also { + startActivityAndCollapseCompat(it) + } + } + } + + private fun doBindService() { + Log.d(TAG, "Bind service") + Intent(this, AmneziaVpnService::class.java).also { + bindService(it, serviceConnection, BIND_ABOVE_CLIENT) + } + isInBoundState = true + } + + private fun doUnbindService() { + if (isInBoundState) { + Log.d(TAG, "Unbind service") + isServiceConnected = false + vpnServiceMessenger.reset() + isInBoundState = false + unbindService(serviceConnection) + } + } + + private fun startVpn() { + if (isServiceConnected) { + connectToVpn() + } else { + if (checkPermission()) { + startVpnService() + doBindService() + } else { + updateVpnState(DISCONNECTED) + } + } + } + + private fun checkPermission() = + if (VpnService.prepare(applicationContext) != null) { + Intent(this, VpnRequestActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + }.also { + startActivityAndCollapseCompat(it) + } + false + } else { + true + } + + private fun startVpnService() = + ContextCompat.startForegroundService( + applicationContext, + Intent(this, AmneziaVpnService::class.java) + ) + + private fun connectToVpn() = vpnServiceMessenger.send(Action.CONNECT) + + private fun stopVpn() = vpnServiceMessenger.send(Action.DISCONNECT) + + @SuppressLint("StartActivityAndCollapseDeprecated") + private fun startActivityAndCollapseCompat(intent: Intent) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + startActivityAndCollapse( + PendingIntent.getActivity( + applicationContext, + 0, + intent, + PendingIntent.FLAG_IMMUTABLE + ) + ) + } else { + @Suppress("DEPRECATION") + startActivityAndCollapse(intent) + } + } + + private fun updateVpnState(state: ProtocolState) { + scope.launch { + VpnStateStore.store { it.copy(protocolState = state) } + } + } + + private fun launchVpnStateListening() = + scope.launch { VpnStateStore.dataFlow().collectLatest(::updateTile) } + + private fun updateTile(vpnState: VpnState) { + Log.d(TAG, "Update tile: $vpnState") + isVpnConfigExists = vpnState.serverName != null + val tile = qsTile ?: return + tile.apply { + label = vpnState.serverName ?: DEFAULT_TILE_LABEL + when (vpnState.protocolState) { + CONNECTED -> { + state = Tile.STATE_ACTIVE + subtitleCompat = null + } + + DISCONNECTED, UNKNOWN -> { + state = Tile.STATE_INACTIVE + subtitleCompat = null + } + + CONNECTING, RECONNECTING -> { + state = Tile.STATE_UNAVAILABLE + subtitleCompat = resources.getString(R.string.connecting) + } + + DISCONNECTING -> { + state = Tile.STATE_UNAVAILABLE + subtitleCompat = resources.getString(R.string.disconnecting) + } + } + updateTile() + } + // double update to fix weird visual glitches + tile.updateTile() + } + + private var Tile.subtitleCompat: CharSequence? + set(value) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + this.subtitle = value + } + } + get() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + return this.subtitle + } + return null + } +} diff --git a/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt b/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt index 78f89ab8..094383e8 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt @@ -1,7 +1,10 @@ package org.amnezia.vpn +import android.app.ActivityManager +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE import android.app.Notification import android.app.PendingIntent +import android.content.Context import android.content.Intent import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED @@ -16,6 +19,7 @@ import android.os.Process import androidx.annotation.MainThread import androidx.core.app.NotificationCompat import androidx.core.app.ServiceCompat +import java.util.concurrent.ConcurrentHashMap import kotlin.LazyThreadSafetyMode.NONE import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope @@ -26,6 +30,7 @@ import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.cancel import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking @@ -39,14 +44,11 @@ import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTING import org.amnezia.vpn.protocol.ProtocolState.RECONNECTING import org.amnezia.vpn.protocol.ProtocolState.UNKNOWN -import org.amnezia.vpn.protocol.Statistics -import org.amnezia.vpn.protocol.Status import org.amnezia.vpn.protocol.VpnException import org.amnezia.vpn.protocol.VpnStartException import org.amnezia.vpn.protocol.awg.Awg import org.amnezia.vpn.protocol.cloak.Cloak import org.amnezia.vpn.protocol.openvpn.OpenVpn -import org.amnezia.vpn.protocol.putStatistics import org.amnezia.vpn.protocol.putStatus import org.amnezia.vpn.protocol.wireguard.Wireguard import org.amnezia.vpn.util.Log @@ -57,12 +59,16 @@ import org.json.JSONObject private const val TAG = "AmneziaVpnService" -const val VPN_CONFIG = "VPN_CONFIG" -const val ERROR_MSG = "ERROR_MSG" -const val SAVE_LOGS = "SAVE_LOGS" +const val MSG_VPN_CONFIG = "VPN_CONFIG" +const val MSG_ERROR = "ERROR" +const val MSG_SAVE_LOGS = "SAVE_LOGS" +const val MSG_CLIENT_NAME = "CLIENT_NAME" const val AFTER_PERMISSION_CHECK = "AFTER_PERMISSION_CHECK" private const val PREFS_CONFIG_KEY = "LAST_CONF" +private const val PREFS_SERVER_NAME = "LAST_SERVER_NAME" +private const val PREFS_SERVER_INDEX = "LAST_SERVER_INDEX" +private const val PROCESS_NAME = "org.amnezia.vpn:amneziaVpnService" private const val NOTIFICATION_ID = 1337 private const val STATISTICS_SENDING_TIMEOUT = 1000L private const val DISCONNECT_TIMEOUT = 5000L @@ -76,6 +82,8 @@ class AmneziaVpnService : VpnService() { private var protocol: Protocol? = null private val protocolCache = mutableMapOf() private var protocolState = MutableStateFlow(UNKNOWN) + private var serverName: String? = null + private var serverIndex: Int = -1 private val isConnected get() = protocolState.value == CONNECTED @@ -89,8 +97,11 @@ class AmneziaVpnService : VpnService() { private var connectionJob: Job? = null private var disconnectionJob: Job? = null private var statisticsSendingJob: Job? = null - private lateinit var clientMessenger: IpcMessenger private lateinit var networkState: NetworkState + private val clientMessengers = ConcurrentHashMap() + + private val isActivityConnected + get() = clientMessengers.any { it.value.name == ACTIVITY_MESSENGER_NAME } private val connectionExceptionHandler = CoroutineExceptionHandler { _, e -> protocolState.value = DISCONNECTED @@ -116,13 +127,22 @@ class AmneziaVpnService : VpnService() { Log.d(TAG, "Handle action: $action") when (action) { Action.REGISTER_CLIENT -> { - clientMessenger.set(msg.replyTo) + val clientName = msg.data.getString(MSG_CLIENT_NAME) + val messenger = IpcMessenger(msg.replyTo, clientName) + clientMessengers[msg.replyTo] = messenger + Log.d(TAG, "Messenger client '$clientName' was registered") + if (clientName == ACTIVITY_MESSENGER_NAME && isConnected) launchSendingStatistics() + } + + Action.UNREGISTER_CLIENT -> { + clientMessengers.remove(msg.replyTo)?.let { + Log.d(TAG, "Messenger client '${it.name}' was unregistered") + if (it.name == ACTIVITY_MESSENGER_NAME) stopSendingStatistics() + } } Action.CONNECT -> { - val vpnConfig = msg.data.getString(VPN_CONFIG) - Prefs.save(PREFS_CONFIG_KEY, vpnConfig) - connect(vpnConfig) + connect(msg.data.getString(MSG_VPN_CONFIG)) } Action.DISCONNECT -> { @@ -130,17 +150,17 @@ class AmneziaVpnService : VpnService() { } Action.REQUEST_STATUS -> { - clientMessenger.send { - ServiceEvent.STATUS.packToMessage { - putStatus(Status.build { - setState(this@AmneziaVpnService.protocolState.value) - }) + clientMessengers[msg.replyTo]?.let { clientMessenger -> + clientMessenger.send { + ServiceEvent.STATUS.packToMessage { + putStatus(this@AmneziaVpnService.protocolState.value) + } } } } Action.SET_SAVE_LOGS -> { - Log.saveLogs = msg.data.getBoolean(SAVE_LOGS) + Log.saveLogs = msg.data.getBoolean(MSG_SAVE_LOGS) } } } @@ -189,7 +209,7 @@ class AmneziaVpnService : VpnService() { Log.d(TAG, "Create Amnezia VPN service") mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) connectionScope = CoroutineScope(SupervisorJob() + Dispatchers.IO + connectionExceptionHandler) - clientMessenger = IpcMessenger(messengerName = "Client") + loadServerData() launchProtocolStateHandler() networkState = NetworkState(this, ::reconnect) } @@ -201,15 +221,13 @@ class AmneziaVpnService : VpnService() { if (isAlwaysOnCompat) { Log.d(TAG, "Start service via Always-on") - connect(Prefs.load(PREFS_CONFIG_KEY)) + connect() } else if (intent?.getBooleanExtra(AFTER_PERMISSION_CHECK, false) == true) { Log.d(TAG, "Start service after permission check") - connect(Prefs.load(PREFS_CONFIG_KEY)) + connect() } else { Log.d(TAG, "Start service") - val vpnConfig = intent?.getStringExtra(VPN_CONFIG) - Prefs.save(PREFS_CONFIG_KEY, vpnConfig) - connect(vpnConfig) + connect(intent?.getStringExtra(MSG_VPN_CONFIG)) } ServiceCompat.startForeground(this, NOTIFICATION_ID, notification, foregroundServiceTypeCompat) return START_REDELIVER_INTENT @@ -219,17 +237,16 @@ class AmneziaVpnService : VpnService() { Log.d(TAG, "onBind by $intent") if (intent?.action == SERVICE_INTERFACE) return super.onBind(intent) isServiceBound = true - if (isConnected) launchSendingStatistics() return vpnServiceMessenger.binder } override fun onUnbind(intent: Intent?): Boolean { Log.d(TAG, "onUnbind by $intent") if (intent?.action != SERVICE_INTERFACE) { - isServiceBound = false - stopSendingStatistics() - clientMessenger.reset() - if (isUnknown || isDisconnected) stopService() + if (clientMessengers.isEmpty()) { + isServiceBound = false + if (isUnknown || isDisconnected) stopService() + } } return true } @@ -238,7 +255,6 @@ class AmneziaVpnService : VpnService() { Log.d(TAG, "onRebind by $intent") if (intent?.action != SERVICE_INTERFACE) { isServiceBound = true - if (isConnected) launchSendingStatistics() } super.onRebind(intent) } @@ -278,17 +294,16 @@ class AmneziaVpnService : VpnService() { */ private fun launchProtocolStateHandler() { mainScope.launch { - protocolState.collect { protocolState -> + // drop first default UNKNOWN state + protocolState.drop(1).collect { protocolState -> Log.d(TAG, "Protocol state changed: $protocolState") when (protocolState) { CONNECTED -> { - clientMessenger.send(ServiceEvent.CONNECTED) networkState.bindNetworkListener() - if (isServiceBound) launchSendingStatistics() + if (isActivityConnected) launchSendingStatistics() } DISCONNECTED -> { - clientMessenger.send(ServiceEvent.DISCONNECTED) networkState.unbindNetworkListener() stopSendingStatistics() if (!isServiceBound) stopService() @@ -300,12 +315,19 @@ class AmneziaVpnService : VpnService() { } RECONNECTING -> { - clientMessenger.send(ServiceEvent.RECONNECTING) stopSendingStatistics() } CONNECTING, UNKNOWN -> {} } + + clientMessengers.send { + ServiceEvent.STATUS_CHANGED.packToMessage { + putStatus(protocolState) + } + } + + VpnStateStore.store { VpnState(protocolState, serverName, serverIndex) } } } } @@ -332,7 +354,17 @@ class AmneziaVpnService : VpnService() { } @MainThread - private fun connect(vpnConfig: String?) { + private fun connect(vpnConfig: String? = null) { + if (vpnConfig == null) { + connectToVpn(Prefs.load(PREFS_CONFIG_KEY)) + } else { + Prefs.save(PREFS_CONFIG_KEY, vpnConfig) + connectToVpn(vpnConfig) + } + } + + @MainThread + private fun connectToVpn(vpnConfig: String) { if (isConnected || protocolState.value == CONNECTING) return Log.d(TAG, "Start VPN connection") @@ -340,6 +372,7 @@ class AmneziaVpnService : VpnService() { protocolState.value = CONNECTING val config = parseConfigToJson(vpnConfig) + saveServerData(config) if (config == null) { onError("Invalid VPN config") protocolState.value = DISCONNECTED @@ -417,24 +450,38 @@ class AmneziaVpnService : VpnService() { private fun onError(msg: String) { Log.e(TAG, msg) mainScope.launch { - clientMessenger.send { + clientMessengers.send { ServiceEvent.ERROR.packToMessage { - putString(ERROR_MSG, msg) + putString(MSG_ERROR, msg) } } } } - private fun parseConfigToJson(vpnConfig: String?): JSONObject? = - try { - vpnConfig?.let { - JSONObject(it) - } - } catch (e: JSONException) { - onError("Invalid VPN config json format: ${e.message}") + private fun parseConfigToJson(vpnConfig: String): JSONObject? = + if (vpnConfig.isBlank()) { null + } else { + try { + JSONObject(vpnConfig) + } catch (e: JSONException) { + onError("Invalid VPN config json format: ${e.message}") + null + } } + private fun saveServerData(config: JSONObject?) { + serverName = config?.opt("description") as String? + serverIndex = config?.opt("serverIndex") as Int? ?: -1 + Prefs.save(PREFS_SERVER_NAME, serverName) + Prefs.save(PREFS_SERVER_INDEX, serverIndex) + } + + private fun loadServerData() { + serverName = Prefs.load(PREFS_SERVER_NAME).ifBlank { null } + if (serverName != null) serverIndex = Prefs.load(PREFS_SERVER_INDEX) + } + private fun checkPermission(): Boolean = if (prepare(applicationContext) != null) { Intent(this, VpnRequestActivity::class.java).apply { @@ -446,4 +493,12 @@ class AmneziaVpnService : VpnService() { } else { true } + + companion object { + fun isRunning(context: Context): Boolean = + (context.getSystemService(ACTIVITY_SERVICE) as ActivityManager) + .runningAppProcesses.any { + it.processName == PROCESS_NAME && it.importance <= IMPORTANCE_FOREGROUND_SERVICE + } + } } diff --git a/client/android/src/org/amnezia/vpn/IpcMessage.kt b/client/android/src/org/amnezia/vpn/IpcMessage.kt index 26c3b9de..2ddff4ef 100644 --- a/client/android/src/org/amnezia/vpn/IpcMessage.kt +++ b/client/android/src/org/amnezia/vpn/IpcMessage.kt @@ -20,9 +20,7 @@ sealed interface IpcMessage { } enum class ServiceEvent : IpcMessage { - CONNECTED, - DISCONNECTED, - RECONNECTING, + STATUS_CHANGED, STATUS, STATISTICS_UPDATE, ERROR @@ -30,6 +28,7 @@ enum class ServiceEvent : IpcMessage { enum class Action : IpcMessage { REGISTER_CLIENT, + UNREGISTER_CLIENT, CONNECT, DISCONNECT, REQUEST_STATUS, diff --git a/client/android/src/org/amnezia/vpn/IpcMessenger.kt b/client/android/src/org/amnezia/vpn/IpcMessenger.kt index 218a165b..58baf31a 100644 --- a/client/android/src/org/amnezia/vpn/IpcMessenger.kt +++ b/client/android/src/org/amnezia/vpn/IpcMessenger.kt @@ -9,11 +9,21 @@ import org.amnezia.vpn.util.Log private const val TAG = "IpcMessenger" class IpcMessenger( + messengerName: String? = null, private val onDeadObjectException: () -> Unit = {}, - private val onRemoteException: () -> Unit = {}, - private val messengerName: String = "Unknown" + private val onRemoteException: () -> Unit = {} ) { private var messenger: Messenger? = null + val name = messengerName ?: "Unknown" + + constructor( + messenger: Messenger, + messengerName: String? = null, + onDeadObjectException: () -> Unit = {}, + onRemoteException: () -> Unit = {} + ) : this(messengerName, onDeadObjectException, onRemoteException) { + this.messenger = messenger + } fun set(messenger: Messenger) { this.messenger = messenger @@ -25,19 +35,29 @@ class IpcMessenger( fun send(msg: () -> Message) = messenger?.sendMsg(msg()) + fun send(msg: Message, replyTo: Messenger) = messenger?.sendMsg(msg.apply { this.replyTo = replyTo }) + fun send(msg: T) where T : Enum, T : IpcMessage = messenger?.sendMsg(msg.packToMessage()) + fun send(msg: T, replyTo: Messenger) + where T : Enum, T : IpcMessage = messenger?.sendMsg(msg.packToMessage().apply { this.replyTo = replyTo }) + private fun Messenger.sendMsg(msg: Message) { try { send(msg) } catch (e: DeadObjectException) { - Log.w(TAG, "$messengerName messenger is dead") + Log.w(TAG, "$name messenger is dead") messenger = null onDeadObjectException() } catch (e: RemoteException) { - Log.w(TAG, "Sending a message to the $messengerName messenger failed: ${e.message}") + Log.w(TAG, "Sending a message to the $name messenger failed: ${e.message}") onRemoteException() } } } + +fun Map.send(msg: () -> Message) = this.values.forEach { it.send(msg) } + +fun Map.send(msg: T) + where T : Enum, T : IpcMessage = this.values.forEach { it.send(msg) } diff --git a/client/android/src/org/amnezia/vpn/VpnState.kt b/client/android/src/org/amnezia/vpn/VpnState.kt new file mode 100644 index 00000000..4d5e9c99 --- /dev/null +++ b/client/android/src/org/amnezia/vpn/VpnState.kt @@ -0,0 +1,75 @@ +package org.amnezia.vpn + +import android.app.Application +import androidx.datastore.core.MultiProcessDataStoreFactory +import androidx.datastore.core.Serializer +import androidx.datastore.dataStoreFile +import java.io.InputStream +import java.io.ObjectInputStream +import java.io.ObjectOutputStream +import java.io.OutputStream +import java.io.Serializable +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.withContext +import org.amnezia.vpn.protocol.ProtocolState +import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED +import org.amnezia.vpn.util.Log + +private const val TAG = "VpnState" +private const val STORE_FILE_NAME = "vpnState" + +data class VpnState( + val protocolState: ProtocolState, + val serverName: String? = null, + val serverIndex: Int = -1 +) : Serializable { + companion object { + private const val serialVersionUID: Long = -1760654961004181606 + val defaultState: VpnState = VpnState(DISCONNECTED) + } +} + +object VpnStateStore { + private lateinit var app: Application + + private val dataStore = MultiProcessDataStoreFactory.create( + serializer = VpnStateSerializer(), + produceFile = { app.dataStoreFile(STORE_FILE_NAME) } + ) + + fun init(app: Application) { + Log.v(TAG, "Init VpnStateStore") + this.app = app + } + + fun dataFlow(): Flow = dataStore.data + + suspend fun store(f: (vpnState: VpnState) -> VpnState) { + try { + dataStore.updateData(f) + } catch (e : Exception) { + Log.e(TAG, "Failed to store VpnState: $e") + } + } +} + +private class VpnStateSerializer : Serializer { + override val defaultValue: VpnState = VpnState.defaultState + + override suspend fun readFrom(input: InputStream): VpnState { + return withContext(Dispatchers.IO) { + ObjectInputStream(input).use { + it.readObject() as VpnState + } + } + } + + override suspend fun writeTo(t: VpnState, output: OutputStream) { + withContext(Dispatchers.IO) { + ObjectOutputStream(output).use { + it.writeObject(t) + } + } + } +} diff --git a/client/android/src/org/amnezia/vpn/qt/QtAndroidController.kt b/client/android/src/org/amnezia/vpn/qt/QtAndroidController.kt index cab810a7..537d9925 100644 --- a/client/android/src/org/amnezia/vpn/qt/QtAndroidController.kt +++ b/client/android/src/org/amnezia/vpn/qt/QtAndroidController.kt @@ -1,18 +1,23 @@ package org.amnezia.vpn.qt +import org.amnezia.vpn.protocol.ProtocolState +import org.amnezia.vpn.protocol.Status + /** * JNI functions of the AndroidController class from android_controller.cpp, * called by events in the Android part of the client */ object QtAndroidController { + + fun onStatus(status: Status) = onStatus(status.state) + fun onStatus(protocolState: ProtocolState) = onStatus(protocolState.ordinal) + external fun onStatus(stateCode: Int) external fun onServiceDisconnected() external fun onServiceError() external fun onVpnPermissionRejected() - external fun onVpnConnected() - external fun onVpnDisconnected() - external fun onVpnReconnecting() + external fun onVpnStateChanged(stateCode: Int) external fun onStatisticsUpdate(rxBytes: Long, txBytes: Long) external fun onFileOpened(uri: String) diff --git a/client/platforms/android/android_controller.cpp b/client/platforms/android/android_controller.cpp index 767004fc..b789f0e0 100644 --- a/client/platforms/android/android_controller.cpp +++ b/client/platforms/android/android_controller.cpp @@ -56,26 +56,10 @@ AndroidController::AndroidController() : QObject() Qt::QueuedConnection); connect( - this, &AndroidController::vpnConnected, this, - [this]() { - qDebug() << "Android event: VPN connected"; - emit connectionStateChanged(Vpn::ConnectionState::Connected); - }, - Qt::QueuedConnection); - - connect( - this, &AndroidController::vpnDisconnected, this, - [this]() { - qDebug() << "Android event: VPN disconnected"; - emit connectionStateChanged(Vpn::ConnectionState::Disconnected); - }, - Qt::QueuedConnection); - - connect( - this, &AndroidController::vpnReconnecting, this, - [this]() { - qDebug() << "Android event: VPN reconnecting"; - emit connectionStateChanged(Vpn::ConnectionState::Reconnecting); + this, &AndroidController::vpnStateChanged, this, + [this](AndroidController::ConnectionState state) { + qDebug() << "Android event: VPN state changed:" << textConnectionState(state); + emit connectionStateChanged(convertState(state)); }, Qt::QueuedConnection); @@ -106,9 +90,7 @@ bool AndroidController::initialize() {"onServiceDisconnected", "()V", reinterpret_cast(onServiceDisconnected)}, {"onServiceError", "()V", reinterpret_cast(onServiceError)}, {"onVpnPermissionRejected", "()V", reinterpret_cast(onVpnPermissionRejected)}, - {"onVpnConnected", "()V", reinterpret_cast(onVpnConnected)}, - {"onVpnDisconnected", "()V", reinterpret_cast(onVpnDisconnected)}, - {"onVpnReconnecting", "()V", reinterpret_cast(onVpnReconnecting)}, + {"onVpnStateChanged", "(I)V", reinterpret_cast(onVpnStateChanged)}, {"onStatisticsUpdate", "(JJ)V", reinterpret_cast(onStatisticsUpdate)}, {"onFileOpened", "(Ljava/lang/String;)V", reinterpret_cast(onFileOpened)}, {"onConfigImported", "(Ljava/lang/String;)V", reinterpret_cast(onConfigImported)}, @@ -158,6 +140,11 @@ void AndroidController::stop() callActivityMethod("stop", "()V"); } +void AndroidController::resetLastServer(int serverIndex) +{ + callActivityMethod("resetLastServer", "(I)V", serverIndex); +} + void AndroidController::saveFile(const QString &fileName, const QString &data) { callActivityMethod("saveFile", "(Ljava/lang/String;Ljava/lang/String;)V", @@ -370,30 +357,14 @@ void AndroidController::onVpnPermissionRejected(JNIEnv *env, jobject thiz) } // static -void AndroidController::onVpnConnected(JNIEnv *env, jobject thiz) +void AndroidController::onVpnStateChanged(JNIEnv *env, jobject thiz, jint stateCode) { Q_UNUSED(env); Q_UNUSED(thiz); - emit AndroidController::instance()->vpnConnected(); -} + auto state = ConnectionState(stateCode); -// static -void AndroidController::onVpnDisconnected(JNIEnv *env, jobject thiz) -{ - Q_UNUSED(env); - Q_UNUSED(thiz); - - emit AndroidController::instance()->vpnDisconnected(); -} - -// static -void AndroidController::onVpnReconnecting(JNIEnv *env, jobject thiz) -{ - Q_UNUSED(env); - Q_UNUSED(thiz); - - emit AndroidController::instance()->vpnReconnecting(); + emit AndroidController::instance()->vpnStateChanged(state); } // static diff --git a/client/platforms/android/android_controller.h b/client/platforms/android/android_controller.h index 86b117f7..3491d837 100644 --- a/client/platforms/android/android_controller.h +++ b/client/platforms/android/android_controller.h @@ -20,9 +20,9 @@ public: // keep synchronized with org.amnezia.vpn.protocol.ProtocolState enum class ConnectionState { + DISCONNECTED, CONNECTED, CONNECTING, - DISCONNECTED, DISCONNECTING, RECONNECTING, UNKNOWN @@ -30,6 +30,7 @@ public: ErrorCode start(const QJsonObject &vpnConfig); void stop(); + void resetLastServer(int serverIndex); void setNotificationText(const QString &title, const QString &message, int timerSec); void saveFile(const QString &fileName, const QString &data); QString openFile(const QString &filter); @@ -48,9 +49,7 @@ signals: void serviceDisconnected(); void serviceError(); void vpnPermissionRejected(); - void vpnConnected(); - void vpnDisconnected(); - void vpnReconnecting(); + void vpnStateChanged(ConnectionState state); void statisticsUpdated(quint64 rxBytes, quint64 txBytes); void fileOpened(QString uri); void configImported(QString config); @@ -77,9 +76,7 @@ private: static void onServiceDisconnected(JNIEnv *env, jobject thiz); static void onServiceError(JNIEnv *env, jobject thiz); static void onVpnPermissionRejected(JNIEnv *env, jobject thiz); - static void onVpnConnected(JNIEnv *env, jobject thiz); - static void onVpnDisconnected(JNIEnv *env, jobject thiz); - static void onVpnReconnecting(JNIEnv *env, jobject thiz); + static void onVpnStateChanged(JNIEnv *env, jobject thiz, jint stateCode); static void onStatisticsUpdate(JNIEnv *env, jobject thiz, jlong rxBytes, jlong txBytes); static void onConfigImported(JNIEnv *env, jobject thiz, jstring data); static void onFileOpened(JNIEnv *env, jobject thiz, jstring uri); diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index f83a0067..8ab5594f 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -20,6 +20,7 @@ namespace amnezia constexpr char dns1[] = "dns1"; constexpr char dns2[] = "dns2"; + constexpr char serverIndex[] = "serverIndex"; constexpr char description[] = "description"; constexpr char name[] = "name"; constexpr char cert[] = "cert"; diff --git a/client/settings.cpp b/client/settings.cpp index 475c52e7..223719e2 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -68,6 +68,7 @@ void Settings::removeServer(int index) servers.removeAt(index); setServersArray(servers); + emit serverRemoved(index); } bool Settings::editServer(int index, const QJsonObject &server) @@ -338,6 +339,7 @@ QString Settings::secondaryDns() const void Settings::clearSettings() { m_settings.clearSettings(); + emit settingsCleared(); } ServerCredentials Settings::defaultServerCredentials() const diff --git a/client/settings.h b/client/settings.h index ed302653..613d567b 100644 --- a/client/settings.h +++ b/client/settings.h @@ -191,6 +191,8 @@ public: signals: void saveLogsChanged(bool enabled); + void serverRemoved(int serverIndex); + void settingsCleared(); private: QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index d267584a..c3719562 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -270,6 +270,7 @@ QJsonObject VpnConnection::createVpnConfiguration(int serverIndex, const ServerC ErrorCode *errorCode) { QJsonObject vpnConfiguration; + vpnConfiguration[config_key::serverIndex] = serverIndex; for (ProtocolEnumNS::Proto proto : ContainerProps::protocolsForContainer(container)) { auto s = m_settings->server(serverIndex); @@ -471,10 +472,15 @@ void VpnConnection::disconnectFromVpn() #ifdef Q_OS_ANDROID if (m_vpnProtocol && m_vpnProtocol.data()) { - connect(AndroidController::instance(), &AndroidController::vpnDisconnected, this, - [this]() { - onConnectionStateChanged(Vpn::ConnectionState::Disconnected); - }, Qt::SingleShotConnection); + auto *const connection = new QMetaObject::Connection; + *connection = connect(AndroidController::instance(), &AndroidController::vpnStateChanged, this, + [this, connection](AndroidController::ConnectionState state) { + if (state == AndroidController::ConnectionState::DISCONNECTED) { + onConnectionStateChanged(Vpn::ConnectionState::Disconnected); + disconnect(*connection); + delete connection; + } + }); m_vpnProtocol.data()->stop(); } #endif From 17748cca474fb07f63f0f45ad6d0ce31d4019b93 Mon Sep 17 00:00:00 2001 From: Dan Nguyen Date: Mon, 4 Mar 2024 22:35:34 +0700 Subject: [PATCH 04/28] ISSUE: In start page, icon is highlighted not correctly when press ESC key ROOT CAUSE: The button state is decided by the attribute isServerInfoShow and it was added by commit 68fe20ddf637acc45e459f84d1619887e0501f4f. The logic to decide whether server info showed is not correct ACTION: Revert commit 68fe20ddf637acc45e459f84d1619887e0501f4f --- client/ui/qml/Pages2/PageStart.qml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index 0bedb010..ad007b48 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -43,9 +43,6 @@ PageType { } function onClosePage() { - tabBar.isServerInfoShow = tabBarStackView.currentItem.objectName !== PageController.getPagePath(PageEnum.PageSettingsServerInfo) - && tabBarStackView.currentItem.objectName !== PageController.getPagePath(PageEnum.PageSettingsSplitTunneling) - if (tabBarStackView.depth <= 1) { return } @@ -60,8 +57,6 @@ PageType { } else { tabBarStackView.push(pagePath, { "objectName" : pagePath }, StackView.Immediate) } - - tabBar.isServerInfoShow = (page === PageEnum.PageSettingsServerInfo) || (page === PageEnum.PageSettingsSplitTunneling) || tabBar.isServerInfoShow } function onGoToStartPage() { @@ -147,7 +142,6 @@ PageType { var pagePath = PageController.getPagePath(page) tabBarStackView.clear(StackView.Immediate) tabBarStackView.replace(pagePath, { "objectName" : pagePath }, StackView.Immediate) - tabBar.isServerInfoShow = false } Component.onCompleted: { @@ -161,7 +155,6 @@ PageType { id: tabBar property int previousIndex: 0 - property bool isServerInfoShow: false anchors.right: parent.right anchors.left: parent.left @@ -192,7 +185,7 @@ PageType { } TabImageButtonType { - isSelected: tabBar.isServerInfoShow ? false : tabBar.currentIndex === 0 + isSelected: tabBar.currentIndex === 0 image: "qrc:/images/controls/home.svg" onClicked: { tabBarStackView.goToTabBarPage(PageEnum.PageHome) @@ -226,7 +219,7 @@ PageType { } TabImageButtonType { - isSelected: tabBar.isServerInfoShow ? true : tabBar.currentIndex === 2 + isSelected: tabBar.currentIndex === 2 image: "qrc:/images/controls/settings-2.svg" onClicked: { tabBarStackView.goToTabBarPage(PageEnum.PageSettings) From 1137e169ea499c352ee6977a69d44673b5150f67 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 4 Mar 2024 21:45:04 +0300 Subject: [PATCH 05/28] removed the replacement of https by http in apiController --- client/ui/controllers/apiController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/controllers/apiController.cpp b/client/ui/controllers/apiController.cpp index 0d8c75e3..036a580d 100644 --- a/client/ui/controllers/apiController.cpp +++ b/client/ui/controllers/apiController.cpp @@ -90,7 +90,7 @@ void ApiController::updateServerConfigFromApi() request.setRawHeader("Authorization", "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8()); QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString(); - request.setUrl(endpoint.replace("https", "http")); // todo remove + request.setUrl(endpoint); QString protocol = serverConfig.value(configKey::protocol).toString(); From 5b4ec608c838012e9d4119b2a875c4b9f6e2fa2b Mon Sep 17 00:00:00 2001 From: Shehab Ahmed Date: Tue, 5 Mar 2024 22:49:30 +0200 Subject: [PATCH 06/28] pushing the Burmese translation file (#669) Burmese translation --- client/CMakeLists.txt | 1 + client/translations/amneziavpn_my_MM.ts | 3162 +++++++++++++++++++++++ client/ui/models/languageModel.cpp | 3 + client/ui/models/languageModel.h | 3 +- 4 files changed, 3168 insertions(+), 1 deletion(-) create mode 100644 client/translations/amneziavpn_my_MM.ts diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 439e10c5..426b6456 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -67,6 +67,7 @@ set(AMNEZIAVPN_TS_FILES ${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_zh_CN.ts ${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_fa_IR.ts ${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ar.ts + ${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_my_MM.ts ) file(GLOB_RECURSE AMNEZIAVPN_TS_SOURCES *.qrc *.cpp *.h *.ui) diff --git a/client/translations/amneziavpn_my_MM.ts b/client/translations/amneziavpn_my_MM.ts new file mode 100644 index 00000000..d1ab6f5c --- /dev/null +++ b/client/translations/amneziavpn_my_MM.ts @@ -0,0 +1,3162 @@ + + + + + ConnectionController + + + VPN Protocols is not installed. + Please install VPN container at first + VPN ပရိုတိုကောများကို မထည့်သွင်းရသေးပါ။ +ကျေးဇူးပြု၍ VPN ကွန်တိန်နာကို အရင်ထည့်သွင်းပါ။ + + + + Connection... + ချိတ်ဆက်နေပါပြီ... + + + + Connected + ချိတ်ဆက်ပြီးသွားပါပြီ + + + + Settings updated successfully, Reconnnection... + ဆက်တင်များကို အောင်မြင်စွာ အပ်ဒိတ်လုပ်ပြီးပါပြီ၊ ပြန်လည်ချိတ်ဆက်နေပါသည်... + + + + Settings updated successfully + ဆက်တင်များကို အောင်မြင်စွာ အပ်ဒိတ်လုပ်ပြီးပါပြီ။ + + + + Reconnection... + ပြန်လည်ချိတ်ဆက်နေပါသည်... + + + + + + + Connect + ချိတ်ဆက်မည် + + + + Disconnection... + အဆက်အသွယ်ဖြတ်နေပါသည်... + + + + ConnectionTypeSelectionDrawer + + + Add new connection + ချိတ်ဆက်မှုအသစ်ထည့်သွင်းပါ။ + + + + Configure your server + သင်၏ဆာဗာကို စီစဉ်ချိန်ညှိပါ။ + + + + Open config file, key or QR code + config ဖိုင်၊ key သို့မဟုတ် QR ကုဒ်ကို ဖွင့်ပါ။ + + + + ContextMenuType + + + C&ut + C&ut + + + + &Copy + &Copy + + + + &Paste + &Paste + + + + &SelectAll + &SelectAll + + + + ExportController + + + Access error! + အသုံးပြုခွင့်တွင်အမှားပါနေပါသည်! + + + + HomeContainersListView + + + Unable change protocol while there is an active connection + လက်ရှိချိတ်ဆက်မှုတစ်ခုရှိနေချိန်တွင် ပရိုတိုကောကို ပြောင်းလဲ၍မရပါ။ + + + + The selected protocol is not supported on the current platform + ရွေးချယ်ထားသော ပရိုတိုကောကို လက်ရှိပလက်ဖောင်းပေါ်တွင် အ‌ထောက်အပံ့မပေးထားပါ။ + + + + HomeSplitTunnelingDrawer + + + Split tunneling + Split tunneling + + + + Allows you to connect to some sites or applications through a VPN connection and bypass others + VPN ချိတ်ဆက်မှုကြားခံ၍ အချို့သောဆိုက်များ သို့မဟုတ် အပလီကေးရှင်းများသို့ ချိတ်ဆက်ဖို့ရန်နှင့် အခြားအရာများကို ကျော်ဖြတ်ရန် လုပ်ဆောင်ပေးသည်။ + + + + Split tunneling on the server + ဆာဗာပေါ်တွင် split tunneling အသုံးပြုထားပါသည်။ + + + + Enabled +Can't be disabled for current server + ဖွင့်ထားပါသည်။ +လက်ရှိဆာဗာအတွက် ပိတ်၍မရပါ။ + + + + Site-based split tunneling + ဝက်ဆိုဒ်အခြေပြု split tunneling + + + + Enabled + ဖွင့်ထားပါသည်။ + + + + Disabled + ပိတ်ထားပါသည်။ + + + + App-based split tunneling + App အခြေပြု split tunneling + + + + ImportController + + + Scanned %1 of %2. + %2 ၏ %1 ကို စကင်န်ဖတ်ထားသည်. + + + + 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' မှ ဖယ်ရှားလိုက်ပါပြီ + + + + Please login as the user + အသုံးပြုသူအဖြစ် log in ဝင်ရောက်ပါ။ + + + + Server added successfully + ဆာဗာကို အောင်မြင်စွာ ထည့်သွင်းပြီးပါပြီ။ + + + + KeyChainClass + + + Read key failed: %1 + key ဖတ်ယူမှု မအောင်မြင်ပါ: %1 + + + + Write key failed: %1 + key ရေးမှု မအောင်မြင်ပါ: %1 + + + + Delete key failed: %1 + key ဖျက်သိမ်းမှု မအောင်မြင်ပါ: %1 + + + + NotificationHandler + + + + AmneziaVPN + AmneziaVPN + + + + VPN Connected + VPN ချိတ်ဆက်ထားပါပြီ + + + + VPN Disconnected + VPN ဖြုတ်လိုက်ပါပြီ + + + + AmneziaVPN notification + AmneziaVPN နိုတီ + + + + Unsecured network detected: + လုံခြုံမှုမရှိသောကွန်ရက်မှန်း ထောက်လှန်းမိသည်: + + + + PageDeinstalling + + + Removing services from %1 + ဝန်ဆောင်မှုများကို %1 မှ ဖယ်ရှားနေပါသည်။ + + + + Usually it takes no more than 5 minutes + များသောအားဖြင့် 5 မိနစ်ထက်မပိုပါ။ + + + + PageHome + + + Split tunneling enabled + split tunnelling ဖွင့်ထားပါသည်။ + + + + Split tunneling disabled + split tunnelling ပိတ်ထားပါသည်။ + + + + VPN protocol + VPN ပရိုတိုကော + + + + Servers + ဆာဗာများ + + + + Unable change server while there is an active connection + လက်ရှိချိတ်ဆက်မှုတစ်ခုရှိနေချိန်တွင် ဆာဗာကို ပြောင်းလဲ၍မရပါ။ + + + + PageProtocolAwgSettings + + + AmneziaWG settings + AmneziaWG ဆက်တင်များ + + + + Port + Port + + + + Remove AmneziaWG + AmneziaWG ကို ဖယ်ရှားမည်။ + + + + Remove AmneziaWG from server? + AmneziaWG ကို ဆာဗာမှ ဖယ်ရှားမည်လား? + + + + All users with whom you shared a connection will no longer be able to connect to it. + သင်နှင့်အတူချိတ်ဆက်မှုတစ်ခုကို မျှဝေထားသည့် အသုံးပြုသူအားလုံး ဤချိတ်ဆက်မှုကိုချိတ်ဆက်နိုင်တော့မည်မဟုတ်ပါ. + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + Cancel + ပယ်ဖျက်မည် + + + + Save and Restart Amnezia + သိမ်းဆည်းပြီး Amnezia ကို ပြန်လည်စတင်မည်။ + + + + PageProtocolCloakSettings + + + Cloak settings + ဖုံးကွယ်အသွင်ယူမှု ဆက်တင်များ + + + + Disguised as traffic from + traffic အဖြစ် အသွင်ယူထားသည် + + + + Port + Port + + + + + Cipher + စာဝှက် + + + + Save and Restart Amnezia + သိမ်းဆည်းပြီး Amnezia ကို ပြန်လည်စတင်မည်။ + + + + PageProtocolOpenVpnSettings + + + OpenVPN settings + OpenVPN ဆက်တင်များ + + + + VPN address subnet + VPN လိပ်စာ ကွန်ရက်ခွဲ + + + + Network protocol + ကွန်ယက် ပရိုတိုကော + + + + Port + Port + + + + Auto-negotiate encryption + အလိုအလျောက် ညှိနှိုင်း ကုဒ်ဝှက်ခြင်း + + + + + Hash + Hash + + + + SHA512 + SHA512 + + + + SHA384 + SHA384 + + + + SHA256 + SHA256 + + + + SHA3-512 + SHA3-512 + + + + SHA3-384 + SHA3-384 + + + + SHA3-256 + SHA3-256 + + + + whirlpool + whirlpool + + + + BLAKE2b512 + BLAKE2b512 + + + + BLAKE2s256 + BLAKE2s256 + + + + SHA1 + SHA1 + + + + + Cipher + စာဝှက် + + + + AES-256-GCM + AES-256-GCM + + + + AES-192-GCM + AES-192-GCM + + + + AES-128-GCM + AES-128-GCM + + + + AES-256-CBC + AES-256-CBC + + + + AES-192-CBC + AES-192-CBC + + + + AES-128-CBC + AES-128-CBC + + + + ChaCha20-Poly1305 + ChaCha20-Poly1305 + + + + ARIA-256-CBC + ARIA-256-CBC + + + + CAMELLIA-256-CBC + CAMELLIA-256-CBC + + + + none + none + + + + TLS auth + TLS auth + + + + Block DNS requests outside of VPN + VPN ပြင်ပရှိ DNS တောင်းဆိုမှုများကို ပိတ်ပင်မည်။ + + + + Additional client configuration commands + ထပ်တိုး client ဖွဲ့စည်းမှုဆိုင်ရာ ညွှန်ကြားချက်များ + + + + + Commands: + အမိန့်ပေးခိုင်းစေချက်များ: + + + + Additional server configuration commands + ထပ်တိုး ဆာဗာ ဖွဲ့စည်းမှုဆိုင်ရာ ညွှန်ကြားချက်များ + + + + Remove OpenVPN + AmneziaWG ကို ဖယ်ရှားမည်။ + + + + Remove OpenVpn from server? + AmneziaWG ကို ဆာဗာမှ ဖယ်ရှားမည်လား? + + + + All users with whom you shared a connection will no longer be able to connect to it. + သင်နှင့်အတူချိတ်ဆက်မှုတစ်ခုကို မျှဝေထားသည့် အသုံးပြုသူအားလုံး ဤချိတ်ဆက်မှုကိုချိတ်ဆက်နိုင်တော့မည်မဟုတ်ပါ. + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + Cancel + ပယ်ဖျက်မည် + + + + Save and Restart Amnezia + သိမ်းဆည်းပြီး Amnezia ကို ပြန်လည်စတင်မည်။ + + + + PageProtocolRaw + + + settings + ဆက်တင်များ + + + + Show connection options + ချိတ်ဆက်မှုရွေးချယ်စရာများကို ပြပါ။ + + + + Connection options %1 + ချိတ်ဆက်မှုရွေးချယ်စရာများ %1 + + + + Remove + ဖယ်ရှားမည် + + + + Remove %1 from server? + %1 ကို ဆာဗာမှ ဖယ်ရှားမည်လား? + + + + All users with whom you shared a connection will no longer be able to connect to it. + သင်နှင့်အတူချိတ်ဆက်မှုတစ်ခုကို မျှဝေထားသည့် အသုံးပြုသူအားလုံး ဤချိတ်ဆက်မှုကိုချိတ်ဆက်နိုင်တော့မည်မဟုတ်ပါ. + + + All users who you shared a connection with will no longer be able to connect to it. + Все пользователи, с которыми вы поделились этим VPN-протоколом, больше не смогут к нему подключаться. + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + Cancel + ပယ်ဖျက်မည် + + + + PageProtocolShadowSocksSettings + + + ShadowSocks settings + ShadowSocks ဆက်တင်များ + + + + Port + Port + + + + + Cipher + စာဝှက် + + + + Save and Restart Amnezia + သိမ်းဆည်းပြီး Amnezia ကို ပြန်လည်စတင်မည်။ + + + + PageServerContainers + + Continue + Продолжить + + + + PageServiceDnsSettings + + + A DNS service is installed on your server, and it is only accessible via VPN. + + DNS ဝန်ဆောင်မှုကို သင့်ဆာဗာတွင် ထည့်သွင်းထားပြီးဖြစ်ပြီး ၎င်းကို VPN မှတစ်ဆင့်သာ အသုံးပြုနိုင်သည်. + + + + + The DNS address is the same as the address of your server. You can configure DNS in the settings, under the connections tab. + DNS လိပ်စာသည် သင့်ဆာဗာလိပ်စာနှင့် အတူတူပင်ဖြစ်ပါသည်။ ချိတ်ဆက်မှုတက်ဘ်အောက်ရှိ ဆက်တင်များတွင် DNS ကို ပြင်ဆင်ချိန်ညှိနိုင်ပါသည်. + + + + Remove + ဖယ်ရှားမည် + + + + Remove %1 from server? + %1 ကို ဆာဗာမှ ဖယ်ရှားမည်လား? + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + Cancel + ပယ်ဖျက်မည် + + + + PageServiceSftpSettings + + + Settings updated successfully + ဆက်တင်များကို အောင်မြင်စွာ အပ်ဒိတ်လုပ်ပြီးပါပြီ။ + + + + SFTP settings + SFTP ဆက်တင်များ + + + + Host + Host + + + + + + + Copied + ကူးယူပြီးပါပြီ + + + + Port + Port + + + + Login + လော့ဂ်အင်ဝင်မည် + + + + Password + စကားဝှက် + + + + Mount folder on device + ဖိုင်တွဲကို စက်တွင် တပ်ဆင်မည်။ + + + + In order to mount remote SFTP folder as local drive, perform following steps: <br> + အဝေးမှ SFTP ဖိုင်တွဲကို စက်တွင်း drive အဖြစ် တပ်ဆင်ရန်အတွက် အောက်ပါအဆင့်များကို လုပ်ဆောင်ပါ: <br> + + + + + <br>1. Install the latest version of + <br>၁။ နောက်ဆုံးထွက်ဗားရှင်းကို ထည့်သွင်းမည် + + + + + <br>2. Install the latest version of + <br>၂။ နောက်ဆုံးထွက်ဗားရှင်းကို ထည့်သွင်းမည် + + + + Detailed instructions + အသေးစိတ်ညွှန်ကြားချက်များ + + + + Remove SFTP and all data stored there + SFTP နှင့် ထိုနေရာတွင် သိမ်းဆည်းထားသည့် ဒေတာအားလုံးကို ဖယ်ရှားမည် + + + + Remove SFTP and all data stored there? + SFTP နှင့် ထိုနေရာတွင် သိမ်းဆည်းထားသည့် ဒေတာအားလုံးကို ဖယ်ရှားမည်လား? + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + Cancel + ပယ်ဖျက်မည် + + + + PageServiceTorWebsiteSettings + + + Settings updated successfully + ဆက်တင်များကို အောင်မြင်စွာ အပ်ဒိတ်လုပ်ပြီးပါပြီ။ + + + + Tor website settings + Tor ဝဘ်ဆိုက်ဆက်တင်များ + + + + Website address + ဝဘ်ဆိုဒ်လိပ်စာ + + + + Copied + ကူးယူပြီးပါပြီ + + + + Use <a href="https://www.torproject.org/download/" style="color: #FBB26A;">Tor Browser</a> to open this URL. + ဤ URL ကိုဖွင့်ရန် <a href="https://www.torproject.org/download/" style="color: #FBB26A;">Tor Browser</a> ကို အသုံးပြုပါ. + + + + After creating your onion site, it takes a few minutes for the Tor network to make it available for use. + သင်၏ onion ဆိုက်ကို ဖန်တီးပြီးနောက်၊ Tor ကွန်ရက်က ၄င်းကိုအသုံးပြုနိုင်အောင်ပြုလုပ်‌ပေးရန် မိနစ်အနည်းငယ်အချိန်ယူသည်. + + + + When configuring WordPress set the this onion address as domain. + WordPress ကို ချိန်ညှိသည့်အခါ ဤ onion လိပ်စာကို domain အဖြစ် သတ်မှတ်ပါ. + + + + Remove website + ဝဘ်ဆိုက်ကိုဖယ်ရှားမည် + + + + The site with all data will be removed from the tor network. + ဒေတာအားလုံးပါသည့် ဆိုက်ကို tor ကွန်ရက်မှ ဖယ်ရှားပါမည်. + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + Cancel + ပယ်ဖျက်မည် + + + + PageSettings + + + Settings + ဆက်တင်များ + + + + Servers + ဆာဗာများ + + + + Connection + ချိတ်ဆက်မှု + + + + Application + အပလီကေးရှင်း + + + + Backup + backup ယူမည် + + + + About AmneziaVPN + AmneziaVPN အကြောင်း + + + + Close application + အပလီကေးရှင်းကို ပိတ်မည် + + + + PageSettingsAbout + + + Support Amnezia + Amnezia ကိုကူညီပံ့ပိုးမည် + + + + This is a free and open source application. If you like it, support the developers with a donation. + ဤအပလီကေးရှင်းသည် အခမဲ့နှင့် open source ဖြစ်သော အက်ပ်တစ်ခုဖြစ်ပါသည်။ ကြိုက်နှစ်သက်ပါက developer များအား လှူဒါန်းခြင်းဖြင့် ကူညီပံ့ပိုးပါ. + + + + And if you don’t like the application, all the more reason to support it - the donation will be used for the improving the application. + အကယ်၍ ဤအပလီကေးရှင်းကို မကြိုက်နှစ်သက်ပါက ပို၍ပင်ကူညီပံ့ပိုးပေးသင့်ပါသည် - အလှူငွေကို အက်ပ်လီကေးရှင်း ပိုမိုကောင်းမွန်စေရန်အတွက် အသုံးပြုသွား အကယ်၍ ဤအပလီကေးရှင်းကို မကြိုက်နှစ်သက်ပါက ပို၍ပင်ကူညီပံ့ပိုးပေးသင့်ပါသည်. အလှူငွေကို အက်ပ်လီကေးရှင်း ပိုမိုကောင်းမွန်စေရန်အတွက် အသုံးပြုသွားပါမည်. + + + + Card on Patreon + Patreon ပေါ်တွင်ရှိသောကတ် + + + + https://www.patreon.com/amneziavpn + https://www.patreon.com/amneziavpn + + + + Show other methods on Github + Github မှ အခြားနည်းလမ်းများကို ပြသပါ။ + + + + https://github.com/amnezia-vpn/amnezia-client#donate + https://github.com/amnezia-vpn/amnezia-client#donate + + + + Contacts + ဆက်သွယ်ရန်လိပ်စာများ + + + + Telegram group + Telegram ဂရု + + + + To discuss features + feature များကိုဆွေးနွေးရန် + + + + https://t.me/amnezia_vpn_en + https://t.me/amnezia_vpn + + + + Mail + မေးလ် + + + + For reviews and bug reports + သုံးသပ်ချက်များနှင့် ချွတ်ယွင်းချက်အစီရင်ခံစာများအတွက် + + + + Github + Github + + + + https://github.com/amnezia-vpn/amnezia-client + https://github.com/amnezia-vpn/amnezia-client + + + + Website + ဝဘ်ဆိုက် + + + + https://amnezia.org + https://amnezia.org + + + + Software version: %1 + ဆော့ဖ်ဝဲဗားရှင်း: %1 + + + + Check for updates + အပ်ဒိတ်များရှိမရှိ စစ်ဆေးမည် + + + + PageSettingsApplication + + + Application + အပလီကေးရှင်း + + + + Allow application screenshots + အပလီကေးရှင်းကို screenshot ရိုက်ရန်ခွင့်ပြုမည် + + + + Auto start + အလိုအ‌လျှောက်စတင်မည် + + + + Launch the application every time the device is starts + စက်စတင်ချိန်တိုင်း အပလီကေးရှင်းကို စတင်မည် + + + + Auto connect + အလိုအ‌လျှောက်ချိတ်ဆက်မည် + + + + Connect to VPN on app start + အက်ပ်စတင်ချိန်တွင် VPN သို့ ချိတ်ဆက်မည် + + + + Start minimized + အက်ပ်စတင်သည့်အခါ minimized ထားပြီးစတင်မည် + + + + Launch application minimized + အက်ပ်စတင်သည့်အခါ minimized ထားပြီးစတင်မည် + + + + Language + ဘာသာစကား + + + + Logging + လော့ဂ်အင် + + + + Enabled + ဖွင့်ထားပါသည် + + + + Disabled + ပိတ်ထားပါသည် + + + + Reset settings and remove all data from the application + ဆက်တင်များကို ပြန်လည်သတ်မှတ်ပြီး အပလီကေးရှင်းမှ ဒေတာအားလုံးကို ဖယ်ရှားမည် + + + + Reset settings and remove all data from the application? + ဆက်တင်များကို ပြန်လည်သတ်မှတ်ပြီး အပလီကေးရှင်းမှ ဒေတာအားလုံးကို ဖယ်ရှားမည်လား? + + + + All settings will be reset to default. All installed AmneziaVPN services will still remain on the server. + ဆက်တင်အားလုံးကို မူရင်းအတိုင်း ပြန်လည်သတ်မှတ်ပါမည်. ထည့်သွင်းထားသော AmneziaVPN ဝန်ဆောင်မှုများအားလုံးသည် ဆာဗာပေါ်တွင် ဆက်လက်ရှိနေမည်ဖြစ်သည်. + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + Cancel + ပယ်ဖျက်မည် + + + + PageSettingsBackup + + + Backup + အရန်သိမ်းဆည်းမည် + + + + Settings restored from backup file + ဆက်တင်များကို အရန်ဖိုင်မှ ပြန်လည်ရယူပြီးပါပြီ + + + + Configuration backup + လုပ်ဆောင်ချိန်ညှိချက်များကို အရန်ဖိုင်အဖြစ်သိမ်းဆည်းမည် + + + + You can save your settings to a backup file to restore them the next time you install the application. + သင်၏ဆက်တင်များကို အရန်ဖိုင်တွင် သိမ်းဆည်းထားခြင်းဖြင့် အပလီကေးရှင်းကို နောက်တစ်ကြိမ်ထည့်သွင်းသည့်အခါ ၎င်းဆက်တင်များကို ပြန်လည်ရယူနိုင်သည်. + + + + Make a backup + အရန်ဖိုင်တစ်ခု ပြုလုပ်မည် + + + + Save backup file + အရန်ဖိုင်ကို သိမ်းဆည်းမည် + + + + + Backup files (*.backup) + ဖိုင်များကိုအရန်သိမ်းဆည်းမည် (*.backup) + + + + Backup file saved + ဖိုင်များကိုအရန်သိမ်းဆည်းပြီးပါပြီ + + + + Restore from backup + အရန်သိမ်းထားသည့်ဖိုင်မှ ပြန်လည်ရယူမည် + + + + Open backup file + အရန်သိမ်းထားသည့်ဖိုင်ကို ဖွင့်မည် + + + + Import settings from a backup file? + ဆက်တင်များကို အရန်ဖိုင်တစ်ခုမှ ပြန်လည်တင်သွင်းမည်လား? + + + + All current settings will be reset + လက်ရှိဆက်တင်များအားလုံးကို ပြန်လည်သတ်မှတ်ပါမည် + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + Cancel + ပယ်ဖျက်မည် + + + + PageSettingsConnection + + + Connection + ချိတ်ဆက်မှု + + + + Use AmneziaDNS + AmneziaDNS ကို အသုံးပြုမည် + + + + If AmneziaDNS is installed on the server + အကယ်၍ AmneziaDNS ကို ဆာဗာတွင် ထည့်သွင်းထားလျှင် + + + + DNS servers + DNS ဆာဗာများ + + + + When AmneziaDNS is not used or installed + AmneziaDNS ကို အသုံးမပြု သို့မဟုတ် ထည့်သွင်းခြင်းမပြုသည့်အခါ + + + + Allows you to use the VPN only for certain Apps + အချို့သောအက်ပ်များအတွက်သာ VPN ကို အသုံးပြုခွင့်ပေးသည် + + + + Site-based split tunneling + ဝက်ဆိုဒ်အခြေပြု split tunneling + + + + Allows you to select which sites you want to access through the VPN + VPN မှတဆင့် သင်ဝင်ရောက်လိုသည့်ဆိုဒ်များကို ရွေးချယ်စေနိုင်သည် + + + + App-based split tunneling + App အခြေပြု split tunneling + + + + PageSettingsDns + + + Default server does not support custom dns + မူရင်းဆာဗာသည် စိတ်ကြိုက် dns ကို အထောက်အပံ့မပေးပါ + + + + DNS servers + DNS ဆာဗာများ + + + + If AmneziaDNS is not used or installed + AmneziaDNS ကို အသုံးမပြု သို့မဟုတ် ထည့်သွင်းခြင်းမပြုသည့်အခါ + + + + Primary DNS + Primary DNS + + + + Secondary DNS + Secondary DNS + + + + Restore default + မူရင်းအတိုင်းပြန်လည်ထားရှိမည် + + + + Restore default DNS settings? + မူရင်း DNS ဆက်တင်များကို ပြန်လည်ရယူလိုပါသလား? + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + Cancel + ပယ်ဖျက်မည် + + + + Settings have been reset + ဆက်တင်များကို ပြန်လည်သတ်မှတ်ပြီးပါပြီ + + + + Save + သိမ်းဆည်းမည် + + + + Settings saved + ဆက်တင်များကို သိမ်းဆည်းပြီးပြီ + + + + PageSettingsLogging + + + Logging + လော့ဂ်အင် + + + + Save logs + မှတ်တမ်းများကိုသိမ်းဆည်းမည် + + + + Open folder with logs + မှတ်တမ်းများဖြင့် ဖိုင်တွဲကိုဖွင့်မည် + + + + Save + သိမ်းဆည်းမည် + + + + Logs files (*.log) + မှတ်တမ်းဖိုင်များ (*.log) + မှတ်တမ်းဖိုင်များ (*.log) + + + + Logs file saved + မှတ်တမ်းဖိုင်များသိမ်းဆည်းပြီးပါပြီ + + + + Save logs to file + မှတ်တမ်းများကို ဖိုင်တွင်သိမ်းဆည်းမည် + + + + Clear logs? + မှတ်တမ်းများရှင်းလင်းမည်လား? + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + Cancel + ပယ်ဖျက်မည် + + + + Logs have been cleaned up + မှတ်တမ်းများကို ရှင်းလင်းပြီးပါပြီ + + + + Clear logs + မှတ်တမ်းများရှင်းလင်းမည် + + + + PageSettingsServerData + + + All installed containers have been added to the application + ထည့်သွင်းထားသည့် ကွန်တိန်နာအားလုံးကို အပလီကေးရှင်းသို့ ပေါင်းထည့်လိုက်ပြီ + + + + Clear Amnezia cache + Amnezia ကက်ရှ်ဖိုင်များကို ရှင်းလင်းမည် + + + + May be needed when changing other settings + အခြားဆက်တင်များကို ပြောင်းလဲသည့်အခါ လိုအပ်နိုင်သည် + + + + Clear cached profiles? + ကက်ရှ်ပရိုဖိုင်များကို ရှင်းမည်လား? + + + + No new installed containers found + အသစ်ထည့်သွင်းထားသော ကွန်တိန်နာများ မတွေ့ရှိပါ + + + + + + + + + + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + + + + + Cancel + ပယ်ဖျက်မည် + + + + Check the server for previously installed Amnezia services + ယခင်က ထည့်သွင်းထားသော Amnezia ဝန်ဆောင်မှုများရှိမရှိ ဆာဗာကို စစ်ဆေးမည် + + + + Add them to the application if they were not displayed + ဖော်ဆောင်ပြသခြင်းမရှိပါက ၎င်းတို့ကို အပလီကေးရှင်းထဲသို့ ထည့်မည် + + + + Reboot server + ဆာဗာကို ပြန်လည်စတင်မည် + + + + Do you want to reboot the server? + ဆာဗာကို ပြန်လည်စတင်ချင်ပါသလား? + + + + The reboot process may take approximately 30 seconds. Are you sure you wish to proceed? + ပြန်လည်စတင်သည့် လုပ်ငန်းစဉ်သည် စက္ကန့် 30 ခန့် ကြာနိုင်သည်. ဆက်လက်လုပ်ဆောင်လိုပါသလား? + + + + Do you want to remove the server from application? + ဆာဗာကို အပလီကေးရှင်းမှဖယ်ရှားချင်ပါသလား? + + + + Do you want to clear server from Amnezia software? + ဆာဗာကို Amnezia ဆော့ဖ်ဝဲလ်မှ ရှင်းလင်းလိုပါသလား? + + + + Reset API config + API config ကို ပြန်လည်သတ်မှတ်မည် + + + + Do you want to reset API config? + API config ကို ပြန်လည်သတ်မှတ်ချင်ပါသလား? + + + + Remove server from application + ဆာဗာကို အပလီကေးရှင်းမှဖယ်ရှားမည် + + + + All installed AmneziaVPN services will still remain on the server. + ထည့်သွင်းထားသော AmneziaVPN ဝန်ဆောင်မှုများအားလုံးသည် ဆာဗာပေါ်တွင် ဆက်လက်ရှိနေမည်ဖြစ်သည်. + + + + Clear server from Amnezia software + ဆာဗာကို Amnezia ဆော့ဖ်ဝဲလ်မှ ရှင်းလင်းမည် + + + + All containers will be deleted on the server. This means that configuration files, keys and certificates will be deleted. + ဆာဗာပေါ်တွင် ကွန်တိန်နာများအားလုံးကို ဖျက်လိုက်ပါမည်. ဆိုလိုသည်မှာ configuration ဖိုင်များ၊ key များနှင့် လက်မှတ်များ ပျက်သွားမည်ဖြစ်သည်. + + + + PageSettingsServerInfo + + + Server name + ဆာဗာအမည် + + + + Save + သိမ်းဆည်းမည် + + + + Protocols + ပရိုတိုကောများ + + + + Services + ဝန်ဆောင်မှုများ + + + + Data + ဒေတာ + + + + PageSettingsServerProtocol + + + settings + ဆက်တင်များ + + + + Remove + ဖယ်ရှားမည် + + + + Remove %1 from server? + %1 ကို ဆာဗာမှ ဖယ်ရှားမည်လား? + + + + All users with whom you shared a connection will no longer be able to connect to it. + သင်နှင့်အတူချိတ်ဆက်မှုတစ်ခုကို မျှဝေထားသည့် အသုံးပြုသူအားလုံး ဤချိတ်ဆက်မှုကိုချိတ်ဆက်နိုင်တော့မည်မဟုတ်ပါ. + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + Cancel + ပယ်ဖျက်မည် + + + + PageSettingsServersList + + + Servers + ဆာဗာများ + + + + PageSettingsSplitTunneling + + + Default server does not support split tunneling function + မူရင်းဆာဗာသည် split tunneling လုပ်ဆောင်ချက်ကို အထောက်အပံ့မပေးပါ + + + + Addresses from the list should not be accessed via VPN + စာရင်းတွင်ဖော်ပြထားသောလိပ်စာများကို VPN ဖြင့် ဝင်ရောက်ခြင်းပြုနိုင်လိမ့်မည် မဟုတ်ပေ + + + + Split tunneling + Split tunneling + + + + Mode + Mode + + + + Remove + ဖယ်ရှားမည် + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + Cancel + ပယ်ဖျက်မည် + + + + Import / Export Sites + ဆိုက်များ သွင်း/ထုတ်မည် + + + + Only the sites listed here will be accessed through the VPN + ဤနေရာတွင်ဖော်ပြထားသောဆိုက်များကိုသာ VPN မှတဆင့်ဝင်ရောက်ပါမည် + + + + Cannot change split tunneling settings during active connection + လက်ရှိချိတ်ဆက်မှုတစ်ခုရှိနေချိန်တွင် split tunneling ဆက်တင်များကို ပြောင်းလဲ၍မရပါ + + + + website or IP + ဝဘ်ဆိုက် သို့မဟုတ် IP + + + + Import + တင်သွင်းမည် + + + + Save site list + ဆိုက်စာရင်းကို သိမ်းဆည်းမည် + + + + Save sites + ဆိုက်များသိမ်းဆည်းမည် + + + + + + Sites files (*.json) + ဆိုက်ဖိုင်များ (*.json) + + + + Import a list of sites + ဆိုက်စာရင်းတစ်ခု တင်သွင်းမည် + + + + Replace site list + ဆိုက်စာရင်းကို အစားထိုးမည် + + + + + Open sites file + ဆိုက်ဖိုင်များ ဖွင့်မည် + + + + Add imported sites to existing ones + တင်သွင်းထားသော ဆိုက်များကို ရှိပြီးသားဆိုက်များထဲသို့ ထည့်မည် + + + + PageSetupWizardConfigSource + + + Server connection + ဆာဗာချိတ်ဆက်မှု + + + + Do not use connection code from public sources. It may have been created to intercept your data. + +It's okay as long as it's from someone you trust. + အများသူငှာအသုံးပြုသည့် ရင်းမြစ်များမှ ချိတ်ဆက်ကုဒ်ကို မသုံးပါနှင့်.အဆိုပါကုဒ်များသည် သင့်ဒေတာကို ကြားဖြတ်ရယူရန် ဖန်တီးထားခြင်းဖြစ်နိုင်သည်. + +သင်ယုံကြည်ရတဲ့သူတစ်ယောက်ဆီမှ ရရှိတဲ့ကုဒ်ဖြစ်နေသရွေ့တော့ အဆင်ပြေပါသည်. + + + + What do you have? + သင့်တွင်ဘာရှိပါသလဲ? + + + + File with connection settings + ချိတ်ဆက်မှုဆက်တင်များပါဝင်သောဖိုင် + + + + File with connection settings or backup + ချိတ်ဆက်မှုဆက်တင်များ သို့မဟုတ် အရန်သိမ်းဆည်းထားမှုပါဝင်သောဖိုင် + + + + Open config file + config ဖိုင်ကိုဖွင့်မည် + + + + QR-code + QR-ကုဒ် + + + + Key as text + Key ကိုစာသားအဖြစ် + + + + PageSetupWizardCredentials + + + Server IP address [:port] + ဆာဗာ IP လိပ်စာ [:port] + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + Enter the address in the format 255.255.255.255:88 + လိပ်စာကို 255.255.255.255:88 ဖော်မတ်ဖြင့် ထည့်ပါ + + + + Login to connect via SSH + SSH မှတစ်ဆင့် ချိတ်ဆက်ရန် အကောင့်ဝင်ပါ + + + + Configure your server + သင်၏ဆာဗာကို စီစဉ်ချိန်ညှိပါ။ + + + + 255.255.255.255:22 + 255.255.255.255:22 + + + + Password or SSH private key + စကားဝှက် သိုမဟုတ် SSH private key + + + + All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties + သင်ထည့်သွင်းသည့်ဒေတာအားလုံးကို တင်းကြပ်လုံခြုံစွာလျှို့ဝှက်ထားမည်ဖြစ်ပြီး Amnezia သို့မဟုတ် မည်သည့်ပြင်ပအဖွဲ့အစည်းကိုမျှ မျှဝေမည် သို့မဟုတ် ထုတ်ဖော်မည်မဟုတ်ပါ + + + + Ip address cannot be empty + IP လိပ်စာသည် ဗလာမဖြစ်ရပါ + + + + Login cannot be empty + လော့ဂ်အင်အချက်အလက်သည် ဗလာမဖြစ်ရပါ + + + + Password/private key cannot be empty + စကားဝှက်/private key သည် ဗလာမဖြစ်ရပါ + + + + PageSetupWizardEasy + + + What is the level of internet control in your region? + သင့်ဒေသရှိ အင်တာနက်ထိန်းချုပ်မှုအဆင့်က ဘယ်လောက်ရှိပါသလဲ? + + + + Set up a VPN yourself + VPN ကို ကိုယ်တိုင်သတ်မှတ်မည် + + + + I want to choose a VPN protocol + VPN ပရိုတိုကောကို ရွေးချင်ပါသည် + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + Set up later + နောက်မှ သတ်မှတ်မည် + + + + PageSetupWizardInstalling + + + The server has already been added to the application + ဆာဗာကို အပလီကေးရှင်းတွင် ထည့်သွင်းပြီးပါပြီ + + + + Amnezia has detected that your server is currently + Amnezia သည် သင့်ဆာဗာက + + + + busy installing other software. Amnezia installation + အခြားဆော့ဖ်ဝဲကို ထည့်သွင်းနေသောကြောင့် အလုပ်ရှုပ်နေကြောင်းထောက်လှန်းမိပါသည်. Amnezia ထည့်သွင်းခြင်းလုပ်ငန်းစဥ် + + + + will pause until the server finishes installing other software + ဆာဗာကို အခြားဆော့ဖ်ဝဲကို ထည့်သွင်းခြင်း မပြီးမချင်း ခေတ္တရပ်ထားပါမည် + + + + Installing + ထည့်သွင်းနေသည် + + + + Cancel installation + ထည့်သွင်းမှုကို ပယ်ဖျက်မည် + + + + + Usually it takes no more than 5 minutes + များသောအားဖြင့် 5 မိနစ်ထက်မပိုပါ + + + + PageSetupWizardProtocolSettings + + + Installing %1 + ထည့်သွင်းနေသည် %1 + + + + More detailed + ပိုမိုအသေးစိတ် + + + + Close + ပိတ်မည် + + + + Network protocol + ကွန်ရက်ပရိုတိုကော + + + + Port + Port + + + + Install + ထည်သွင်းမည် + + + + PageSetupWizardProtocols + + + VPN protocol + VPN ပရိုတိုကော + + + + Choose the one with the highest priority for you. Later, you can install other protocols and additional services, such as DNS proxy and SFTP. + သင့်အတွက် ဦးစားပေးအဖြစ်ဆုံးကို ရွေးချယ်ပါ. နောက်ပိုင်းတွင်၊ သင်သည် DNS proxy နှင့် SFTP ကဲ့သို့သော အခြားပရိုတိုကောများနှင့် ထပ်ဆောင်းဝန်ဆောင်မှုများကို ထည့်သွင်းနိုင်သည်. + + + + PageSetupWizardQrReader + + + Point the camera at the QR code and hold for a couple of seconds. + ကင်မရာနှင့် QR ကုဒ်ကို ချိန်ပြီး စက္ကန့်အနည်းငယ်လောက် ငြိမ်ထားပေးပါ. + + + + PageSetupWizardStart + + + Settings restored from backup file + ဆက်တင်များကို အရန်သိမ်းဆည်းထားသောဖိုင်မှ ပြန်လည်ရယူပြီးပါပြီ + + + + Free service for creating a personal VPN on your server. + သင့်ဆာဗာပေါ်တွင် ကိုယ်ပိုင် VPN ဖန်တီးရန်အတွက် အခမဲ့ဝန်ဆောင်မှု. + + + + Helps you access blocked content without revealing your privacy, even to VPN providers. + အခြား VPN ဝန်ဆောင်မှုများကိုပင် သင်၏ privacy ကိုမဖော်ပြဘဲ ပိတ်ဆို့ထားသော အကြောင်းအရာများကို သင်ဝင်ရောက်ကြည့်ရှုနိုင်ရန် အကူအညီပေးပါသည်. + + + + I have the data to connect + ကျွန်ုပ်တွင်ချိတ်ဆက်ဖို့အတွက်ဒေတာရှိသည် + + + + I have nothing + ကျွန်ုပ်တွင်ဘာမှမရှိပါ + + + + https://amnezia.org/instructions/0_starter-guide + https://amnezia.org/instructions/0_starter-guide + + + + PageSetupWizardTextKey + + + Connection key + ချိန်ဆက်မှု key + + + + A line that starts with vpn://... + vpn://... ဖြင့် စတင်သော စာကြောင်း... + + + + Key + Key + + + + Insert + ထည်သွင်းမည် + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + PageSetupWizardViewConfig + + + New connection + ချိတ်ဆက်မှုအသစ် + + + + Do not use connection code from public sources. It could be created to intercept your data. + အများသူငှာအသုံးပြုသည့် ရင်းမြစ်များမှ ချိတ်ဆက်ကုဒ်ကို မသုံးပါနှင့်.အဆိုပါကုဒ်များသည် သင့်ဒေတာကို ကြားဖြတ်ရယူရန် ဖန်တီးထားခြင်းဖြစ်နိုင်သည်. + + + + Collapse content + အကြောင်းအရာများကိုဖြန့်ချမည် + + + + Show content + အကြောင်းအရာများကိုပြမည် + + + + Connect + ချိတ်ဆက်မည် + + + + PageShare + + + OpenVpn native format + OpenVpn မူရင်းဖောမတ် + + + + WireGuard native format + WireGuard မူရင်းဖော်မတ် + + + + Connection + ချိတ်ဆက်မှု + + + + + Server + ဆာဗာ + + + + Config revoked + Config ကိုပြန်ရုပ်သိမ်းလိုက်ပါပြီ + + + + Connection to + ဤဆာဗာသို့ချိတ်ဆက်မှု + + + + File with connection settings to + ဤဆာဗာနှင့်ချိတ်ဆက်မှု ဆက်တင်များပါရှိသော ဖိုင် + + + + Save OpenVPN config + OpenVPN config ကိုသိမ်းဆည်းမည် + + + + Save WireGuard config + WireGuard config ကိုသိမ်းဆည်းမည် + + + + Save AmneziaWG config + AmneziaWG config ကိုသိမ်းဆည်းမည် + + + + Save ShadowSocks config + ShadowSocks config ကိုသိမ်းဆည်းမည် + + + + Save Cloak config + Cloak config ကိုသိမ်းဆည်းမည် + + + + For the AmneziaVPN app + AmneziaVPN အက်ပ်အတွက် + + + + AmneziaWG native format + AmneziaWG မူရင်းဖော်မတ် + + + + ShadowSocks native format + ShadowSocks မူရင်းဖောမတ် + + + + Cloak native format + Cloak မူရင်းဖော်မတ် + + + + Share VPN Access + VPN အသုံးပြုခွင့်ကိုမျှဝေမည် + + + + Share full access to the server and VPN + ဆာဗာနှင့် VPN သို့ အပြည့်အဝဝင်ရောက်ခွင့်ကို မျှဝေမည် + + + + Use for your own devices, or share with those you trust to manage the server. + သင့်ကိုယ်ပိုင်စက်ပစ္စည်းများအတွက် အသုံးပြုရန် သို့မဟုတ် ဆာဗာကို စီမံခန့်ခွဲရန် သင်ယုံကြည်ရသူများနှင့် မျှဝေရန်. + + + + + Users + အသုံးပြုသူများ + + + + User name + အသုံးပြုသူနာမည် + + + + Search + ရှာဖွေမည် + + + + Creation date: + ဖန်တီးပြုလုပ်သည့်ရက်စွဲ: + + + + 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 + ပယ်ဖျက်မည် + + + + Share VPN access without the ability to manage the server + ဆာဗာကို စီမံခန့်ခွဲနိုင်စွမ်းမပါရှိဘဲ VPN အသုံးပြုခွင့်ကို မျှဝေမည် + + + + + Protocol + ပရိုတိုကော + + + + + Connection format + ချိတ်ဆက်မှုဖောမတ် + + + + + Share + မျှဝေမည် + + + + PageShareFullAccess + + + Full access to the server and VPN + ဆာဗာနှင့် VPN ကို အပြည့်အဝဝင်ရောက်ခွင့် + + + + We recommend that you use full access to the server only for your own additional devices. + + သင့်ကိုယ်ပိုင်အပိုပစ္စည်းများအတွက်သာ ဆာဗာသို့ အပြည့်အဝဝင်ရောက်ခွင့်ကို အသုံးပြုရန် ကျွန်ုပ်တို့ အကြံပြုပါသည်. + + + + + If you share full access with other people, they can remove and add protocols and services to the server, which will cause the VPN to work incorrectly for all users. + သင်သည် အခြားသူများနှင့် အပြည့်အဝဝင်ရောက်ခွင့်ကို မျှဝေပါက၊ ၎င်းတို့သည် ပရိုတိုကောများနှင့် ဝန်ဆောင်မှုများကို ဆာဗာသို့ထည့်သွင်းခြင်း ဆာဗာမှဖယ်ရှားခြင်းများ ပြုလုပ်နိုင်သောကြောင့် အသုံးပြုသူများအားလုံးအတွက် VPN မှားယွင်းစွာ လုပ်ဆောင်ခြင်းများဖြစ်စေနိုင်ပါသည်. + + + + + Server + ဆာဗာ + + + + Accessing + ဝင်ရောက်နေသည် + + + + File with accessing settings to + ဤဆာဗာနှင့်ဝင်ရောက်နိုင်မှု ဆက်တင်များပါရှိသော ဖိုင် + + + + Share + မျှဝေမည် + + + + Connection to + ဤဆာဗာသို့ချိတ်ဆက်မှု + + + + File with connection settings to + ဤဆာဗာနှင့်ချိတ်ဆက်မှု ဆက်တင်များပါရှိသော ဖိုင် + + + + PopupType + + + Close + ပိတ်မည် + + + + QKeychain::DeletePasswordJobPrivate + + + Password entry not found + စကားဝှက် ထည့်သွင်းခြင်း မတွေ့ပါ + + + + Could not decrypt data + ဒေတာကို ကုတ်ဝှက်ဖြည်၍မရပါ + + + + + Unknown error + အမည်မသိ မှားယွင်းမှု + + + + Could not open wallet: %1; %2 + ပိုက်ဆံအိတ်ကို ဖွင့်၍မရပါ: %1; %2 + + + + Password not found + စကားဝှက်ကို ရှာမတွေ့ပါ + + + + Could not open keystore + keystore ကို ဖွင့်၍မရပါ + + + + Could not remove private key from keystore + Key store မှ ကိုယ်ပိုင် key ကို ဖယ်ရှား၍မရပါ + + + + QKeychain::JobPrivate + + + Unknown error + အမည်မသိ မှားယွင်းမှု + + + + Access to keychain denied + Keychain အသုံးပြုခွင့် ငြင်းပယ်ခံလိုက်ရသည် + + + + QKeychain::PlainTextStore + + + Could not store data in settings: access error + ဆက်တင်များတွင် ဒေတာကို သိမ်းဆည်း၍မရပါ: ဝင်ရောက်ခွင့်မှားယွင်းမှု + + + + Could not store data in settings: format error + ဆက်တင်များတွင် ဒေတာကို သိမ်းဆည်း၍မရပါ: ဖော်မတ်မှားယွင်းမှု + + + + Could not delete data from settings: access error + ဆက်တင်များမှ ဒေတာကို ဖျက်သိမ်း၍မရပါ: ဝင်ရောက်ခွင့်မှားယွင်းမှု + + + + Could not delete data from settings: format error + ဆက်တင်များမှ ဒေတာကို ဖျက်သိမ်း၍မရပါ: ဖော်မတ်မှားယွင်းမှု + + + + Entry not found + ဝင်ခွင့်ရှာမတွေ့ပါ + + + + QKeychain::ReadPasswordJobPrivate + + + Password entry not found + စကားဝှက် ထည့်သွင်းခြင်း မတွေ့ပါ + + + + + Could not decrypt data + ဒေတာကို ကုတ်ဝှက်ဖြည်၍မရပါ + + + + D-Bus is not running + D-Bus လုပ်ဆောင်နေခြင်းမရှိပါ + + + + + Unknown error + အမည်မသိ မှားယွင်းမှု + + + + No keychain service available + Keychain ဝန်ဆောင်မှု မရရှိနိုင်ပါ + + + + Could not open wallet: %1; %2 + ပိုက်ဆံအိတ်ကို ဖွင့်၍မရပါ: %1; %2 + + + + Access to keychain denied + Keychain အသုံးပြုခွင့် ငြင်းပယ်ခံလိုက်ရသည် + + + + Could not determine data type: %1; %2 + ဒေတာအမျိုးအစားကို သတ်မှတ်၍မရပါ: %1; %2 + + + + + Entry not found + ဝင်ခွင့်ရှာမတွေ့ပါ + + + + Unsupported entry type 'Map' + Unsupported entry type 'Map' + + + + Unknown kwallet entry type '%1' + Unknown kwallet entry type '%1' + + + + Password not found + စကားဝှက်ကို ရှာမတွေ့ပါ + + + + Could not open keystore + keystore ကို ဖွင့်၍မရပါ + + + + Could not retrieve private key from keystore + Key store မှ ကိုယ်ပိုင် key ကို ထုတ်ယူ၍မရပါ + + + + Could not create decryption cipher + ကုတ်ဝှက်ဖြည်ခြင်းဖန်တီး၍မရပါ + + + + QKeychain::WritePasswordJobPrivate + + + Credential size exceeds maximum size of %1 + အထောက်အထားအရွယ်အစားသည် အများဆုံးအရွယ်အစား %1 ထက် ကျော်လွန်နေပါသည် + + + + Credential key exceeds maximum size of %1 + အထောက်အထား key သည် အများဆုံးအရွယ်အစား %1 ထက် ကျော်လွန်နေပါသည် + + + + Writing credentials failed: Win32 error code %1 + အထောက်အထားများရေးသားခြင်း မအောင်မြင်ပါ: Win32 အမှားကုဒ် %1 + + + + Encryption failed + ကုတ်ဝှက်ခြင်းမအောင်မြင်ပါ + + + + D-Bus is not running + D-Bus လုပ်ဆောင်နေခြင်းမရှိပါ + + + + + Unknown error + အမည်မသိ မှားယွင်းမှု + + + + Could not open wallet: %1; %2 + ပိုက်ဆံအိတ်ကို ဖွင့်၍မရပါ: %1; %2 + + + + Password not found + စကားဝှက်ကို ရှာမတွေ့ပါ + + + + Could not open keystore + keystore ကို ဖွင့်၍မရပါ + + + + Could not create private key generator + ကိုယ်ပိုင် key ဖန်တီးမှုစက်ကိုမဖန်တီးနိုင်ပါ + + + + Could not generate new private key + ကိုယ်ပိုင် key အသစ် မထုတ်ပေးနိုင်ပါ + + + + Could not retrieve private key from keystore + Key store မှ ကိုယ်ပိုင် key ကို ထုတ်ယူ၍မရပါ + + + + Could not create encryption cipher + ကုတ်ဝှက်ဖြည်ခြင်းဖန်တီး၍မရပါ + + + + Could not encrypt data + ဒေတာကို ကုတ်ဝှက်၍မရပါ + + + + QObject + + + No error + မှားယွင်းမှုမရှိပါ + + + + Unknown Error + အမည်မသိ မှားယွင်းမှု + + + + Function not implemented + လုပ်ဆောင်ချက်ကို မတတ်ဆင်ရသေးပါ + + + + Server check failed + ဆာဗာစစ်ဆေးမှု မအောင်မြင်ပါ + + + + Server port already used. Check for another software + ဆာဗာ Port ကို အသုံးပြုပြီးဖြစ်သည်. အခြားဆော့ဖ်ဝဲရှိမရှိ စစ်ဆေးပါ + + + + Server error: Docker container missing + ဆာဗာ မှားယွင်းမှု: Docker ကွန်တိန်နာ ပျောက်နေသည် + + + + Server error: Docker failed + ဆာဗာ မှားယွင်းမှု: Docker မအောင်မြင်ပါ + + + + Installation canceled by user + ထည့်သွင်းမှုကို အသုံးပြုသူမှ ပယ်ဖျက်လိုက်သည် + + + + The user does not have permission to use sudo + ဤအသုံးပြုသူသည် sudo ကိုအသုံးပြုရန်ခွင့်ပြုချက်မရှိပါ + + + + Ssh request was denied + Ssh တောင်းဆိုမှု ငြင်းဆိုခံလိုက်ရပါသည် + + + + Ssh request was interrupted + Ssh တောင်းဆိုမှု အနှောက်အယက်ခံလိုက်ရပါသည် + + + + Ssh internal error + စက်တွင်းဖြစ်သော Ssh မှားယွင်းမှု + + + + Invalid private key or invalid passphrase entered + မမှန်ကန်သော ကိုယ်ပိုင် key သို့မဟုတ် မမှန်ကန်သော စကားဝှက်ကို ထည့်သွင်းထားသည် + + + + The selected private key format is not supported, use openssh ED25519 key types or PEM key types + ရွေးချယ်ထားသော ကိုယ်ပိုင် key ဖော်မတ်ကို ထောက်ပံ့မှုမပေးပါ၊ openssh ED25519 key အမျိုးအစားများ သို့မဟုတ် PEM သော့အမျိုးအစားများကို အသုံးပြုပါ + + + + Timeout connecting to server + ဆာဗာသို့ ချိတ်ဆက်ခြင်း အချိန်ကုန်သွားသည် + + + + Sftp error: End-of-file encountered + Sftp မှားယွင်းမှု: ဖိုင်အဆုံးသတ်ကို ကြုံတွေ့ခဲ့ရသည် + + + + Sftp error: File does not exist + Sftp မှားယွင်းမှု: ဖိုင်မရှိပါ + + + + Sftp error: Permission denied + Sftp မှားယွင်းမှု: ခွင့်ပြုချက် ငြင်းဆိုခံလိုက်ရပါသည် + + + + Sftp error: Generic failure + Sftp မှားယွင်းမှု: ယေဘုယ မအောင်မြင်ခြင်း + + + + Sftp error: Garbage received from server + မှားယွင်းမှု: ဆာဗာမှ အမှိုက်များကို လက်ခံရရှိခဲ့သည် + + + + Sftp error: No connection has been set up + Sftp မှားယွင်းမှု: ချိတ်ဆက်မှု မသတ်မှတ်ရသေးပါ + + + + Sftp error: There was a connection, but we lost it + Sftp မှားယွင်းမှု: ချိတ်ဆက်မှုတစ်ခုရှိခဲ့သော်လည်း ဆုံးရှုံးသွားခဲ့ပါသည် + + + + Sftp error: Operation not supported by libssh yet + Sftp အမှား: လုပ်ဆောင်ချက်ကို libssh မှ မထောက်ပံ့သေးပါ + + + + Sftp error: Invalid file handle + Sftp မှားယွင်းမှု: ဖိုင်ကိုင်တွယ်မှု မမှန်ကန်ပါ + + + + Sftp error: No such file or directory path exists + Sftp မှားယွင်းမှု: ဤဖိုင်အမျိုးအစား သို့မဟုတ် လမ်းညွှန်လမ်းကြောင်းမျိုး မရှိပါ + + + + Sftp error: An attempt to create an already existing file or directory has been made + Sftp မှားယွင်းမှု: ရှိပြီးသား ဖိုင် သို့မဟုတ် လမ်းညွှန်ကို ဖန်တီးရန် ကြိုးပမ်းမှုတစ်ခု ပြုလုပ်ပြီးဖြစ်သည် + + + + Sftp error: Write-protected filesystem + Sftp မှားယွင်းမှု: ရေးသားခြင်းမှကာကွယ်ထားသော ဖိုင်စနစ် + + + + Sftp error: No media was in remote drive + Sftp မှားယွင်းမှု: မီဒီယာသည် အဝေးမှ drive ထဲတွင် မရှိခဲ့ပါ + + + + The config does not contain any containers and credentials for connecting to the server + Config တွင် ဆာဗာသို့ချိတ်ဆက်ရန်အတွက် ကွန်တိန်နာများနှင့် အထောက်အထားများ မပါဝင်ပါ + + + + Error when retrieving configuration from API + API မှ စီစဉ်သတ်မှတ်မှုကို ရယူသည့်အခါ အမှားအယွင်းဖြစ်ပေါ်နေသည် + + + + This config has already been added to the application + ဤ config ကို အပလီကေးရှင်းထဲသို့ ထည့်သွင်းပြီးဖြစ်သည် + + + + ErrorCode: %1. + မှားယွင်းမှုကုတ်: %1. + + + + OpenVPN config missing + OpenVPN config ပျောက်ဆုံးနေပါသည် + + + + OpenVPN management server error + OpenVPN စီမံခန့်ခွဲမှုဆာဗာ အမှားအယွင်း + + + + OpenVPN executable missing + OpenVPN စီမံလုပ်ဆောင်နိုင်မှု ပျောက်ဆုံးနေပါသည် + + + + ShadowSocks (ss-local) executable missing + ShadowSocks (ss-local) executable ပျောက်နေပါသည် + + + + Cloak (ck-client) executable missing + Cloak (ck-client) စီမံလုပ်ဆောင်နိုင်မှု ပျောက်ဆုံးနေပါသည် + + + + Amnezia helper service error + Amnezia helper ဝန်ဆောင်မှု မှားယွင်းမှု + + + + OpenSSL failed + OpenSSL မအောင်မြင်ပါ + + + + Can't connect: another VPN connection is active + ချိတ်ဆက်၍မရပါ: အခြား VPN ချိတ်ဆက်မှုတစ်ခုရှိနေပါသည် + + + + Can't setup OpenVPN TAP network adapter + OpenVPN TAP ကွန်ရက် adapter ကို စနစ်တည်ဆောက်၍မရပါ + + + + VPN pool error: no available addresses + VPN pool မှားယွင်းမှု: ရရှိနိုင်သောလိပ်စာများမရှိပါ + + + + VPN connection error + VPN ချိတ်ဆက်မှုမှားယွင်းနေပါသည် + + + + Internal error + စက်တွင်းဖြစ်သော မှားယွင်းမှု + + + + IPsec + IPsec + + + + ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions. + ShadowSocks - ၎င်းသည် ပုံမှန်ဝဘ်လမ်းကြောင်းနှင့် ဆင်တူစေရန် VPN အသွားအလာကို ဖုံးကွယ်ထားသော်လည်း ၎င်းကို အချို့သော ဆင်ဆာဖြတ်ထားသော ဒေသများရှိ ခွဲခြမ်းစိတ်ဖြာမှုစနစ်များက ထောက်လှန်းသိရှိနိုင်ပါသည်. + + + + OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. Ideal for bypassing blocking in regions with the highest levels of censorship. + OpenVPN over Cloak - ဝဘ်အသွားအလာအဖြစ် ဟန်ဆောင်ထားသည့် VPN ပါသော OpenVPN နှင့် active-probing ထောက်လှမ်းခြင်းမှ ကာကွယ်ပေးခြင်း. ဆင်ဆာဖြတ်တောက်မှု အမြင့်ဆုံးအဆင့်ရှိသော ဒေသများတွင် ပိတ်ဆို့ခြင်းများကို ကျော်ဖြတ်ရန်အတွက် အကောင်းဆုံးဖြစ်သည်. + + + + Create a file vault on your server to securely store and transfer files. + ဖိုင်များကို လုံခြုံစွာသိမ်းဆည်းရန်နှင့် လွှဲပြောင်းရန်အတွက် သင့်ဆာဗာပေါ်တွင် fire vault တစ်ခု ဖန်တီးပါ. + + + + This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against blocking. + +OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. + +Cloak protects OpenVPN from detection and blocking. + +Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected + +Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. + +If there is a extreme level of Internet censorship in your region, we advise you to use only OpenVPN over Cloak from the first connection + +* Available in the AmneziaVPN across all platforms +* High power consumption on mobile devices +* Flexible settings +* Not recognised by DPI analysis systems +* Works over TCP network protocol, 443 port. + + ဤပစ္စည်းသည်ပိတ်ဆို့ခြင်းမှကာကွယ်ရန်အတွက် အထူးထုတ်လုပ်ထားသည့် OpenVPN ပရိုတိုကောနှင့် Cloak plugin ၏ပေါင်းစပ်မှုဖြစ်သည်. + +OpenVPN သည် ကလိုင်းယင့်နှင့် ဆာဗာကြားရှိ အင်တာနက်အသွားအလာအားလုံးကို ကုဒ်ဝှက်ခြင်းဖြင့် လုံခြုံသော VPN ချိတ်ဆက်မှုကို ပံ့ပိုးပေးပါသည် + +Cloak သည် OpenVPN ကိုရှာဖွေမတွေ့ရှိအောင်နှင့်ပိတ်ဆို့ခံရခြင်းမှကာကွယ်ပေးသည်. + +Cloak သည် ပက်ကတ်မက်တာဒေတာကို မွမ်းမံနိုင်သောကြောင့် VPN အသွားအလာကို ပုံမှန်ဝဘ်သွားလာမှုအဖြစ် လုံးလုံးလျားလျားဖုံးကွယ်ထားနိုင်ပြီး Active Probing မှ VPN ကို ထောက်လှမ်းသိရှိခြင်းမှလည်း ကာကွယ်ပေးပါသည်. Cloak သည် အလွန်ရှာဖွေရခက်အောင်လုပ်ဆောင်ပေးပါသည်. + +ပထမဆုံးဒေတာပက်ကေ့ချ်ကိုလက်ခံရရှိပြီးနောက်ချက်ချင်း၊ Cloak သည် အဝင်ချိတ်ဆက်မှုကို စစ်မှန်ကြောင်းသက်သေပြပေးသည်. စစ်မှန်ကြောင်းအထောက်အထား မတွေ့ရှိပါက၊ ပလပ်အင်သည် ဆာဗာကို ဝဘ်ဆိုဒ်အတုအဖြစ် ဖုံးကွယ်ထားပြီး သင်၏ VPN ကို ခွဲခြမ်းစိတ်ဖြာမှုစနစ်များတွင် မမြင်ရအောင်ကာကွယ်ပေးပါသည်. + +သင့်ဒေသတွင်အင်တာနက်ဆင်ဆာဖြတ်တောက်မှုအလွန်ခံစားနေရပါက၊ ပထမဆုံးချိတ်ဆက်မှုမှ Cloak တွင် OpenVPN ကိုသာအသုံးပြုရန်အကြံပြုအပ်ပါသည်. + +* ပလက်ဖောင်းအားလုံးတွင်ရှိသော AmneziaVPN တွင်ရရှိနိုင်ပါသည် +* မိုဘိုင်းစက်ပစ္စည်းများတွင် ပါဝါသုံးစွဲမှုမြင့်မားခြင်း +* ပြောင်းလွယ်ပြင်ဆင်ရန်လွယ်သောဆက်တင်များ +* DPI ခွဲခြမ်းစိတ်ဖြာမှုစနစ်များက ‌ထောက်လှန်းမသိရှိနိုင်ပါ +* TCP ကွန်ရက်ပရိုတိုကော၊ 443 port တွင် အလုပ်လုပ်ပါသည်. + + + + + A relatively new popular VPN protocol with a simplified architecture. +WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. +WireGuard is very susceptible to blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Easily recognised by DPI analysis systems, susceptible to blocking +* Works over UDP network protocol. + ရိုးရှင်းသော တည်ဆောက်ပုံဖြင့် အတော်လေးနာမည်ကြီးသော VPN ပရိုတိုကောအသစ်။ +WireGuard သည် ၎င်းအားအသုံးပြုထားသောစက်အားလုံးကို တည်ငြိမ်သော VPN ချိတ်ဆက်မှုနှင့် စွမ်းဆောင်ရည်မြင့်မားမှုကို ရရှိစေပါသည်။ Hard-coded encryption ဆက်တင်များကို အသုံးပြုထားပါသည်။ OpenVPN နှင့် နှိုင်းယှဉ်ပါက WireGuard သည် latency နည်းပါးပြီး ဒေတာလွှဲပြောင်းမှု ပိုမိုကောင်းမွန်ပါသည်။ +WireGuard သည် ၎င်း၏ ကွဲပြားသော packet လက်မှတ်များ ကြောင့် ပိတ်ဆို့ခြင်းကို အလွန်ခံရနိုင်ချေရှိသည်။ ရှုပ်ထွေးသောနည်းပညာများကို အသုံးပြုသည့် အခြားသော VPN ပရိုတိုကောများနှင့် မတူဘဲ၊ WireGuard ပက်ကတ်များ၏ တသမတ်တည်း လက်မှတ်ပုံစံများကြောင့် ၎င်းတို့ကိုပိုမိုလွယ်ကူစွာ ရှာဖွေဖော်ထုတ်နိုင်ကာ အဆင့်မြင့် Deep Packet Inspection (DPI) စနစ်များနှင့် အခြားသော ကွန်ရက်စောင့်ကြည့်ရေးကိရိယာများဖြင့် ပိတ်ဆို့ထားနိုင်သည်။ + +* ပလက်ဖောင်းအားလုံးရှိ AmneziaVPN တွင်ရနိုင်ပါသည်။ +* ပါဝါသုံးစွဲမှုနည်းပါးခြင်း။ +* ဆက်တင်အရေအတွက်နည်းပါခြင်း။ +* DPI ခွဲခြမ်းစိတ်ဖြာမှုစနစ်များက အလွယ်တကူ ထောက်လှန်းသိရှိခံရနိုင်ပြီး ပိတ်ဆို့ခြင်းခံရနိုင်သည်။ +* UDP ကွန်ရက်ပရိုတိုကောပေါ်တွင် အလုပ်လုပ်သည်။. + + + + IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol. +One of its distinguishing features is its ability to swiftly switch between networks and devices, making it particularly adaptive in dynamic network environments. +While it offers a blend of security, stability, and speed, it's essential to note that IKEv2 can be easily detected and is susceptible to blocking. + +* Available in the AmneziaVPN only on Windows +* Low power consumption, on mobile devices +* Minimal configuration +* Recognised by DPI analysis systems +* Works over UDP network protocol, ports 500 and 4500. + IPSec ကုဒ်ဝှက်ခြင်းအလွှာနှင့်တွဲဆက်ထားသည့် IKEv2 သည် ခေတ်မီပြီး တည်ငြိမ်သော VPN ပရိုတိုကောဖြစ်သည်။ +၎င်း၏ထူးခြားသောအင်္ဂါရပ်များထဲမှတစ်ခုမှာ ကွန်ရက်များနှင့် စက်ပစ္စည်းများကြား လျင်မြန်စွာပြောင်းလဲနိုင်သည့်စွမ်းရည်ဖြစ်ပြီး ဤစွမ်းရည်ကပင် dynamic ဖြစ်သောကွန်ရက်ပတ်ဝန်းကျင်များတွင် လိုက်လျောညီထွေဖြစ်စေရန်အကူအညီပေးပါသည်။ +IKEv2 သည် လုံခြုံရေး၊ တည်ငြိမ်မှု၊ နှင့် အမြန်နှုန်းတို့ ပေးစွမ်းနိုင်သော်လည်း၊ အလွယ်တကူ ထောက်လှန်းသိရှိခံရနိုင်ပြီး ပိတ်ဆို့ခြင်း ခံရနိုင်သည်ကို သတိပြုရန် အရေးကြီးပါသည်။ + +* Windows တွင်ရှိသော AmneziaVPN တွင်သာရနိုင်သည်။ +* မိုဘိုင်းစက်ပစ္စည်းများတွင် ပါဝါသုံးစွဲမှုနည်းပါးခြင်း။ +* တပ်ဆင်ချိန်ညှိရန်သိပ်မလိုအပ်ခြင်း။ +* DPI ခွဲခြမ်းစိတ်ဖြာမှုစနစ်များမှထောက်လှန်းနိုင်ခြင်း။ +* UDP ကွန်ရက်ပရိုတိုကော၊ port 500 နှင့် 4500 ကျော်တွင် အလုပ်လုပ်သည်။. + + + + DNS Service + DNS ဝန်ဆောင်မှု + + + + Sftp file sharing service + Sftp ဖိုင်မျှဝေခြင်းဝန်ဆောင်မှု + + + + + Website in Tor network + Tor ကွန်ရက်ထဲရှိ ဝဘ်ဆိုဒ် + + + + Amnezia DNS + Amnezia DNS + + + + OpenVPN is the most popular VPN protocol, with flexible configuration options. It uses its own security protocol with SSL/TLS for key exchange. + OpenVPN သည် ပြောင်းလွယ်ပြင်လွယ် ဖွဲ့စည်းမှုရွေးချယ်စရာများပါရှိသော လူကြိုက်အများဆုံး VPN ပရိုတိုကောဖြစ်သည်. ၎င်းသည် key လဲလှယ်မှုအတွက် SSL/TLS ဖြင့် ၎င်း၏ကိုယ်ပိုင်လုံခြုံရေးပရိုတိုကောကို အသုံးပြုသည်. + + + + WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship. + WireGuard - မြင့်မားသောစွမ်းဆောင်ရည်၊ မြန်နှုန်းမြင့်နှင့် ပါဝါသုံးစွဲမှုနည်းသော လူကြိုက်များသော VPN ပရိုတိုကောအသစ်. ဆင်ဆာဖြတ်မှုအဆင့်နိမ့်သော ဒေသများတွင်အသုံးပြုရန်အကြံပြုထားသည်. + + + + AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, but very resistant to blockages. Recommended for regions with high levels of censorship. + AmneziaWG - WireGuard ကိုအခြေခံထားသော Amnezia မှ အထူးပရိုတိုကော. ၎င်းသည် WireGuard ကဲ့သို့မြန်ဆန်သော်ပြီး ပိတ်ဆို့ခြင်းများကိုလည်း ခံနိုင်ရည်ရှိပါသည်. ဆင်ဆာဖြတ်တောက်မှု မြင့်မားသော ဒေသများတွင်အသုံးပြုရန် အကြံပြုပါသည်. + + + + IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss. It has native support on the latest versions of Android and iOS. + IKEv2 - ခေတ်မီတည်ငြိမ်သောပရိုတိုကော၊ အခြားအရာများထက်အနည်းငယ်ပိုမြန်သည်၊ signal ပျောက်ဆုံးပြီးနောက် ချိတ်ဆက်မှုကို ပြန်လည်ရယူပေးသည်. ၎င်းသည် Android နှင့် iOS ၏နောက်ဆုံးဗားရှင်းများတွင် မူရင်းအတိုင်းထောက်ပံ့မှုရရှိသည်. + + + + Deploy a WordPress site on the Tor network in two clicks. + ကလစ်နှစ်ချက်နှိပ်ရုံဖြင့် Tor ကွန်ရက်ပေါ်တွင် WordPress ဆိုက်တစ်ခုကို ဖြန့်ကျက်လိုက်ပါ. + + + + Replace the current DNS server with your own. This will increase your privacy level. + လက်ရှိ DNS ဆာဗာကို သင့်ကိုယ်ပိုင် DNS ဆာဗာဖြင့် အစားထိုးပါ. ဤသို့ပြုလုပ်ခြင်းသည် သင်၏ကိုယ်ရေးကိုယ်တာလုံခြုံမှုအဆင့်ကို တိုးမြှင့်ပေးလိမ့်မည်. + + + + OpenVPN stands as one of the most popular and time-tested VPN protocols available. +It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. + +* Available in the AmneziaVPN across all platforms +* Normal power consumption on mobile devices +* Flexible customisation to suit user needs to work with different operating systems and devices +* Recognised by DPI analysis systems and therefore susceptible to blocking +* Can operate over both TCP and UDP network protocols. + OpenVPN သည်ပေါ်ပြူလာအဖြစ်ဆုံးနှင့် ကာလရှည်ကြာအသုံးဝင်ခဲ့ အသုံးဝင်နေစဲဖြစ်သော VPN ပရိုတိုကောများထဲမှတစ်ခုဖြစ်သည်။ +ကုဒ်ဝှက်ခြင်းနှင့် key လဲလှယ်ခြင်းအတွက် SSL/TLS ၏ ခွန်အားကို အသုံးချခြင်းဖြင့် OpenVPN သည် ၎င်း၏ထူးခြားသော လုံခြုံရေးပရိုတိုကောကို အသုံးပြုထားသည်။ ထို့အပြင် OpenVPN ၏ အထောက်အထားစိစစ်ခြင်းနည်းလမ်းများစွာအတွက်အထောက်အပံ့ပေးထားမှုသည် ၎င်းကို စွယ်စုံရလိုက်လျောညီထွေဖြစ်စေပြီး စက်ပစ္စည်းများနှင့် လည်ပတ်မှုစနစ်များစွာကို အထောက်အပံ့ပေးစေပါသည်။ ၎င်း၏ open-source သဘောသဘာဝကြောင့် OpenVPN သည် ၎င်းကို ကျယ်ကျယ်ပြန့်ပြန့် စိစစ်စောင့်ကြည့်ပေးသည့် global community ကြောင့် လုံခြုံရေးပိုမိုကောင်းမွန်လာသည့်အကျိုးကျေးဇူးများ ရရှိခဲ့သည်။ စွမ်းဆောင်ရည်၊ လုံခြုံရေးနှင့် မည်သည့်စက်ပစ္စည်းနှင့်မဆိုလိုက်လျှောညီထွေရှိမှုဂုဏ်သတ္တိတို့ကို မျှတစွာပိုင်ဆိုင်ထားသော OpenVPN သည် ကိုယ်ရေးကိုယ်တာလုံခြုံမှုကိုအထူးဂရုစိုက်သော ပုဂ္ဂိုလ်များနှင့် စီးပွားရေးလုပ်ငန်းများအတွက် ထိပ်တန်းရွေးချယ်မှုတစ်ခုအဖြစ် ရပ်တည်နေစဲဖြစ်ပါသည်။ + +* ပလက်ဖောင်းအားလုံးရှိ AmneziaVPN တွင်အသုံးပြုနိုင်သည်။ +* မိုဘိုင်းစက်ပစ္စည်းများတွင် ပုံမှန်ပါဝါသုံးစွဲမှုရှိခြင်း။ +* မတူညီသော operating system များ၊ စက်ပစ္စည်းများနှင့်အလုပ်လုပ်သော အသုံးပြုသူများအတွက် လိုအပ်ချက်များနှင့်ကိုက်ညီရန် လိုသလိုချိန်ညှိနိုင်ခြင်း။ +* DPI ခွဲခြမ်းစိတ်ဖြာမှုစနစ်များကထောက်လှန်းနိုင်သောကြောင့် ပိတ်ဆို့ခြင်းခံရနိုင်သည်။ +* TCP နှင့် UDP ကွန်ရက် ပရိုတိုကော နှစ်ခုလုံးတွင် လည်ပတ်နိုင်သည်။. + + + + Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol. + +* Available in the AmneziaVPN only on desktop platforms +* Normal power consumption on mobile devices + +* Configurable encryption protocol +* Detectable by some DPI systems +* Works over TCP network protocol. + SOCKS5 ပရိုတိုကောကို အတုယူအခြေခံတည်ဆောက်ထားသော Shadowsocks သည် AEAD cipher ကိုအသုံးပြု၍ ချိတ်ဆက်မှုကိုကာကွယ်ပေးသည်။ Shadowsocks သည် ထောက်လှန်းသိရှိခံရခြင်းမှရှောင်ရှားနိုင်ရန်နှင့် ထောက်လှန်းသည့်သူများခက်ခဲစေရန် ဒီဇိုင်းထုတ်ထားသော်လည်း စံသတ်မှတ်ထားသည့် HTTPS ချိတ်ဆက်မှုနှင့် ထပ်တူမကျပါ။ သို့သော်၊ အချို့သောလမ်းကြောင်းဆိုင်ရာ ခွဲခြမ်းစိတ်ဖြာမှုစနစ်များသည် Shadowsocks ချိတ်ဆက်မှုကို ရှာဖွေတွေ့ရှိနိုင်သေးသည်။ Amnezia တွင် ထောက်ပံ့မှုအကန့်အသတ်ရှိသောကြောင့် AmneziaWG ပရိုတိုကောကို အသုံးပြုရန် အကြံပြုထားသည်။ + +* Desktop ပလပ်ဖောင်းများတွင်ရှိ‌သော AmneziaVPN တွင်သာအသုံးပြုနိုင်ပါသည်။ +* မိုဘိုင်းစက်ပစ္စည်းများတွင် ပုံမှန်ပါဝါသုံးစွဲမှုရှိခြင်း။ + +* ပြင်ဆင်သတ်မှတ်နိုင်သော စာဝှက်စနစ် ပရိုတိုကော +* အချို့သော DPI စနစ်များဖြင့် ထောက်လှန်းသိရှိနိုင်သည်။ +* TCP ကွန်ရက် ပရိုတိုကောပေါ်တွင် အလုပ်လုပ်သည်။. + + + + A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. +While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. +This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Not recognised by DPI analysis systems, resistant to blocking +* Works over UDP network protocol. + လူကြိုက်များသော VPN ပရိုတိုကော၏ ခေတ်မီပြန်လည်တည်ဆောက်မှုတစ်ခုဖြစ်သော AmneziaWG သည် WireGuard မှသတ်မှတ်ထားသော အခြေခံအုတ်မြစ်ပေါ်တွင် တည်ဆောက်ထားပြီး ၎င်း၏ရိုးရှင်းသောတည်ဆောက်ပုံနှင့် စွမ်းဆောင်ရည်မြင့်မားသောစွမ်းရည်များကို ဆက်လက်ထိန်းသိမ်းထားပါသည်။ +WireGuard သည် ၎င်း၏ စွမ်းဆောင်ရည်အတွက် လူသိများသော်လည်း ၎င်း၏ ထူးခြားသော packet လက်မှတ်များ ကြောင့် အလွယ်တကူ ထောက်လှန်းရှာဖွေတွေ့ရှိနိုင်သည့် ပြဿနာများ ရှိခဲ့ပါသည်။ AmneziaWG သည် ၎င်း၏ အသွားအလာကို ပုံမှန်အင်တာနက်အသွားအလာနှင့် ရောနှောကာ ပိုမိုကောင်းမွန်သော ရှုပ်ထွေးသော နည်းလမ်းများကို အသုံးပြုခြင်းဖြင့် ဤပြဿနာကို ဖြေရှင်းပေးထားပါသည်။ +ဆိုလိုသည်မှာ AmneziaWG သည် နောက်ထပ်ထောက်လှန်းရခက်စေသည့်အလွှာတစ်ခုထပ်ထည့်စဉ်တွင် မူရင်းမြန်ဆန်သောစွမ်းဆောင်ရည်ကို ထိန်းသိမ်းထားနိုင်သောကြောင့် မြန်ဆန်ပြီးပါးနပ်သော VPN ချိတ်ဆက်မှုကိုလိုချင်သူများအတွက် အကောင်းဆုံးရွေးချယ်မှုတစ်ခုဖြစ်ပါသည်။ + +* ပလက်ဖောင်းအားလုံးရှိ AmneziaVPN တွင်ရနိုင်သည်။ +* ပါဝါသုံးစွဲမှုနည်းပါးခြင်း။ +* ဆက်တင်အရေအတွက်နည်းပါခြင်း။ (အလုပ်ရှုပ်သက်သာ) +* DPI ခွဲခြမ်းစိတ်ဖြာမှုစနစ်များက ထောက်လှန်းမှုမပြုနိုင်ဘဲ ပိတ်ဆို့ခြင်းကိုခံနိုင်ရည်ရှိသည်။ +* UDP ကွန်ရက်ပရိုတိုကောပေါ်တွင် အလုပ်လုပ်သည်။. + + + + Sftp file sharing service - is secure FTP service + Sftp ဖိုင်မျှဝေခြင်းဝန်ဆောင်မှု - လုံခြုံသော FTP ဝန်ဆောင်မှုဖြစ်သည် + + + + Sftp service + Sftp ဝန်ဆောင်မှု + + + + Entry not found + ဝင်ခွင့်မတွေ့ရှိပါ + + + + Access to keychain denied + Keychain အသုံးပြုခွင့် ငြင်းပယ်ခံလိုက်ရသည် + + + + No keyring daemon + keyring daemon မရှိပါ + + + + Already unlocked + လော့ခ်ဖွင့်ပြီးဖြစ်သည် + + + + No such keyring + ဤ keyring မျိုးမရှိပါ + + + + Bad arguments + Bad arguments + + + + I/O error + I/O မှားယွင်းမှု + + + + Cancelled + ပယ်ဖျက်လိုက်သည် + + + + Keyring already exists + Keyring ရှိနှင့်ပြီးဖြစ်သည် + + + + No match + ကိုက်ညီမှုမရှိပါ + + + + Unknown error + အမည်မသိ မှားယွင်းမှု + + + + error 0x%1: %2 + မှားယွင်းမှု 0x%1: %2 + + + + SelectLanguageDrawer + + + Choose language + ဘာသာစကားကို ရွေးချယ်ပါ + + + + Settings + + + Server #1 + ဆာဗာ #1 + + + + + Server + ဆာဗာ + + + + SettingsController + + + All settings have been reset to default values + ဆက်တင်အားလုံးကို မူရင်းတန်ဖိုးများအဖြစ် ပြန်လည်သတ်မှတ်ထားသည် + + + + Cached profiles cleared + ကက်ရှ်ပရိုဖိုင်များကို ရှင်းလင်းပြီးပါပြီ + + + + Backup file is corrupted + အရန်သိမ်းထားသည့်ဖိုင်ပျက်ဆီးနေသည် + + + + ShareConnectionDrawer + + + + Save AmneziaVPN config + AmneziaWG config ကိုသိမ်းဆည်းမည် + + + + Share + မျှဝေမည် + + + + Copy + ကူးယူမည် + + + + + Copied + ကူးယူပြီးပါပြီ + + + + Copy config string + config string ကိုကူးယူမည် + + + + Show connection settings + ချိတ်ဆက်မှုဆက်တင်များကို ပြပါ + + + + To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file" + Amnezia အက်ပ်ရှိ QR ကုဒ်ကိုဖတ်ရန်အတွက်အောက်ပါအတိုင်း ရွေးချယ်ပါ "ဆာဗာထည့်ရန်" → "ချိတ်ဆက်ရန် ဒေတာရှိသည်" → "QR ကုဒ်၊ key သို့မဟုတ် ဆက်တင်ဖိုင်" + + + + SitesController + + + Hostname not look like ip adress or domain name + လက်ခံသူအမည်သည် ip လိပ်စာ သို့မဟုတ် ဒိုမိန်းအမည်နှင့် မတူပါ + + + + New site added: %1 + ဆိုဒ်အသစ်ထပ်ထည့်ပြီးပါပြီ: %1 + + + + Site removed: %1 + ဆိုက်ကို ဖယ်ရှားလိုက်သည်: %1 + + + + Can't open file: %1 + ဖိုင်ကိုဖွင့်၍မရပါ: %1 + + + + Failed to parse JSON data from file: %1 + JSON ဒေတာကို ဖိုင်မှ ခွဲခြမ်းထုပ်ယူမှု မအောင်မြင်ပါ: %1 + + + + The JSON data is not an array in file: %1 + JSON ဒေတာသည် ဖိုင်ထဲရှိ array တစ်ခုမဟုတ်ပါ: %1 + + + + Import completed + တင်သွင်းခြင်းပြီးဆုံးသွားပါပြီ + + + + Export completed + ထုတ်ယူခြင်းပြီးဆုံးသွားပါပြီ + + + + SystemTrayNotificationHandler + + + + Show + ပြမည် + + + + + Connect + ချိတ်ဆက်မည် + + + + + Disconnect + ဖြုတ်ချမည် + + + + + Visit Website + ဝဘ်ဆိုက်ကိုဝင်ကြည့်မည် + + + + + Quit + ထွက်မည် + + + + TextFieldWithHeaderType + + + The field can't be empty + ဖြည့်သွင်းရမည့်နေရာသည် အလွတ်မဖြစ်ရပါ + + + + VpnConnection + + + Mbps + Mbps + + + + VpnProtocol + + + Unknown + အမည်မသိ + + + + Disconnected + ဖြုတ်လိုက်ပါပြီ + + + + Preparing + ပြင်ဆင်နေပါသည် + + + + Connecting... + ချိတ်ဆက်နေပါပြီ... + + + + Connected + ချိတ်ဆက်ပြီးသွားပါပြီ + + + + Disconnecting... + အဆက်အသွယ်ဖြတ်နေပါသည်... + + + + Reconnecting... + ပြန်လည်ချိတ်ဆက်နေပါသည်... + + + + Error + မှားယွင်းမှု + + + + amnezia::ContainerProps + + + Low + Low + + + + Medium or High + Medium သို့မဟုတ် High + + + + Extreme + Extreme + + + + I just want to increase the level of my privacy. + ကျွန်ုပ်၏ကိုယ်ရေးကိုယ်တာလုံခြုံမှုအဆင့်ကို မြှင့်တင်လိုပါသည်. + + + + I want to bypass censorship. This option recommended in most cases. + ဆင်ဆာဖြတ်တောက်ခြင်းကို ကျော်ဖြတ်ချင်ပါသည်. ဤရွေးချယ်မှုကို ကိစ္စအများစုအတွက် အကြံပြုထားသည်. + + + + Most VPN protocols are blocked. Recommended if other options are not working. + VPN ပရိုတိုကောအများစုကို ပိတ်ဆို့ထားသည်. အခြားရွေးချယ်စရာများ အလုပ်မလုပ်ပါက အသုံးပြုရန်အကြံပြုထားသည်. + + + + main2 + + + Private key passphrase + ကိုယ်ပိုင် key စကားဝှက် + + + + Save + သိမ်းဆည်းမည် + + + diff --git a/client/ui/models/languageModel.cpp b/client/ui/models/languageModel.cpp index 47e41708..28652d2b 100644 --- a/client/ui/models/languageModel.cpp +++ b/client/ui/models/languageModel.cpp @@ -46,6 +46,7 @@ QString LanguageModel::getLocalLanguageName(const LanguageSettings::AvailableLan case LanguageSettings::AvailableLanguageEnum::China_cn: strLanguage = "\347\256\200\344\275\223\344\270\255\346\226\207"; break; case LanguageSettings::AvailableLanguageEnum::Persian: strLanguage = "فارسی"; break; case LanguageSettings::AvailableLanguageEnum::Arabic: strLanguage = "العربية"; break; + case LanguageSettings::AvailableLanguageEnum::Burmese: strLanguage = "မြန်မာဘာသာ"; break; default: break; } @@ -61,6 +62,7 @@ void LanguageModel::changeLanguage(const LanguageSettings::AvailableLanguageEnum case LanguageSettings::AvailableLanguageEnum::China_cn: emit updateTranslations(QLocale::Chinese); break; case LanguageSettings::AvailableLanguageEnum::Persian: emit updateTranslations(QLocale::Persian); break; case LanguageSettings::AvailableLanguageEnum::Arabic: emit updateTranslations(QLocale::Arabic); break; + case LanguageSettings::AvailableLanguageEnum::Burmese: emit updateTranslations(QLocale::Burmese); break; default: emit updateTranslations(QLocale::English); break; } } @@ -74,6 +76,7 @@ int LanguageModel::getCurrentLanguageIndex() case QLocale::Chinese: return static_cast(LanguageSettings::AvailableLanguageEnum::China_cn); break; case QLocale::Persian: return static_cast(LanguageSettings::AvailableLanguageEnum::Persian); break; case QLocale::Arabic: return static_cast(LanguageSettings::AvailableLanguageEnum::Arabic); break; + case QLocale::Burmese: return static_cast(LanguageSettings::AvailableLanguageEnum::Burmese); break; default: return static_cast(LanguageSettings::AvailableLanguageEnum::English); break; } } diff --git a/client/ui/models/languageModel.h b/client/ui/models/languageModel.h index b07a5c78..fe4b9fbd 100644 --- a/client/ui/models/languageModel.h +++ b/client/ui/models/languageModel.h @@ -14,7 +14,8 @@ namespace LanguageSettings Russian, China_cn, Persian, - Arabic + Arabic, + Burmese }; Q_ENUM_NS(AvailableLanguageEnum) From 840c388ab998ccc2a3e0b9e12b9b52d33568ce51 Mon Sep 17 00:00:00 2001 From: isamnezia <156459471+isamnezia@users.noreply.github.com> Date: Wed, 6 Mar 2024 04:18:19 +0300 Subject: [PATCH 07/28] Add in-app screenshot preventing (#606) In-app screenshot preventing fixes --- client/amnezia_application.cpp | 10 +++ .../src/org/amnezia/vpn/AmneziaActivity.kt | 10 +++ client/cmake/ios.cmake | 1 + .../platforms/android/android_controller.cpp | 5 ++ client/platforms/android/android_controller.h | 1 + client/platforms/ios/QtAppDelegate.mm | 25 ------ client/platforms/ios/ScreenProtection.swift | 87 +++++++++++++++++++ client/settings.h | 2 + client/ui/controllers/settingsController.cpp | 29 ------- client/ui/qml/Pages2/PageHome.qml | 1 - 10 files changed, 116 insertions(+), 55 deletions(-) create mode 100644 client/platforms/ios/ScreenProtection.swift diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 1ac179fd..7ddc8878 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -24,6 +24,7 @@ #if defined(Q_OS_IOS) #include "platforms/ios/ios_controller.h" + #include #endif #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) @@ -98,6 +99,10 @@ void AmneziaApplication::init() connect(m_settings.get(), &Settings::saveLogsChanged, AndroidController::instance(), &AndroidController::setSaveLogs); + AndroidController::instance()->setScreenshotsEnabled(m_settings->isScreenshotsEnabled()); + connect(m_settings.get(), &Settings::screenshotsEnabledChanged, + AndroidController::instance(), &AndroidController::setScreenshotsEnabled); + connect(m_settings.get(), &Settings::serverRemoved, AndroidController::instance(), &AndroidController::resetLastServer); @@ -134,6 +139,11 @@ void AmneziaApplication::init() m_pageController->goToPageSettingsBackup(); m_settingsController->importBackupFromOutside(filePath); }); + + AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); + connect(m_settings.get(), &Settings::screenshotsEnabledChanged, [](bool enabled) { + AmneziaVPN::toggleScreenshots(enabled); + }); #endif m_notificationHandler.reset(NotificationHandler::create(nullptr)); diff --git a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt index f01e8df6..ff25ab05 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt @@ -14,6 +14,7 @@ import android.os.IBinder import android.os.Looper import android.os.Message import android.os.Messenger +import android.view.WindowManager.LayoutParams import android.webkit.MimeTypeMap import android.widget.Toast import androidx.annotation.MainThread @@ -453,4 +454,13 @@ class AmneziaActivity : QtActivity() { Log.v(TAG, "Clear logs") Log.clearLogs() } + + @Suppress("unused") + fun setScreenshotsEnabled(enabled: Boolean) { + Log.v(TAG, "Set screenshots enabled: $enabled") + mainScope.launch { + val flag = if (enabled) 0 else LayoutParams.FLAG_SECURE + window.setFlags(flag, LayoutParams.FLAG_SECURE) + } + } } diff --git a/client/cmake/ios.cmake b/client/cmake/ios.cmake index 824c1caf..ce6c8f94 100644 --- a/client/cmake/ios.cmake +++ b/client/cmake/ios.cmake @@ -107,6 +107,7 @@ target_sources(${PROJECT} PRIVATE ${CLIENT_ROOT_DIR}/platforms/ios/LogController.swift ${CLIENT_ROOT_DIR}/platforms/ios/Log.swift ${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift + ${CLIENT_ROOT_DIR}/platforms/ios/ScreenProtection.swift ) target_sources(${PROJECT} PRIVATE diff --git a/client/platforms/android/android_controller.cpp b/client/platforms/android/android_controller.cpp index b789f0e0..e18fd864 100644 --- a/client/platforms/android/android_controller.cpp +++ b/client/platforms/android/android_controller.cpp @@ -204,6 +204,11 @@ void AndroidController::clearLogs() callActivityMethod("clearLogs", "()V"); } +void AndroidController::setScreenshotsEnabled(bool enabled) +{ + callActivityMethod("setScreenshotsEnabled", "(Z)V", enabled); +} + // Moving log processing to the Android side jclass AndroidController::log; jmethodID AndroidController::logDebug; diff --git a/client/platforms/android/android_controller.h b/client/platforms/android/android_controller.h index 3491d837..6c104f3c 100644 --- a/client/platforms/android/android_controller.h +++ b/client/platforms/android/android_controller.h @@ -39,6 +39,7 @@ public: void setSaveLogs(bool enabled); void exportLogsFile(const QString &fileName); void clearLogs(); + void setScreenshotsEnabled(bool enabled); static bool initLogging(); static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message); diff --git a/client/platforms/ios/QtAppDelegate.mm b/client/platforms/ios/QtAppDelegate.mm index 70e54400..bd7ad6b1 100644 --- a/client/platforms/ios/QtAppDelegate.mm +++ b/client/platforms/ios/QtAppDelegate.mm @@ -3,7 +3,6 @@ #include -UIView *_screen; @implementation QIOSApplicationDelegate (AmneziaVPNDelegate) @@ -15,19 +14,6 @@ UIView *_screen; return YES; } -- (void)applicationWillResignActive:(UIApplication *)application -{ - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. - _screen = [UIScreen.mainScreen snapshotViewAfterScreenUpdates: false]; - UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle: UIBlurEffectStyleDark]; - UIVisualEffectView *blurBackground = [[UIVisualEffectView alloc] initWithEffect: blurEffect]; - [_screen addSubview: blurBackground]; - blurBackground.frame = _screen.frame; - UIWindow *_window = UIApplication.sharedApplication.keyWindow; - [_window addSubview: _screen]; -} - - (void)applicationDidEnterBackground:(UIApplication *)application { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. @@ -41,17 +27,6 @@ UIView *_screen; NSLog(@"In the foreground"); } -- (void)applicationDidBecomeActive:(UIApplication *)application -{ - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. - [_screen removeFromSuperview]; -} - -- (void)applicationWillTerminate:(UIApplication *)application -{ - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. -} - -(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { // We will add content here soon. NSLog(@"In the completionHandler"); diff --git a/client/platforms/ios/ScreenProtection.swift b/client/platforms/ios/ScreenProtection.swift new file mode 100644 index 00000000..1355dc13 --- /dev/null +++ b/client/platforms/ios/ScreenProtection.swift @@ -0,0 +1,87 @@ +import UIKit + +public func toggleScreenshots(_ isEnabled: Bool) { + let window = UIApplication.shared.keyWindows.first! + + if isEnabled { + ScreenProtection.shared.disable(for: window.rootViewController!.view) + } else { + ScreenProtection.shared.enable(for: window.rootViewController!.view) + } +} + +extension UIApplication { + var keyWindows: [UIWindow] { + connectedScenes + .compactMap { + if #available(iOS 15.0, *) { + ($0 as? UIWindowScene)?.keyWindow + } else { + ($0 as? UIWindowScene)?.windows.first { $0.isKeyWindow } + } + } + } +} + +class ScreenProtection { + public static let shared = ScreenProtection() + + var pairs = [ProtectionPair]() + + private var blurView: UIVisualEffectView? + private var recordingObservation: NSKeyValueObservation? + + public func enable(for view: UIView) { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + view.subviews.forEach { + self.pairs.append(ProtectionPair(from: $0)) + } + } + } + + public func disable(for view: UIView) { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + self.pairs.forEach { + $0.removeProtection() + } + + self.pairs.removeAll() + } + } +} + +struct ProtectionPair { + let textField: UITextField + let layer: CALayer + + init(from view: UIView) { + let secureTextField = UITextField() + secureTextField.backgroundColor = .clear + secureTextField.translatesAutoresizingMaskIntoConstraints = false + secureTextField.isSecureTextEntry = true + + view.insertSubview(secureTextField, at: 0) + secureTextField.isUserInteractionEnabled = false + + view.layer.superlayer?.addSublayer(secureTextField.layer) + secureTextField.layer.sublayers?.last?.addSublayer(view.layer) + + secureTextField.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true + secureTextField.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true + secureTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true + secureTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true + + self.init(textField: secureTextField, layer: view.layer) + } + + init(textField: UITextField, layer: CALayer) { + self.textField = textField + self.layer = layer + } + + func removeProtection() { + textField.superview?.superview?.layer.addSublayer(layer) + textField.layer.removeFromSuperlayer() + textField.removeFromSuperview() + } +} diff --git a/client/settings.h b/client/settings.h index 613d567b..b11747e1 100644 --- a/client/settings.h +++ b/client/settings.h @@ -185,12 +185,14 @@ public: void setScreenshotsEnabled(bool enabled) { setValue("Conf/screenshotsEnabled", enabled); + emit screenshotsEnabledChanged(enabled); } void clearSettings(); signals: void saveLogsChanged(bool enabled); + void screenshotsEnabledChanged(bool enabled); void serverRemoved(int serverIndex); void settingsCleared(); diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index 4aa64533..658bbb19 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -7,9 +7,7 @@ #include "ui/qautostart.h" #include "version.h" #ifdef Q_OS_ANDROID - #include "platforms/android/android_utils.h" #include "platforms/android/android_controller.h" - #include #endif #ifdef Q_OS_IOS @@ -29,20 +27,6 @@ SettingsController::SettingsController(const QSharedPointer &serve m_settings(settings) { m_appVersion = QString("%1 (%2, %3)").arg(QString(APP_VERSION), __DATE__, GIT_COMMIT_HASH); - -#ifdef Q_OS_ANDROID - if (!m_settings->isScreenshotsEnabled()) { - // Set security screen for Android app - AndroidUtils::runOnAndroidThreadSync([]() { - QJniObject activity = AndroidUtils::getActivity(); - QJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;"); - if (window.isValid()) { - const int FLAG_SECURE = 8192; - window.callMethod("addFlags", "(I)V", FLAG_SECURE); - } - }); - } -#endif } void SettingsController::toggleAmneziaDns(bool enable) @@ -204,19 +188,6 @@ bool SettingsController::isScreenshotsEnabled() void SettingsController::toggleScreenshotsEnabled(bool enable) { m_settings->setScreenshotsEnabled(enable); -#ifdef Q_OS_ANDROID - std::string command = enable ? "clearFlags" : "addFlags"; - - // Set security screen for Android app - AndroidUtils::runOnAndroidThreadSync([&command]() { - QJniObject activity = AndroidUtils::getActivity(); - QJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;"); - if (window.isValid()) { - const int FLAG_SECURE = 8192; - window.callMethod(command.c_str(), "(I)V", FLAG_SECURE); - } - }); -#endif } bool SettingsController::isCameraPresent() diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index 06ce907a..335d59a4 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -160,7 +160,6 @@ PageType { Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter text: ServersModel.defaultServerDescriptionCollapsed } - } expandedContent: Item { id: serverMenuContainer From 6b6a76d2ccb3aaf6cfec6468ac03989cdfd20300 Mon Sep 17 00:00:00 2001 From: dimov96 <45920583+dimov96@users.noreply.github.com> Date: Wed, 6 Mar 2024 02:24:28 +0100 Subject: [PATCH 08/28] Replace sftp with scp (#602) Replace sftp with scp --- client/configurators/openvpn_configurator.cpp | 2 +- .../configurators/wireguard_configurator.cpp | 2 +- client/core/controllers/serverController.cpp | 10 +- client/core/controllers/serverController.h | 4 +- client/core/defs.h | 27 ++-- client/core/errorstrings.cpp | 24 ++- client/core/sshclient.cpp | 146 +++++++----------- client/core/sshclient.h | 24 +-- 8 files changed, 98 insertions(+), 141 deletions(-) diff --git a/client/configurators/openvpn_configurator.cpp b/client/configurators/openvpn_configurator.cpp index 8b201fbf..0cd331f7 100644 --- a/client/configurators/openvpn_configurator.cpp +++ b/client/configurators/openvpn_configurator.cpp @@ -76,7 +76,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) { if (errorCode) - *errorCode = ErrorCode::SshSftpFailureError; + *errorCode = ErrorCode::SshScpFailureError; } return connData; diff --git a/client/configurators/wireguard_configurator.cpp b/client/configurators/wireguard_configurator.cpp index 8bfd5e75..f28ac539 100644 --- a/client/configurators/wireguard_configurator.cpp +++ b/client/configurators/wireguard_configurator.cpp @@ -159,7 +159,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon .arg(connData.clientPubKey, connData.pskKey, connData.clientIP); e = serverController.uploadTextFileToContainer(container, credentials, configPart, m_serverConfigPath, - libssh::SftpOverwriteMode::SftpAppendToExisting); + libssh::ScpOverwriteMode::ScpAppendToExisting); if (e) { if (errorCode) diff --git a/client/core/controllers/serverController.cpp b/client/core/controllers/serverController.cpp index 9a170a85..99ee5b11 100644 --- a/client/core/controllers/serverController.cpp +++ b/client/core/controllers/serverController.cpp @@ -118,7 +118,7 @@ ServerController::runContainerScript(const ServerCredentials &credentials, Docke ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials, const QString &file, const QString &path, - libssh::SftpOverwriteMode overwriteMode) + libssh::ScpOverwriteMode overwriteMode) { ErrorCode e = ErrorCode::NoError; QString tmpFileName = QString("/tmp/%1.tmp").arg(Utils::getRandomString(16)); @@ -139,7 +139,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, if (e) return e; - if (overwriteMode == libssh::SftpOverwriteMode::SftpOverwriteExisting) { + if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) { e = runScript(credentials, replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path), genVarsForScript(credentials, container)), @@ -147,7 +147,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, if (e) return e; - } else if (overwriteMode == libssh::SftpOverwriteMode::SftpAppendToExisting) { + } else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) { e = runScript(credentials, replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName), genVarsForScript(credentials, container)), @@ -199,7 +199,7 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container, } ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, - const QString &remotePath, libssh::SftpOverwriteMode overwriteMode) + const QString &remotePath, libssh::ScpOverwriteMode overwriteMode) { auto error = m_sshClient.connectToHost(credentials); if (error != ErrorCode::NoError) { @@ -211,7 +211,7 @@ ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credential localFile.write(data); localFile.close(); - error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName(), remotePath, "non_desc"); + error = m_sshClient.scpFileCopy(overwriteMode, localFile.fileName(), remotePath, "non_desc"); if (error != ErrorCode::NoError) { return error; diff --git a/client/core/controllers/serverController.h b/client/core/controllers/serverController.h index 16569dbb..7caad366 100644 --- a/client/core/controllers/serverController.h +++ b/client/core/controllers/serverController.h @@ -38,7 +38,7 @@ public: ErrorCode uploadTextFileToContainer( DockerContainer container, const ServerCredentials &credentials, const QString &file, const QString &path, - libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting); + libssh::ScpOverwriteMode overwriteMode = libssh::ScpOverwriteMode::ScpOverwriteExisting); QByteArray getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials, const QString &path, ErrorCode *errorCode = nullptr); @@ -80,7 +80,7 @@ private: ErrorCode isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container); ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath, - libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting); + libssh::ScpOverwriteMode overwriteMode = libssh::ScpOverwriteMode::ScpOverwriteExisting); ErrorCode setupServerFirewall(const ServerCredentials &credentials); diff --git a/client/core/defs.h b/client/core/defs.h index 6d1f1a34..e6f3cece 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -46,25 +46,12 @@ namespace amnezia SshPrivateKeyFormatError = 304, SshTimeoutError = 305, - // Ssh sftp errors - SshSftpEofError = 400, - SshSftpNoSuchFileError = 401, - SshSftpPermissionDeniedError = 402, - SshSftpFailureError = 403, - SshSftpBadMessageError = 404, - SshSftpNoConnectionError = 405, - SshSftpConnectionLostError = 406, - SshSftpOpUnsupportedError = 407, - SshSftpInvalidHandleError = 408, - SshSftpNoSuchPathError = 409, - SshSftpFileAlreadyExistsError = 410, - SshSftpWriteProtectError = 411, - SshSftpNoMediaError = 412, + // Ssh scp errors + SshScpFailureError = 400, // Local errors OpenVpnConfigMissing = 500, OpenVpnManagementServerError = 501, - ConfigMissing = 502, // Distro errors OpenVpnExecutableMissing = 600, @@ -92,7 +79,15 @@ namespace amnezia // Api errors ApiConfigDownloadError = 1100, - ApiConfigAlreadyAdded = 1101 + ApiConfigAlreadyAdded = 1101, + + // QFile errors + OpenError = 1200, + ReadError = 1201, + PermissionsError = 1202, + UnspecifiedError = 1203, + FatalError = 1204, + AbortError = 1205 }; } // namespace amnezia diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index 48cba3c5..17ac5ab7 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -28,20 +28,8 @@ QString errorString(ErrorCode code) { case(SshPrivateKeyFormatError): errorMessage = QObject::tr("The selected private key format is not supported, use openssh ED25519 key types or PEM key types"); break; case(SshTimeoutError): errorMessage = QObject::tr("Timeout connecting to server"); break; - // Libssh sftp errors - case(SshSftpEofError): errorMessage = QObject::tr("Sftp error: End-of-file encountered"); break; - case(SshSftpNoSuchFileError): errorMessage = QObject::tr("Sftp error: File does not exist"); break; - case(SshSftpPermissionDeniedError): errorMessage = QObject::tr("Sftp error: Permission denied"); break; - case(SshSftpFailureError): errorMessage = QObject::tr("Sftp error: Generic failure"); break; - case(SshSftpBadMessageError): errorMessage = QObject::tr("Sftp error: Garbage received from server"); break; - case(SshSftpNoConnectionError): errorMessage = QObject::tr("Sftp error: No connection has been set up"); break; - case(SshSftpConnectionLostError): errorMessage = QObject::tr("Sftp error: There was a connection, but we lost it"); break; - case(SshSftpOpUnsupportedError): errorMessage = QObject::tr("Sftp error: Operation not supported by libssh yet"); break; - case(SshSftpInvalidHandleError): errorMessage = QObject::tr("Sftp error: Invalid file handle"); break; - case(SshSftpNoSuchPathError): errorMessage = QObject::tr("Sftp error: No such file or directory path exists"); break; - case(SshSftpFileAlreadyExistsError): errorMessage = QObject::tr("Sftp error: An attempt to create an already existing file or directory has been made"); break; - case(SshSftpWriteProtectError): errorMessage = QObject::tr("Sftp error: Write-protected filesystem"); break; - case(SshSftpNoMediaError): errorMessage = QObject::tr("Sftp error: No media was in remote drive"); break; + // Ssh scp errors + case(SshScpFailureError): errorMessage = QObject::tr("Scp error: Generic failure"); break; // Local errors case (OpenVpnConfigMissing): errorMessage = QObject::tr("OpenVPN config missing"); break; @@ -68,6 +56,14 @@ QString errorString(ErrorCode code) { case (ApiConfigDownloadError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break; case (ApiConfigAlreadyAdded): errorMessage = QObject::tr("This config has already been added to the application"); break; + // QFile errors + case(OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break; + case(ReadError): errorMessage = QObject::tr("QFile error: An error occurred when reading from the file"); break; + case(PermissionsError): errorMessage = QObject::tr("QFile error: The file could not be accessed"); break; + case(UnspecifiedError): errorMessage = QObject::tr("QFile error: An unspecified error occurred"); break; + case(FatalError): errorMessage = QObject::tr("QFile error: A fatal error occurred"); break; + case(AbortError): errorMessage = QObject::tr("QFile error: The operation was aborted"); break; + case(InternalError): default: errorMessage = QObject::tr("Internal error"); break; diff --git a/client/core/sshclient.cpp b/client/core/sshclient.cpp index 03670b30..01ef7627 100644 --- a/client/core/sshclient.cpp +++ b/client/core/sshclient.cpp @@ -10,16 +10,10 @@ const uint32_t S_IRWXU = 0644; #endif namespace libssh { - const QString libsshTimeoutError = "Timeout connecting to"; + constexpr auto libsshTimeoutError{"Timeout connecting to"}; std::function Client::m_passphraseCallback; - Client::Client(QObject *parent) : QObject(parent) - { } - - Client::~Client() - { } - int Client::callback(const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata) { auto passphrase = m_passphraseCallback(); @@ -171,13 +165,13 @@ namespace libssh { return ErrorCode::NoError; }; - auto error = readOutput(false); - if (error != ErrorCode::NoError) { - return error; + auto errorCode = readOutput(false); + if (errorCode != ErrorCode::NoError) { + return errorCode; } - error = readOutput(true); - if (error != ErrorCode::NoError) { - return error; + errorCode = readOutput(true); + if (errorCode != ErrorCode::NoError) { + return errorCode; } } else { return closeChannel(); @@ -222,100 +216,79 @@ namespace libssh { return fromLibsshErrorCode(); } - ErrorCode Client::sftpFileCopy(const SftpOverwriteMode overwriteMode, const QString& localPath, const QString& remotePath, const QString &fileDesc) + ErrorCode Client::scpFileCopy(const ScpOverwriteMode overwriteMode, const QString& localPath, const QString& remotePath, const QString &fileDesc) { - m_sftpSession = sftp_new(m_session); + m_scpSession = ssh_scp_new(m_session, SSH_SCP_WRITE, remotePath.toStdString().c_str()); - if (m_sftpSession == nullptr) { - return closeSftpSession(); + if (m_scpSession == nullptr) { + return fromLibsshErrorCode(); } - int result = sftp_init(m_sftpSession); - - if (result != SSH_OK) { - return closeSftpSession(); + if (ssh_scp_init(m_scpSession) != SSH_OK) { + auto errorCode = fromLibsshErrorCode(); + closeScpSession(); + return errorCode; } QFutureWatcher watcher; - connect(&watcher, &QFutureWatcher::finished, this, &Client::sftpFileCopyFinished); - + connect(&watcher, &QFutureWatcher::finished, this, &Client::scpFileCopyFinished); QFuture future = QtConcurrent::run([this, overwriteMode, &localPath, &remotePath, &fileDesc]() { - int accessType = O_WRONLY | O_CREAT | overwriteMode; - sftp_file file; - const size_t bufferSize = 16384; - char buffer[bufferSize]; + const int accessType = O_WRONLY | O_CREAT | overwriteMode; + const int localFileSize = QFileInfo(localPath).size(); - file = sftp_open(m_sftpSession, remotePath.toStdString().c_str(), accessType, S_IRWXU); - - if (file == nullptr) { - return closeSftpSession(); + int result = ssh_scp_push_file(m_scpSession, remotePath.toStdString().c_str(), localFileSize, accessType); + if (result != SSH_OK) { + return fromLibsshErrorCode(); } - int localFileSize = QFileInfo(localPath).size(); - int chunksCount = localFileSize / (bufferSize); - QFile fin(localPath); if (fin.open(QIODevice::ReadOnly)) { - for (int currentChunkId = 0; currentChunkId < chunksCount; currentChunkId++) { - QByteArray chunk = fin.read(bufferSize); - if (chunk.size() != bufferSize) return ErrorCode::SshSftpEofError; + constexpr size_t bufferSize = 16384; + int transferred = 0; + int currentChunkSize = bufferSize; - int bytesWritten = sftp_write(file, chunk.data(), chunk.size()); + while (transferred < localFileSize) { - if (bytesWritten != chunk.size()) { - fin.close(); - sftp_close(file); - return closeSftpSession(); + // Last Chunk + if ((localFileSize - transferred) < bufferSize) { + currentChunkSize = localFileSize % bufferSize; } - } - int lastChunkSize = localFileSize % bufferSize; - - if (lastChunkSize != 0) { - QByteArray lastChunk = fin.read(lastChunkSize); - if (lastChunk.size() != lastChunkSize) return ErrorCode::SshSftpEofError; - - int bytesWritten = sftp_write(file, lastChunk.data(), lastChunkSize); - - if (bytesWritten != lastChunkSize) { - fin.close(); - sftp_close(file); - return closeSftpSession(); + QByteArray chunk = fin.read(currentChunkSize); + if (chunk.size() != currentChunkSize) { + return fromFileErrorCode(fin.error()); } + + result = ssh_scp_write(m_scpSession, chunk.data(), chunk.size()); + if (result != SSH_OK) { + return fromLibsshErrorCode(); + } + + transferred += currentChunkSize; } } else { - sftp_close(file); - return closeSftpSession(); + return fromFileErrorCode(fin.error()); } - fin.close(); - - int result = sftp_close(file); - if (result != SSH_OK) { - return closeSftpSession(); - } - - return closeSftpSession(); + return ErrorCode::NoError; }); watcher.setFuture(future); QEventLoop wait; - QObject::connect(this, &Client::sftpFileCopyFinished, &wait, &QEventLoop::quit); + QObject::connect(this, &Client::scpFileCopyFinished, &wait, &QEventLoop::quit); wait.exec(); + closeScpSession(); return watcher.result(); } - ErrorCode Client::closeSftpSession() + void Client::closeScpSession() { - auto errorCode = fromLibsshSftpErrorCode(sftp_get_error(m_sftpSession)); - if (m_sftpSession != nullptr) { - sftp_free(m_sftpSession); - m_sftpSession = nullptr; + if (m_scpSession != nullptr) { + ssh_scp_free(m_scpSession); + m_scpSession = nullptr; } - qCritical() << ssh_get_error(m_session); - return errorCode; } ErrorCode Client::fromLibsshErrorCode() @@ -337,24 +310,17 @@ namespace libssh { default: return ErrorCode::SshInternalError; } } - ErrorCode Client::fromLibsshSftpErrorCode(int errorCode) + + ErrorCode Client::fromFileErrorCode(QFileDevice::FileError fileError) { - switch (errorCode) { - case(SSH_FX_OK): return ErrorCode::NoError; - case(SSH_FX_EOF): return ErrorCode::SshSftpEofError; - case(SSH_FX_NO_SUCH_FILE): return ErrorCode::SshSftpNoSuchFileError; - case(SSH_FX_PERMISSION_DENIED): return ErrorCode::SshSftpPermissionDeniedError; - case(SSH_FX_FAILURE): return ErrorCode::SshSftpFailureError; - case(SSH_FX_BAD_MESSAGE): return ErrorCode::SshSftpBadMessageError; - case(SSH_FX_NO_CONNECTION): return ErrorCode::SshSftpNoConnectionError; - case(SSH_FX_CONNECTION_LOST): return ErrorCode::SshSftpConnectionLostError; - case(SSH_FX_OP_UNSUPPORTED): return ErrorCode::SshSftpOpUnsupportedError; - case(SSH_FX_INVALID_HANDLE): return ErrorCode::SshSftpInvalidHandleError; - case(SSH_FX_NO_SUCH_PATH): return ErrorCode::SshSftpNoSuchPathError; - case(SSH_FX_FILE_ALREADY_EXISTS): return ErrorCode::SshSftpFileAlreadyExistsError; - case(SSH_FX_WRITE_PROTECT): return ErrorCode::SshSftpWriteProtectError; - case(SSH_FX_NO_MEDIA): return ErrorCode::SshSftpNoMediaError; - default: return ErrorCode::SshSftpFailureError; + switch (fileError) { + case QFileDevice::NoError: return ErrorCode::NoError; + case QFileDevice::ReadError: return ErrorCode::ReadError; + case QFileDevice::OpenError: return ErrorCode::OpenError; + case QFileDevice::PermissionsError: return ErrorCode::PermissionsError; + case QFileDevice::FatalError: return ErrorCode::FatalError; + case QFileDevice::AbortError: return ErrorCode::AbortError; + default: return ErrorCode::UnspecifiedError; } } diff --git a/client/core/sshclient.h b/client/core/sshclient.h index 74c3b724..2ef26fb1 100644 --- a/client/core/sshclient.h +++ b/client/core/sshclient.h @@ -2,29 +2,29 @@ #define SSHCLIENT_H #include +#include #include #include -#include #include "defs.h" using namespace amnezia; namespace libssh { - enum SftpOverwriteMode { + enum ScpOverwriteMode { /*! Overwrite any existing files */ - SftpOverwriteExisting = O_TRUNC, + ScpOverwriteExisting = O_TRUNC, /*! Append new content if the file already exists */ - SftpAppendToExisting = O_APPEND + ScpAppendToExisting = O_APPEND }; class Client : public QObject { Q_OBJECT public: - Client(QObject *parent = nullptr); - ~Client(); + Client() = default; + ~Client() = default; ErrorCode connectToHost(const ServerCredentials &credentials); void disconnectFromHost(); @@ -32,26 +32,26 @@ namespace libssh { const std::function &cbReadStdOut, const std::function &cbReadStdErr); ErrorCode writeResponse(const QString &data); - ErrorCode sftpFileCopy(const SftpOverwriteMode overwriteMode, + ErrorCode scpFileCopy(const ScpOverwriteMode overwriteMode, const QString &localPath, const QString &remotePath, - const QString& fileDesc); + const QString &fileDesc); ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey, const std::function &passphraseCallback); private: ErrorCode closeChannel(); - ErrorCode closeSftpSession(); + void closeScpSession(); ErrorCode fromLibsshErrorCode(); - ErrorCode fromLibsshSftpErrorCode(int errorCode); + ErrorCode fromFileErrorCode(QFileDevice::FileError fileError); static int callback(const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata); ssh_session m_session = nullptr; ssh_channel m_channel = nullptr; - sftp_session m_sftpSession = nullptr; + ssh_scp m_scpSession = nullptr; static std::function m_passphraseCallback; signals: void writeToChannelFinished(); - void sftpFileCopyFinished(); + void scpFileCopyFinished(); }; } From 9201ca1e0353bcea2f5bf9d6c5b9414dcac3e98e Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Wed, 6 Mar 2024 14:22:44 +0500 Subject: [PATCH 09/28] fixed screen fade when switching from PageSetupWizardStart to PageStart --- client/ui/qml/Pages2/PageSetupWizardStart.qml | 12 +--------- client/ui/qml/Pages2/PageStart.qml | 22 +++++++------------ client/ui/qml/main2.qml | 15 +++++++++++++ 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/client/ui/qml/Pages2/PageSetupWizardStart.qml b/client/ui/qml/Pages2/PageSetupWizardStart.qml index 698eacb3..89d4dc12 100644 --- a/client/ui/qml/Pages2/PageSetupWizardStart.qml +++ b/client/ui/qml/Pages2/PageSetupWizardStart.qml @@ -22,10 +22,6 @@ PageType { PageController.goToPage(PageEnum.PageSetupWizardViewConfig) } - function onShowBusyIndicator(visible) { - busyIndicator.visible = visible - } - function onClosePage() { if (stackView.depth <= 1) { return @@ -53,7 +49,7 @@ PageType { } function onEscapePressed() { - if (isControlsDisabled || busyIndicator.visible) { + if (isControlsDisabled) { return } @@ -159,10 +155,4 @@ PageType { ConnectionTypeSelectionDrawer { id: connectionTypeSelection } - - BusyIndicatorType { - id: busyIndicator - anchors.centerIn: parent - z: 1 - } } diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index ad007b48..3fd2d73c 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -14,6 +14,8 @@ import "../Components" PageType { id: root + property bool isControlsDisabled: false + Connections { target: PageController @@ -32,14 +34,8 @@ PageType { tabBarStackView.push(pagePath, { "objectName" : pagePath }, StackView.PushTransition) } - function onShowBusyIndicator(visible) { - busyIndicator.visible = visible - tabBarStackView.enabled = !visible - tabBar.enabled = !visible - } - function onDisableControls(disabled) { - tabBar.enabled = !disabled + isControlsDisabled = disabled } function onClosePage() { @@ -67,7 +63,7 @@ PageType { } function onEscapePressed() { - if (!tabBar.enabled || busyIndicator.visible) { + if (root.isControlsDisabled) { return } @@ -136,6 +132,8 @@ PageType { width: parent.width height: root.height - tabBar.implicitHeight + enabled: !root.isControlsDisabled + function goToTabBarPage(page) { connectionTypeSelection.close() @@ -165,6 +163,8 @@ PageType { leftPadding: 96 rightPadding: 96 + enabled: !root.isControlsDisabled + background: Shape { width: parent.width height: parent.height @@ -236,12 +236,6 @@ PageType { } } - BusyIndicatorType { - id: busyIndicator - anchors.centerIn: parent - z: 1 - } - ConnectionTypeSelectionDrawer { id: connectionTypeSelection diff --git a/client/ui/qml/main2.qml b/client/ui/qml/main2.qml index bfecf46b..101ee06d 100644 --- a/client/ui/qml/main2.qml +++ b/client/ui/qml/main2.qml @@ -86,6 +86,11 @@ Window { function onGoToPageSettingsBackup() { PageController.goToPage(PageEnum.PageSettingsBackup) } + + function onShowBusyIndicator(visible) { + busyIndicator.visible = visible + PageController.disableControls(visible) + } } Connections { @@ -215,6 +220,16 @@ Window { } } + Item { + anchors.fill: parent + + BusyIndicatorType { + id: busyIndicator + anchors.centerIn: parent + z: 1 + } + } + function showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) { questionDrawer.headerText = headerText questionDrawer.descriptionText = descriptionText From bca68fc1855c04ed6e244cb22156927cd7eb4bf2 Mon Sep 17 00:00:00 2001 From: pokamest Date: Wed, 6 Mar 2024 10:07:49 -0800 Subject: [PATCH 10/28] iOS crash fix --- client/amnezia_application.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 7ddc8878..cca8032b 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -140,7 +140,10 @@ void AmneziaApplication::init() m_settingsController->importBackupFromOutside(filePath); }); - AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); + QTimer::singleShot(0, this, [this](){ + AmneziaVPN::toggleScreenshots(m_settings->isScreenshotsEnabled()); + }); + connect(m_settings.get(), &Settings::screenshotsEnabledChanged, [](bool enabled) { AmneziaVPN::toggleScreenshots(enabled); }); From da84ba1a4dd05e34d74dbc39d831d742eca0d3c9 Mon Sep 17 00:00:00 2001 From: pokamest Date: Wed, 6 Mar 2024 18:34:07 +0000 Subject: [PATCH 11/28] Text fixes and some ts updates --- client/translations/amneziavpn_ar.ts | 240 ++++++++-------- client/translations/amneziavpn_fa_IR.ts | 234 +++++++++------- client/translations/amneziavpn_my_MM.ts | 236 +++++++++------- client/translations/amneziavpn_ru.ts | 257 ++++++++++-------- client/translations/amneziavpn_zh_CN.ts | 234 +++++++++------- .../Pages2/PageSetupWizardConfigSource.qml | 3 +- .../qml/Pages2/PageSetupWizardViewConfig.qml | 2 +- 7 files changed, 674 insertions(+), 532 deletions(-) diff --git a/client/translations/amneziavpn_ar.ts b/client/translations/amneziavpn_ar.ts index dde6bad6..48932cbd 100644 --- a/client/translations/amneziavpn_ar.ts +++ b/client/translations/amneziavpn_ar.ts @@ -5,45 +5,45 @@ ConnectionController - - - + + + Connect اتصل - + VPN Protocols is not installed. Please install VPN container at first لم يتم تثبيت بروتوكولات VPN, من فضلك قم بتنزيل حاوية VPN اولاً - + Connection... اتصال... - + Connected تم الاتصال - + Reconnection... إعادة الاتصال... - + Disconnection... إنهاء الاتصال... - + Settings updated successfully, Reconnnection... تم تحديث الاعدادات بنجاح, جاري إعادة الاتصال... - + Settings updated successfully تم تحديث الاعدادات بنجاح @@ -324,17 +324,17 @@ Already installed containers were found on the server. All installed containers - + VPN protocol بروتوكول VPN - + Servers الخوادم - + Unable change server while there is an active connection لا يمكن تغير الخادم بينما هناك اتصال مفعل @@ -1703,41 +1703,45 @@ And if you don't like the app, all the more support it - the donation will اتصال الخادم - Do not use connection code from public sources. It may have been created to intercept your data. It's okay as long as it's from someone you trust. - لا تستخدم رمز الاتصال من المصادر العامة. ربما تم إنشاؤه لاعتراض بياناتك + لا تستخدم رمز الاتصال من المصادر العامة. ربما تم إنشاؤه لاعتراض بياناتك لا بأس طالما انه من شخص تثق به. - + + Do not use connection codes from untrusted sources, as they may be created to intercept your data. + + + + What do you have? ماذا لديك؟ - + File with connection settings or backup ملف إعدادات اتصال او نسخ احتياطي - + File with connection settings ملف إعدادات اتصال - + Open config file افتح ملف تكوين - + QR-code رمز QR - + Key as text مفتاح كنص @@ -1823,7 +1827,7 @@ It's okay as long as it's from someone you trust. واصل - + Set up later إعداد في وقت لاحق @@ -1924,32 +1928,32 @@ It's okay as long as it's from someone you trust. PageSetupWizardStart - + Settings restored from backup file تم استرداد الإعدادات من ملف نسخة احتياطية - + Free service for creating a personal VPN on your server. خدمة مجانية لأنشاء VPN شخصي علي الخادم الشخصي. - + Helps you access blocked content without revealing your privacy, even to VPN providers. يساعدك في الولوج للمحتوي المحظور بدون إظهار خصوصيات, حتي لمزود ال VPN. - + I have the data to connect لدي البيانات المطلوبة للأتصال - + I have nothing ليس لدي اي شئ - + https://amnezia.org/instructions/0_starter-guide @@ -1991,9 +1995,13 @@ It's okay as long as it's from someone you trust. اتصال جديد - Do not use connection code from public sources. It could be created to intercept your data. - لا تستخدم رمز الاتصال من مصادر مفتوحة, قد تكون مصنوعة للتعارض مع بياناتك. + لا تستخدم رمز الاتصال من مصادر مفتوحة, قد تكون مصنوعة للتعارض مع بياناتك. + + + + Do not use connection codes from untrusted sources, as they may be created to intercept your data. + @@ -2085,7 +2093,7 @@ It's okay as long as it's from someone you trust. - + Users المستخدمين @@ -2095,52 +2103,52 @@ It's okay as long as it's from someone you trust. شارك اتصال VPN بدون القدرة علي إدارة الخادم - + Search ابحث - + Creation date: تاريخ الإنشاء: - + 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 إلغاء @@ -2173,8 +2181,8 @@ It's okay as long as it's from someone you trust. البروتوكولات - - + + Protocol بروتوكول @@ -2194,14 +2202,14 @@ It's okay as long as it's from someone you trust. اسم المستخدم - - + + Connection format تنسيق الاتصال - + Share شارك @@ -2583,147 +2591,161 @@ It's okay as long as it's from someone you trust. انتهت مدة الاتصال بالخادم - - Sftp error: End-of-file encountered - - - - Sftp error: File does not exist - خطأ Sftp: الملف غير موجود + خطأ Sftp: الملف غير موجود - Sftp error: Permission denied - خطأ Sftp: تم حظر الصلحيات + خطأ Sftp: تم حظر الصلحيات - Sftp error: Generic failure - خطأ Sftp: فشل عام + خطأ Sftp: فشل عام - Sftp error: Garbage received from server - خطأ Sftp: تم استلام نفايات من الخادم + خطأ Sftp: تم استلام نفايات من الخادم - Sftp error: No connection has been set up - خطأ Sftp: لم يتم إعداد اتصال + خطأ Sftp: لم يتم إعداد اتصال - Sftp error: There was a connection, but we lost it - خطأ Sftp: كان هناك اتصال, ولكن خسرناه + خطأ Sftp: كان هناك اتصال, ولكن خسرناه - Sftp error: Operation not supported by libssh yet - خطأ Sftp: العملية ليست مدعومة من libssh بعد + خطأ Sftp: العملية ليست مدعومة من libssh بعد - - Sftp error: Invalid file handle - - - - Sftp error: No such file or directory path exists - خطأ Sftp: لا يوجد مسار ملف او مجلد مثل هذا + خطأ Sftp: لا يوجد مسار ملف او مجلد مثل هذا - Sftp error: An attempt to create an already existing file or directory has been made - خطأ Sftp: محاولة إنشاء ملف او مجلد موجود بالفعل + خطأ Sftp: محاولة إنشاء ملف او مجلد موجود بالفعل - Sftp error: Write-protected filesystem - خطأ Sftp: نظام كتابة الملفات محمي + خطأ Sftp: نظام كتابة الملفات محمي - Sftp error: No media was in remote drive - خطأ Sftp: لا يوجد وسائط في القرص البعيد + خطأ Sftp: لا يوجد وسائط في القرص البعيد - + VPN connection error - + Error when retrieving configuration from API خطأ عند استرداد التكوين من API - + This config has already been added to the application هذا التكوين بالفعل تمت إضافتة للبرنامج - + ErrorCode: %1. - + OpenVPN config missing OpenVpn تكوين مفقود - + + Scp error: Generic failure + + + + OpenVPN management server error OpenVpn خطأ في إدارة الخادم - + OpenVPN executable missing OpenVpn executeable مفقود - + ShadowSocks (ss-local) executable missing ShadowSocks (ss-local) executable مفقود - + Cloak (ck-client) executable missing Cloak (ck-client) executable مفقود - + Amnezia helper service error خطأ في خدمة مٌساعد Amnezia - + OpenSSL failed فشل OpenSSL - + Can't connect: another VPN connection is active لا يمكن الاتصال: هناك اتصال VPN اخر بالفعل يعمل - + Can't setup OpenVPN TAP network adapter لا يمك نتثبيت محول شبكة OpenVPN TAP - + VPN pool error: no available addresses VPN pool error: لا يوجد عنواين مٌتاحة - + The config does not contain any containers and credentials for connecting to the server التكوين لا يحتوي علي اي حاويات و اعتماد للأتصال بالخادم - + + QFile error: The file could not be opened + + + + + QFile error: An error occurred when reading from the file + + + + + 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 خطأ داخلي @@ -3023,8 +3045,8 @@ While it offers a blend of security, stability, and speed, it's essential t خادم #1 - - + + Server خادم @@ -3032,17 +3054,17 @@ While it offers a blend of security, stability, and speed, it's essential t SettingsController - + Backup file is corrupted ملف النسخه الاحتياطيه تالف - + All settings have been reset to default values تم استرجاع جميع الإعدادات للإعدادات الافتراضية - + Cached profiles cleared تم حذف الملفات الشخصية المٌخزنة مؤقتاُ @@ -3056,28 +3078,28 @@ While it offers a blend of security, stability, and speed, it's essential t احفظ تكوين AmneziaVPN - + Share شارك - + Copy انسخ - - + + Copied تم النسخ - + Copy config string انسخ نص التكوين - + Show connection settings اظهر إعدادات الاتصال @@ -3086,7 +3108,7 @@ While it offers a blend of security, stability, and speed, it's essential t 展示内容 - + To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file" حتي تقرأ رمز ال QR في تطبيق Amnezia, اختار "إضافة خادم" - "لدي بيانات الاتصال" - "رمز Qr, او مفتاح تعريف او ملف إعدادات" @@ -3178,7 +3200,7 @@ While it offers a blend of security, stability, and speed, it's essential t VpnConnection - + Mbps @@ -3262,12 +3284,12 @@ While it offers a blend of security, stability, and speed, it's essential t main2 - + Private key passphrase عبارة المرور الخاصة بالمفتاح - + Save احفظ diff --git a/client/translations/amneziavpn_fa_IR.ts b/client/translations/amneziavpn_fa_IR.ts index b5caf979..1c632bf3 100644 --- a/client/translations/amneziavpn_fa_IR.ts +++ b/client/translations/amneziavpn_fa_IR.ts @@ -34,48 +34,48 @@ ConnectionController - + VPN Protocols is not installed. Please install VPN container at first پروتکل وی‎پی‎ان نصب نشده است لطفا کانتینر وی‎پی‎ان را نصب کنید - + Connection... در حال ارتباط... - + Connected متصل - + Settings updated successfully, Reconnnection... تنظیمات به روز رسانی شد در حال اتصال دوباره... - + Settings updated successfully تنظیمات با موفقیت به‎روز‎رسانی شدند - + Reconnection... اتصال دوباره... - - - + + + Connect اتصال - + Disconnection... قطع ارتباط... @@ -335,17 +335,17 @@ Already installed containers were found on the server. All installed containers تونل تقسیم‌شده غیرفعال شده - + VPN protocol پروتکل وی‎پی‎ان - + Servers سرورها - + Unable change server while there is an active connection امکان تغییر سرور در هنگام متصل بودن وجود ندارد @@ -1715,41 +1715,45 @@ Already installed containers were found on the server. All installed containers ارتباط سرور - Do not use connection code from public sources. It may have been created to intercept your data. It's okay as long as it's from someone you trust. - از کد اتصالاتی که در منابع عمومی هستند استفاده نکنید. ممکن است برای شنود اطلاعات شما ایجاد شده باشند. + از کد اتصالاتی که در منابع عمومی هستند استفاده نکنید. ممکن است برای شنود اطلاعات شما ایجاد شده باشند. ایرادی ندارد که از طرف کسی باشد که به او اعتماد دارید. - + + Do not use connection codes from untrusted sources, as they may be created to intercept your data. + + + + What do you have? چی داری؟ - + File with connection settings فایل شامل تنظیمات اتصال - + File with connection settings or backup فایل شامل تنظیمات اتصال یا بک‎آپ - + Open config file باز کردن فایل تنظیمات - + QR-code QR-Code - + Key as text متن شامل کلید @@ -1852,7 +1856,7 @@ and will not be shared or disclosed to the Amnezia or any third parties ادامه - + Set up later بعدا تنظیم شود @@ -1953,32 +1957,32 @@ and will not be shared or disclosed to the Amnezia or any third parties PageSetupWizardStart - + Settings restored from backup file تنظیمات از فایل بک‎آپ بازیابی شدند - + Free service for creating a personal VPN on your server. سرویس رایگان برای ایجاد وی‎پی‎ان شخصی بر روی سرور خودتان. - + Helps you access blocked content without revealing your privacy, even to VPN providers. به شما کمک می‎کند که بدون فاش کردن حریم شخصی خودتان حتی برای ارائه دهنده وی‎پی‎ان به محتوای مسدود شده دسترسی پیدا کنید. - + I have the data to connect من داده برای اتصال دارم - + I have nothing من هیچی ندارم - + https://amnezia.org/instructions/0_starter-guide @@ -2019,9 +2023,13 @@ and will not be shared or disclosed to the Amnezia or any third parties ارتباط جدید - Do not use connection code from public sources. It could be created to intercept your data. - از کد اتصالی که در منابع عمومی هست استفاده نکنید. ممکن است برای شنود اطلاعات شما ایجاد شده باشد. + از کد اتصالی که در منابع عمومی هست استفاده نکنید. ممکن است برای شنود اطلاعات شما ایجاد شده باشد. + + + + Do not use connection codes from untrusted sources, as they may be created to intercept your data. + @@ -2159,7 +2167,7 @@ and will not be shared or disclosed to the Amnezia or any third parties - + Users کاربران @@ -2169,37 +2177,37 @@ and will not be shared or disclosed to the Amnezia or any third parties نام کاربری - + Search جستجو - + Creation date: تاریخ ایجاد: - + Rename تغییر نام - + Client name نام کلاینت - + Save ذخیره - + Revoke ابطال - + Revoke the config for a user - %1? لغو پیکربندی برای یک کاربر %1؟ @@ -2208,17 +2216,17 @@ and will not be shared or disclosed to the Amnezia or any third parties ابطال تنظیمات برای کاربر - + The user will no longer be able to connect to your server. کاربر دیگر نمی‎تواند به سرور وصل شود. - + Continue ادامه - + Cancel کنسل @@ -2236,20 +2244,20 @@ and will not be shared or disclosed to the Amnezia or any third parties به اشتراک گذاری دسترسی به مدیریت سرور. کاربری که دسترسی کامل سرور با او به اشتراک گذاشته می‎شود می‎تواند پروتکل‌‎ها و سرویس‎ها را در سرور حذف یا اضافه کند و یا تنظیمات سرور را تغییر دهد. - - + + Protocol پروتکل - - + + Connection format فرمت ارتباط - + Share اشتراک‎گذاری @@ -2627,91 +2635,113 @@ and will not be shared or disclosed to the Amnezia or any third parties + Scp error: Generic failure + + + Sftp error: End-of-file encountered - Sftp error: End-of-file encountered + Sftp error: End-of-file encountered - Sftp error: File does not exist - Sftp error: File does not exist + Sftp error: File does not exist - Sftp error: Permission denied - Sftp error: Permission denied + Sftp error: Permission denied - Sftp error: Generic failure - Sftp error: Generic failure + Sftp error: Generic failure - Sftp error: Garbage received from server - Sftp error: Garbage received from server + Sftp error: Garbage received from server - Sftp error: No connection has been set up - Sftp error: No connection has been set up + Sftp error: No connection has been set up - Sftp error: There was a connection, but we lost it - Sftp error: There was a connection, but we lost it + Sftp error: There was a connection, but we lost it - Sftp error: Operation not supported by libssh yet - Sftp error: Operation not supported by libssh yet + Sftp error: Operation not supported by libssh yet - Sftp error: Invalid file handle - Sftp error: Invalid file handle + Sftp error: Invalid file handle - Sftp error: No such file or directory path exists - Sftp error: No such file or directory path exists + Sftp error: No such file or directory path exists - Sftp error: An attempt to create an already existing file or directory has been made - Sftp error: An attempt to create an already existing file or directory has been made + Sftp error: An attempt to create an already existing file or directory has been made - Sftp error: Write-protected filesystem - Sftp error: Write-protected filesystem + Sftp error: Write-protected filesystem - Sftp error: No media was in remote drive - Sftp error: No media was in remote drive + Sftp error: No media was in remote drive - + The config does not contain any containers and credentials for connecting to the server تنظیمات شامل هیچ کانتینر یا اعتبارنامه‎ای برای اتصال به سرور نیست - + VPN connection error - + Error when retrieving configuration from API - + This config has already been added to the application - + + QFile error: The file could not be opened + + + + + QFile error: An error occurred when reading from the file + + + + + 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. @@ -2720,52 +2750,52 @@ and will not be shared or disclosed to the Amnezia or any third parties Failed to save config to disk - + OpenVPN config missing OpenVPN config missing - + OpenVPN management server error OpenVPN management server error - + OpenVPN executable missing OpenVPN executable missing - + ShadowSocks (ss-local) executable missing ShadowSocks (ss-local) executable missing - + Cloak (ck-client) executable missing Cloak (ck-client) executable missing - + Amnezia helper service error Amnezia helper service error - + OpenSSL failed OpenSSL failed - + Can't connect: another VPN connection is active Can't connect: another VPN connection is active - + Can't setup OpenVPN TAP network adapter Can't setup OpenVPN TAP network adapter - + VPN pool error: no available addresses VPN pool error: no available addresses @@ -2774,7 +2804,7 @@ and will not be shared or disclosed to the Amnezia or any third parties The config does not contain any containers and credentiaks for connecting to the server - + Internal error Internal error @@ -3153,8 +3183,8 @@ This means that AmneziaWG keeps the fast performance of the original while addin Server #1 - - + + Server Server @@ -3162,17 +3192,17 @@ This means that AmneziaWG keeps the fast performance of the original while addin SettingsController - + All settings have been reset to default values تمام تنظیمات به مقادیر پیش فرض ریست شد - + Cached profiles cleared پروفایل ذخیره شده پاک شد - + Backup file is corrupted فایل بک‎آپ خراب شده است @@ -3186,33 +3216,33 @@ This means that AmneziaWG keeps the fast performance of the original while addin ذخیره تنظیمات AmneziaVPN - + Share اشتراک‎گذاری - + Copy کپی - - + + Copied کپی شد - + Copy config string کپی‎کردن متن تنظیمات - + Show connection settings نمایش تنظیمات ارتباط - + To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file" برای خواندن QR Code در نرم‎افزار AmneziaVPN "اضافه کردن سرور" -> "من داده برای اتصال دارم" -> "QR Code، کلید یا فایل تنظیمات" @@ -3304,7 +3334,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin VpnConnection - + Mbps Mbps @@ -3408,12 +3438,12 @@ This means that AmneziaWG keeps the fast performance of the original while addin main2 - + Private key passphrase عبارت کلید خصوصی - + Save ذخیره diff --git a/client/translations/amneziavpn_my_MM.ts b/client/translations/amneziavpn_my_MM.ts index d1ab6f5c..6fd3a0a6 100644 --- a/client/translations/amneziavpn_my_MM.ts +++ b/client/translations/amneziavpn_my_MM.ts @@ -4,47 +4,47 @@ ConnectionController - + VPN Protocols is not installed. Please install VPN container at first VPN ပရိုတိုကောများကို မထည့်သွင်းရသေးပါ။ ကျေးဇူးပြု၍ VPN ကွန်တိန်နာကို အရင်ထည့်သွင်းပါ။ - + Connection... ချိတ်ဆက်နေပါပြီ... - + Connected ချိတ်ဆက်ပြီးသွားပါပြီ - + Settings updated successfully, Reconnnection... ဆက်တင်များကို အောင်မြင်စွာ အပ်ဒိတ်လုပ်ပြီးပါပြီ၊ ပြန်လည်ချိတ်ဆက်နေပါသည်... - + Settings updated successfully ဆက်တင်များကို အောင်မြင်စွာ အပ်ဒိတ်လုပ်ပြီးပါပြီ။ - + Reconnection... ပြန်လည်ချိတ်ဆက်နေပါသည်... - - - + + + Connect ချိတ်ဆက်မည် - + Disconnection... အဆက်အသွယ်ဖြတ်နေပါသည်... @@ -301,17 +301,17 @@ Already installed containers were found on the server. All installed containers split tunnelling ပိတ်ထားပါသည်။ - + VPN protocol VPN ပရိုတိုကော - + Servers ဆာဗာများ - + Unable change server while there is an active connection လက်ရှိချိတ်ဆက်မှုတစ်ခုရှိနေချိန်တွင် ဆာဗာကို ပြောင်းလဲ၍မရပါ။ @@ -1610,41 +1610,45 @@ Already installed containers were found on the server. All installed containers ဆာဗာချိတ်ဆက်မှု - Do not use connection code from public sources. It may have been created to intercept your data. It's okay as long as it's from someone you trust. - အများသူငှာအသုံးပြုသည့် ရင်းမြစ်များမှ ချိတ်ဆက်ကုဒ်ကို မသုံးပါနှင့်.အဆိုပါကုဒ်များသည် သင့်ဒေတာကို ကြားဖြတ်ရယူရန် ဖန်တီးထားခြင်းဖြစ်နိုင်သည်. + အများသူငှာအသုံးပြုသည့် ရင်းမြစ်များမှ ချိတ်ဆက်ကုဒ်ကို မသုံးပါနှင့်.အဆိုပါကုဒ်များသည် သင့်ဒေတာကို ကြားဖြတ်ရယူရန် ဖန်တီးထားခြင်းဖြစ်နိုင်သည်. သင်ယုံကြည်ရတဲ့သူတစ်ယောက်ဆီမှ ရရှိတဲ့ကုဒ်ဖြစ်နေသရွေ့တော့ အဆင်ပြေပါသည်. - + + Do not use connection codes from untrusted sources, as they may be created to intercept your data. + + + + What do you have? သင့်တွင်ဘာရှိပါသလဲ? - + File with connection settings ချိတ်ဆက်မှုဆက်တင်များပါဝင်သောဖိုင် - + File with connection settings or backup ချိတ်ဆက်မှုဆက်တင်များ သို့မဟုတ် အရန်သိမ်းဆည်းထားမှုပါဝင်သောဖိုင် - + Open config file config ဖိုင်ကိုဖွင့်မည် - + QR-code QR-ကုဒ် - + Key as text Key ကိုစာသားအဖြစ် @@ -1730,7 +1734,7 @@ It's okay as long as it's from someone you trust. ဆက်လက်လုပ်ဆောင်မည် - + Set up later နောက်မှ သတ်မှတ်မည် @@ -1831,32 +1835,32 @@ It's okay as long as it's from someone you trust. PageSetupWizardStart - + Settings restored from backup file ဆက်တင်များကို အရန်သိမ်းဆည်းထားသောဖိုင်မှ ပြန်လည်ရယူပြီးပါပြီ - + Free service for creating a personal VPN on your server. သင့်ဆာဗာပေါ်တွင် ကိုယ်ပိုင် VPN ဖန်တီးရန်အတွက် အခမဲ့ဝန်ဆောင်မှု. - + Helps you access blocked content without revealing your privacy, even to VPN providers. အခြား VPN ဝန်ဆောင်မှုများကိုပင် သင်၏ privacy ကိုမဖော်ပြဘဲ ပိတ်ဆို့ထားသော အကြောင်းအရာများကို သင်ဝင်ရောက်ကြည့်ရှုနိုင်ရန် အကူအညီပေးပါသည်. - + I have the data to connect ကျွန်ုပ်တွင်ချိတ်ဆက်ဖို့အတွက်ဒေတာရှိသည် - + I have nothing ကျွန်ုပ်တွင်ဘာမှမရှိပါ - + https://amnezia.org/instructions/0_starter-guide https://amnezia.org/instructions/0_starter-guide @@ -1897,9 +1901,13 @@ It's okay as long as it's from someone you trust. ချိတ်ဆက်မှုအသစ် - Do not use connection code from public sources. It could be created to intercept your data. - အများသူငှာအသုံးပြုသည့် ရင်းမြစ်များမှ ချိတ်ဆက်ကုဒ်ကို မသုံးပါနှင့်.အဆိုပါကုဒ်များသည် သင့်ဒေတာကို ကြားဖြတ်ရယူရန် ဖန်တီးထားခြင်းဖြစ်နိုင်သည်. + အများသူငှာအသုံးပြုသည့် ရင်းမြစ်များမှ ချိတ်ဆက်ကုဒ်ကို မသုံးပါနှင့်.အဆိုပါကုဒ်များသည် သင့်ဒေတာကို ကြားဖြတ်ရယူရန် ဖန်တီးထားခြင်းဖြစ်နိုင်သည်. + + + + Do not use connection codes from untrusted sources, as they may be created to intercept your data. + @@ -2017,7 +2025,7 @@ It's okay as long as it's from someone you trust. - + Users အသုံးပြုသူများ @@ -2027,52 +2035,52 @@ It's okay as long as it's from someone you trust. အသုံးပြုသူနာမည် - + Search ရှာဖွေမည် - + Creation date: ဖန်တီးပြုလုပ်သည့်ရက်စွဲ: - + 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 ပယ်ဖျက်မည် @@ -2082,20 +2090,20 @@ It's okay as long as it's from someone you trust. ဆာဗာကို စီမံခန့်ခွဲနိုင်စွမ်းမပါရှိဘဲ VPN အသုံးပြုခွင့်ကို မျှဝေမည် - - + + Protocol ပရိုတိုကော - - + + Connection format ချိတ်ဆက်မှုဖောမတ် - + Share မျှဝေမည် @@ -2472,147 +2480,169 @@ It's okay as long as it's from someone you trust. ဆာဗာသို့ ချိတ်ဆက်ခြင်း အချိန်ကုန်သွားသည် - Sftp error: End-of-file encountered - Sftp မှားယွင်းမှု: ဖိုင်အဆုံးသတ်ကို ကြုံတွေ့ခဲ့ရသည် + Sftp မှားယွင်းမှု: ဖိုင်အဆုံးသတ်ကို ကြုံတွေ့ခဲ့ရသည် - Sftp error: File does not exist - Sftp မှားယွင်းမှု: ဖိုင်မရှိပါ + Sftp မှားယွင်းမှု: ဖိုင်မရှိပါ - Sftp error: Permission denied - Sftp မှားယွင်းမှု: ခွင့်ပြုချက် ငြင်းဆိုခံလိုက်ရပါသည် + Sftp မှားယွင်းမှု: ခွင့်ပြုချက် ငြင်းဆိုခံလိုက်ရပါသည် - Sftp error: Generic failure - Sftp မှားယွင်းမှု: ယေဘုယ မအောင်မြင်ခြင်း + Sftp မှားယွင်းမှု: ယေဘုယ မအောင်မြင်ခြင်း - Sftp error: Garbage received from server - မှားယွင်းမှု: ဆာဗာမှ အမှိုက်များကို လက်ခံရရှိခဲ့သည် + မှားယွင်းမှု: ဆာဗာမှ အမှိုက်များကို လက်ခံရရှိခဲ့သည် - Sftp error: No connection has been set up - Sftp မှားယွင်းမှု: ချိတ်ဆက်မှု မသတ်မှတ်ရသေးပါ + Sftp မှားယွင်းမှု: ချိတ်ဆက်မှု မသတ်မှတ်ရသေးပါ - Sftp error: There was a connection, but we lost it - Sftp မှားယွင်းမှု: ချိတ်ဆက်မှုတစ်ခုရှိခဲ့သော်လည်း ဆုံးရှုံးသွားခဲ့ပါသည် + Sftp မှားယွင်းမှု: ချိတ်ဆက်မှုတစ်ခုရှိခဲ့သော်လည်း ဆုံးရှုံးသွားခဲ့ပါသည် - Sftp error: Operation not supported by libssh yet - Sftp အမှား: လုပ်ဆောင်ချက်ကို libssh မှ မထောက်ပံ့သေးပါ + Sftp အမှား: လုပ်ဆောင်ချက်ကို libssh မှ မထောက်ပံ့သေးပါ - Sftp error: Invalid file handle - Sftp မှားယွင်းမှု: ဖိုင်ကိုင်တွယ်မှု မမှန်ကန်ပါ + Sftp မှားယွင်းမှု: ဖိုင်ကိုင်တွယ်မှု မမှန်ကန်ပါ - Sftp error: No such file or directory path exists - Sftp မှားယွင်းမှု: ဤဖိုင်အမျိုးအစား သို့မဟုတ် လမ်းညွှန်လမ်းကြောင်းမျိုး မရှိပါ + Sftp မှားယွင်းမှု: ဤဖိုင်အမျိုးအစား သို့မဟုတ် လမ်းညွှန်လမ်းကြောင်းမျိုး မရှိပါ - Sftp error: An attempt to create an already existing file or directory has been made - Sftp မှားယွင်းမှု: ရှိပြီးသား ဖိုင် သို့မဟုတ် လမ်းညွှန်ကို ဖန်တီးရန် ကြိုးပမ်းမှုတစ်ခု ပြုလုပ်ပြီးဖြစ်သည် + Sftp မှားယွင်းမှု: ရှိပြီးသား ဖိုင် သို့မဟုတ် လမ်းညွှန်ကို ဖန်တီးရန် ကြိုးပမ်းမှုတစ်ခု ပြုလုပ်ပြီးဖြစ်သည် - Sftp error: Write-protected filesystem - Sftp မှားယွင်းမှု: ရေးသားခြင်းမှကာကွယ်ထားသော ဖိုင်စနစ် + Sftp မှားယွင်းမှု: ရေးသားခြင်းမှကာကွယ်ထားသော ဖိုင်စနစ် - Sftp error: No media was in remote drive - Sftp မှားယွင်းမှု: မီဒီယာသည် အဝေးမှ drive ထဲတွင် မရှိခဲ့ပါ + Sftp မှားယွင်းမှု: မီဒီယာသည် အဝေးမှ drive ထဲတွင် မရှိခဲ့ပါ - + The config does not contain any containers and credentials for connecting to the server Config တွင် ဆာဗာသို့ချိတ်ဆက်ရန်အတွက် ကွန်တိန်နာများနှင့် အထောက်အထားများ မပါဝင်ပါ - + Error when retrieving configuration from API API မှ စီစဉ်သတ်မှတ်မှုကို ရယူသည့်အခါ အမှားအယွင်းဖြစ်ပေါ်နေသည် - + This config has already been added to the application ဤ config ကို အပလီကေးရှင်းထဲသို့ ထည့်သွင်းပြီးဖြစ်သည် - + ErrorCode: %1. မှားယွင်းမှုကုတ်: %1. - + OpenVPN config missing OpenVPN config ပျောက်ဆုံးနေပါသည် - + + Scp error: Generic failure + + + + OpenVPN management server error OpenVPN စီမံခန့်ခွဲမှုဆာဗာ အမှားအယွင်း - + OpenVPN executable missing OpenVPN စီမံလုပ်ဆောင်နိုင်မှု ပျောက်ဆုံးနေပါသည် - + ShadowSocks (ss-local) executable missing ShadowSocks (ss-local) executable ပျောက်နေပါသည် - + Cloak (ck-client) executable missing Cloak (ck-client) စီမံလုပ်ဆောင်နိုင်မှု ပျောက်ဆုံးနေပါသည် - + Amnezia helper service error Amnezia helper ဝန်ဆောင်မှု မှားယွင်းမှု - + OpenSSL failed OpenSSL မအောင်မြင်ပါ - + Can't connect: another VPN connection is active ချိတ်ဆက်၍မရပါ: အခြား VPN ချိတ်ဆက်မှုတစ်ခုရှိနေပါသည် - + Can't setup OpenVPN TAP network adapter OpenVPN TAP ကွန်ရက် adapter ကို စနစ်တည်ဆောက်၍မရပါ - + VPN pool error: no available addresses VPN pool မှားယွင်းမှု: ရရှိနိုင်သောလိပ်စာများမရှိပါ - + VPN connection error VPN ချိတ်ဆက်မှုမှားယွင်းနေပါသည် - + + QFile error: The file could not be opened + + + + + QFile error: An error occurred when reading from the file + + + + + 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 စက်တွင်းဖြစ်သော မှားယွင်းမှု @@ -2914,8 +2944,8 @@ WireGuard သည် ၎င်း၏ စွမ်းဆောင်ရည်အ ဆာဗာ #1 - - + + Server ဆာဗာ @@ -2923,17 +2953,17 @@ WireGuard သည် ၎င်း၏ စွမ်းဆောင်ရည်အ SettingsController - + All settings have been reset to default values ဆက်တင်အားလုံးကို မူရင်းတန်ဖိုးများအဖြစ် ပြန်လည်သတ်မှတ်ထားသည် - + Cached profiles cleared ကက်ရှ်ပရိုဖိုင်များကို ရှင်းလင်းပြီးပါပြီ - + Backup file is corrupted အရန်သိမ်းထားသည့်ဖိုင်ပျက်ဆီးနေသည် @@ -2947,33 +2977,33 @@ WireGuard သည် ၎င်း၏ စွမ်းဆောင်ရည်အ AmneziaWG config ကိုသိမ်းဆည်းမည် - + Share မျှဝေမည် - + Copy ကူးယူမည် - - + + Copied ကူးယူပြီးပါပြီ - + Copy config string config string ကိုကူးယူမည် - + Show connection settings ချိတ်ဆက်မှုဆက်တင်များကို ပြပါ - + To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file" Amnezia အက်ပ်ရှိ QR ကုဒ်ကိုဖတ်ရန်အတွက်အောက်ပါအတိုင်း ရွေးချယ်ပါ "ဆာဗာထည့်ရန်" → "ချိတ်ဆက်ရန် ဒေတာရှိသည်" → "QR ကုဒ်၊ key သို့မဟုတ် ဆက်တင်ဖိုင်" @@ -3065,7 +3095,7 @@ WireGuard သည် ၎င်း၏ စွမ်းဆောင်ရည်အ VpnConnection - + Mbps Mbps @@ -3149,12 +3179,12 @@ WireGuard သည် ၎င်း၏ စွမ်းဆောင်ရည်အ main2 - + Private key passphrase ကိုယ်ပိုင် key စကားဝှက် - + Save သိမ်းဆည်းမည် diff --git a/client/translations/amneziavpn_ru.ts b/client/translations/amneziavpn_ru.ts index f770095b..eadc2a14 100644 --- a/client/translations/amneziavpn_ru.ts +++ b/client/translations/amneziavpn_ru.ts @@ -27,47 +27,47 @@ ConnectionController - + VPN Protocols is not installed. Please install VPN container at first VPN протоколы не установлены. Пожалуйста, установите протокол - + Connection... Подключение... - + Connected Подключено - + Settings updated successfully, Reconnnection... Настройки успешно обновлены, Подключение... - + Settings updated successfully - Настройки успешно обновлены. + Настройки успешно обновлены. - + Reconnection... Переподключение... - - - + + + Connect Подключиться - + Disconnection... Отключение... @@ -143,7 +143,7 @@ Split tunneling - Раздельное VPN-туннелирование + Раздельное-туннелирование @@ -153,33 +153,34 @@ Split tunneling on the server - + Раздельное-туннелирование на сервере Enabled Can't be disabled for current server - + Включено. +Не может быть отключено для текущего сервера. Site-based split tunneling - Раздельное туннелирование сайтов + Раздельное туннелирование по сайтам Enabled - Включено + Включено Disabled - Отключено + Отключено App-based split tunneling - Раздельное VPN-туннелирование приложений + Раздельное VPN-туннелирование по приложениям @@ -208,7 +209,7 @@ Can't be disabled for current server Added containers that were already installed on the server - Добавлены сервисы и протоколы, которые были ранее установлены на сервер + Добавлены сервисы и протоколы, которые были ранее установлены на сервер @@ -225,7 +226,7 @@ Already installed containers were found on the server. All installed containers Server '%1' was rebooted - + Сервер '%1' перезагружен @@ -326,17 +327,17 @@ Already installed containers were found on the server. All installed containers Раздельное туннелирование выключено - + VPN protocol VPN протокол - + Servers Серверы - + Unable change server while there is an active connection Невозможно изменить сервер при активном соединении @@ -931,7 +932,7 @@ Already installed containers were found on the server. All installed containers Support Amnezia - Поддержите Amnezia + Поддержите Amnezia @@ -1690,41 +1691,45 @@ Already installed containers were found on the server. All installed containers Подключение к серверу - Do not use connection code from public sources. It may have been created to intercept your data. It's okay as long as it's from someone you trust. - Не используйте код подключения из публичных источников. Его могли создать, чтобы перехватить ваши данные. + Не используйте код подключения из публичных источников. Его могли создать, чтобы перехватить ваши данные. Всё в порядке, если кодом поделился пользователь, которому вы доверяете. - + + Do not use connection codes from untrusted sources, as they may be created to intercept your data. + Не используйте код подключения из недоверенных источников. Его могли создать, чтобы перехватить ваши данные. + + + What do you have? Выберите что у вас есть - + File with connection settings Файл с настройками подключения - + File with connection settings or backup Файл с настройками подключения или бэкап - + Open config file Открыть файл с конфигурацией - + QR-code QR-код - + Key as text Ключ в виде текста @@ -1827,7 +1832,7 @@ and will not be shared or disclosed to the Amnezia or any third parties Продолжить - + Set up later Настроить позднее @@ -1936,32 +1941,32 @@ and will not be shared or disclosed to the Amnezia or any third parties PageSetupWizardStart - + Settings restored from backup file Восстановление настроек из бэкап файла - + Free service for creating a personal VPN on your server. Простое и бесплатное приложение для запуска self-hosted VPN с высокими требованиями к приватности. - + Helps you access blocked content without revealing your privacy, even to VPN providers. Помогает получить доступ к заблокированному контенту, не раскрывая вашу конфиденциальность даже провайдерам VPN. - + I have the data to connect У меня есть данные для подключения - + I have nothing У меня ничего нет - + https://amnezia.org/instructions/0_starter-guide @@ -2002,9 +2007,13 @@ and will not be shared or disclosed to the Amnezia or any third parties Новое соединение - Do not use connection code from public sources. It could be created to intercept your data. - Не используйте код подключения из публичных источников. Его могли создать, чтобы перехватить ваши данные. + Не используйте код подключения из публичных источников. Его могли создать, чтобы перехватить ваши данные. + + + + Do not use connection codes from untrusted sources, as they may be created to intercept your data. + Не используйте код подключения из недоверенных источников. Его могли создать, чтобы перехватить ваши данные. @@ -2138,7 +2147,7 @@ and will not be shared or disclosed to the Amnezia or any third parties - + Users Пользователи @@ -2148,52 +2157,52 @@ and will not be shared or disclosed to the Amnezia or any third parties Имя пользователя - + Search - + Creation date: Дата создания - + 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 Отменить @@ -2207,20 +2216,20 @@ and will not be shared or disclosed to the Amnezia or any third parties Поделиться доступом к VPN, без возможности управления сервером - - + + Protocol Протокол - - + + Connection format Формат подключения - + Share Поделиться @@ -2597,86 +2606,78 @@ and will not be shared or disclosed to the Amnezia or any third parties + Scp error: Generic failure + + + Sftp error: End-of-file encountered - Sftp error: End-of-file encountered + Sftp error: End-of-file encountered - Sftp error: File does not exist - Sftp error: File does not exist + Sftp error: File does not exist - Sftp error: Permission denied - Sftp error: Permission denied + Sftp error: Permission denied - Sftp error: Generic failure - Sftp error: Generic failure + Sftp error: Generic failure - Sftp error: Garbage received from server - Sftp error: Garbage received from server + Sftp error: Garbage received from server - Sftp error: No connection has been set up - Sftp error: No connection has been set up + Sftp error: No connection has been set up - Sftp error: There was a connection, but we lost it - Sftp error: There was a connection, but we lost it + Sftp error: There was a connection, but we lost it - Sftp error: Operation not supported by libssh yet - Sftp error: Operation not supported by libssh yet + Sftp error: Operation not supported by libssh yet - Sftp error: Invalid file handle - Sftp error: Invalid file handle + Sftp error: Invalid file handle - Sftp error: No such file or directory path exists - Sftp error: No such file or directory path exists + Sftp error: No such file or directory path exists - Sftp error: An attempt to create an already existing file or directory has been made - Sftp error: An attempt to create an already existing file or directory has been made + Sftp error: An attempt to create an already existing file or directory has been made - Sftp error: Write-protected filesystem - Sftp error: Write-protected filesystem + Sftp error: Write-protected filesystem - Sftp error: No media was in remote drive - Sftp error: No media was in remote drive + Sftp error: No media was in remote drive - + The config does not contain any containers and credentials for connecting to the server Конфиг не содержит контейнеров и учетных данных для подключения к серверу - + Error when retrieving configuration from API - + This config has already been added to the application - + ErrorCode: %1. @@ -2685,62 +2686,92 @@ and will not be shared or disclosed to the Amnezia or any third parties Failed to save config to disk - + OpenVPN config missing OpenVPN config missing - + OpenVPN management server error OpenVPN management server error - + OpenVPN executable missing OpenVPN executable missing - + ShadowSocks (ss-local) executable missing ShadowSocks (ss-local) executable missing - + Cloak (ck-client) executable missing Cloak (ck-client) executable missing - + Amnezia helper service error Amnezia helper service error - + OpenSSL failed OpenSSL failed - + Can't connect: another VPN connection is active Can't connect: another VPN connection is active - + Can't setup OpenVPN TAP network adapter Can't setup OpenVPN TAP network adapter - + VPN pool error: no available addresses VPN pool error: no available addresses - + VPN connection error - + + QFile error: The file could not be opened + + + + + QFile error: An error occurred when reading from the file + + + + + 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 @@ -3077,8 +3108,8 @@ This means that AmneziaWG keeps the fast performance of the original while addin Server #1 - - + + Server Server @@ -3086,17 +3117,17 @@ This means that AmneziaWG keeps the fast performance of the original while addin SettingsController - + All settings have been reset to default values Все настройки были сброшены к значению "По умолчанию" - + Cached profiles cleared Кэш профиля очищен - + Backup file is corrupted Backup файл поврежден @@ -3110,33 +3141,33 @@ This means that AmneziaWG keeps the fast performance of the original while addin Сохранить config AmneziaVPN - + Share Поделиться - + Copy Скопировать - - + + Copied Скопировано - + Copy config string - + Show connection settings Показать настройки подключения - + To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file" Для считывания QR-кода в приложении Amnezia выберите "Добавить сервер" → "У меня есть данные для подключения" → "QR-код, ключ или файл настроек" @@ -3228,7 +3259,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin VpnConnection - + Mbps Mbps @@ -3332,12 +3363,12 @@ This means that AmneziaWG keeps the fast performance of the original while addin main2 - + Private key passphrase Кодовая фраза для закрытого ключа - + Save Сохранить diff --git a/client/translations/amneziavpn_zh_CN.ts b/client/translations/amneziavpn_zh_CN.ts index 8410f8e2..8d1a1e65 100644 --- a/client/translations/amneziavpn_zh_CN.ts +++ b/client/translations/amneziavpn_zh_CN.ts @@ -20,45 +20,45 @@ ConnectionController - - - + + + Connect 连接 - + VPN Protocols is not installed. Please install VPN container at first 请先安装VPN协议 - + Connection... 连接中 - + Connected 已连接 - + Reconnection... 重连中 - + Disconnection... 断开中 - + Settings updated successfully, Reconnnection... 配置已更新,重连中 - + Settings updated successfully 配置更新成功 @@ -353,17 +353,17 @@ Already installed containers were found on the server. All installed containers 分隔隧道已禁用 - + VPN protocol VPN协议 - + Servers 服务器 - + Unable change server while there is an active connection 已建立连接时无法更改服务器配置 @@ -1797,40 +1797,44 @@ And if you don't like the app, all the more support it - the donation will 服务器连接 - Do not use connection code from public sources. It may have been created to intercept your data. It's okay as long as it's from someone you trust. - 请勿使用公共来源的连接码。它可能是为了拦截您的数据而创建的。 + 请勿使用公共来源的连接码。它可能是为了拦截您的数据而创建的。 请确保连接码来源可信。 - + + Do not use connection codes from untrusted sources, as they may be created to intercept your data. + + + + What do you have? 你用什么方式创建连接? - + File with connection settings or backup 包含连接配置或备份的文件 - + File with connection settings 包含连接配置的文件 - + Open config file 打开配置文件 - + QR-code 二维码 - + Key as text 授权码文本 @@ -1930,7 +1934,7 @@ and will not be shared or disclosed to the Amnezia or any third parties 继续 - + Set up later 稍后设置 @@ -2039,32 +2043,32 @@ and will not be shared or disclosed to the Amnezia or any third parties PageSetupWizardStart - + Settings restored from backup file 从备份文件还原配置 - + Free service for creating a personal VPN on your server. 在您的服务器上架设私人免费VPN服务。 - + Helps you access blocked content without revealing your privacy, even to VPN providers. 帮助您访问受限内容,保护您的隐私,即使是VPN提供商也无法获取。 - + I have the data to connect 我有连接配置 - + I have nothing 我没有 - + https://amnezia.org/instructions/0_starter-guide @@ -2105,9 +2109,13 @@ and will not be shared or disclosed to the Amnezia or any third parties 新连接 - Do not use connection code from public sources. It could be created to intercept your data. - 请勿使用公共来源的连接码。它可以被创建来拦截您的数据。 + 请勿使用公共来源的连接码。它可以被创建来拦截您的数据。 + + + + Do not use connection codes from untrusted sources, as they may be created to intercept your data. + @@ -2199,7 +2207,7 @@ and will not be shared or disclosed to the Amnezia or any third parties - + Users 用户 @@ -2209,52 +2217,52 @@ and will not be shared or disclosed to the Amnezia or any third parties 共享 VPN 访问,无需管理服务器 - + Search - + Creation date: - + 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 取消 @@ -2319,8 +2327,8 @@ and will not be shared or disclosed to the Amnezia or any third parties 协议 - - + + Protocol 协议 @@ -2340,14 +2348,14 @@ and will not be shared or disclosed to the Amnezia or any third parties 用户名 - - + + Connection format 连接格式 - + Share 共享 @@ -2729,86 +2737,108 @@ and will not be shared or disclosed to the Amnezia or any third parties + Scp error: Generic failure + + + Sftp error: End-of-file encountered - Sftp错误: End-of-file encountered + Sftp错误: End-of-file encountered - Sftp error: File does not exist - Sftp错误: 文件不存在 + Sftp错误: 文件不存在 - Sftp error: Permission denied - Sftp错误: 权限不足 + Sftp错误: 权限不足 - Sftp error: Generic failure - Sftp错误: 一般失败 + Sftp错误: 一般失败 - Sftp error: Garbage received from server - Sftp错误: 从服务器收到垃圾信息 + Sftp错误: 从服务器收到垃圾信息 - Sftp error: No connection has been set up - Sftp 错误: 未建立连接 + Sftp 错误: 未建立连接 - Sftp error: There was a connection, but we lost it - Sftp 错误: 已有连接丢失 + Sftp 错误: 已有连接丢失 - Sftp error: Operation not supported by libssh yet - Sftp error: libssh不支持该操作 + Sftp error: libssh不支持该操作 - Sftp error: Invalid file handle - Sftp error: 无效的文件句柄 + Sftp error: 无效的文件句柄 - Sftp error: No such file or directory path exists - Sftp 错误: 文件夹或文件不存在 + Sftp 错误: 文件夹或文件不存在 - Sftp error: An attempt to create an already existing file or directory has been made - Sftp 错误: 文件或目录已存在 + Sftp 错误: 文件或目录已存在 - Sftp error: Write-protected filesystem - Sftp 错误: 文件系统写保护 + Sftp 错误: 文件系统写保护 - Sftp error: No media was in remote drive - Sftp 错误: 远程驱动器中没有媒介 + Sftp 错误: 远程驱动器中没有媒介 - + VPN connection error - + Error when retrieving configuration from API - + This config has already been added to the application - + + QFile error: The file could not be opened + + + + + QFile error: An error occurred when reading from the file + + + + + 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. @@ -2817,57 +2847,57 @@ and will not be shared or disclosed to the Amnezia or any third parties 配置保存到磁盘失败 - + OpenVPN config missing OpenVPN配置丢失 - + OpenVPN management server error OpenVPN 管理服务器错误 - + OpenVPN executable missing OpenVPN 可执行文件丢失 - + ShadowSocks (ss-local) executable missing ShadowSocks (ss-local) 执行文件丢失 - + Cloak (ck-client) executable missing Cloak (ck-client) 执行文件丢失 - + Amnezia helper service error Amnezia 服务连接失败 - + OpenSSL failed OpenSSL错误 - + Can't connect: another VPN connection is active 无法连接:另一个VPN连接处于活跃状态 - + Can't setup OpenVPN TAP network adapter 无法设置 OpenVPN TAP 网络适配器 - + VPN pool error: no available addresses VPN 池错误:没有可用地址 - + The config does not contain any containers and credentials for connecting to the server 配置不包含任何用于连接服务器的容器和凭据 @@ -2876,7 +2906,7 @@ and will not be shared or disclosed to the Amnezia or any third parties 该配置不包含任何用于连接到服务器的容器和凭据。 - + Internal error @@ -3245,8 +3275,8 @@ While it offers a blend of security, stability, and speed, it's essential t - - + + Server 服务器 @@ -3254,17 +3284,17 @@ While it offers a blend of security, stability, and speed, it's essential t SettingsController - + Backup file is corrupted 备份文件已损坏 - + All settings have been reset to default values 所配置恢复为默认值 - + Cached profiles cleared 缓存的配置文件已清除 @@ -3278,28 +3308,28 @@ While it offers a blend of security, stability, and speed, it's essential t 保存配置 - + Share 共享 - + Copy 拷贝 - - + + Copied 已拷贝 - + Copy config string 复制配置字符串 - + Show connection settings 显示连接配置 @@ -3308,7 +3338,7 @@ While it offers a blend of security, stability, and speed, it's essential t 展示内容 - + To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file" 要应用二维码到 Amnezia,请底部工具栏点击“+”→“连接方式”→“二维码、授权码或配置文件” @@ -3400,7 +3430,7 @@ While it offers a blend of security, stability, and speed, it's essential t VpnConnection - + Mbps @@ -3504,12 +3534,12 @@ While it offers a blend of security, stability, and speed, it's essential t main2 - + Private key passphrase 私钥密码 - + Save 保存 diff --git a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml index 519aa6cc..33aee0f1 100644 --- a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml +++ b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml @@ -48,8 +48,7 @@ PageType { Layout.leftMargin: 16 headerText: qsTr("Server connection") - descriptionText: qsTr("Do not use connection code from public sources. It may have been created to intercept your data.\n -It's okay as long as it's from someone you trust.") + descriptionText: qsTr("Do not use connection codes from untrusted sources, as they may be created to intercept your data.") } Header2TextType { diff --git a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml index c755d88d..fa40280b 100644 --- a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml +++ b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml @@ -92,7 +92,7 @@ PageType { Layout.fillWidth: true Layout.topMargin: 16 - text: qsTr("Do not use connection code from public sources. It could be created to intercept your data.") + text: qsTr("Do not use connection codes from untrusted sources, as they may be created to intercept your data.") color: "#878B91" } From 99182f4a677ab5bb206d75236ff7d12fa82ad1b7 Mon Sep 17 00:00:00 2001 From: Andrey Zaharow Date: Wed, 6 Mar 2024 23:04:53 +0100 Subject: [PATCH 12/28] Fix translations --- client/translations/amneziavpn_ar.ts | 6 +++--- client/translations/amneziavpn_fa_IR.ts | 10 +++++----- client/translations/amneziavpn_my_MM.ts | 6 +++--- client/translations/amneziavpn_ru.ts | 14 +++++++------- client/translations/amneziavpn_zh_CN.ts | 10 +++++----- client/ui/qml/Pages2/PageSettingsServerData.qml | 6 +++--- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/client/translations/amneziavpn_ar.ts b/client/translations/amneziavpn_ar.ts index 48932cbd..58a52572 100644 --- a/client/translations/amneziavpn_ar.ts +++ b/client/translations/amneziavpn_ar.ts @@ -1423,7 +1423,7 @@ And if you don't like the app, all the more support it - the donation will - Do you want to clear server from Amnezia software? + Do you want to clear server Amnezia-installed services? هل تريد حذف الخادم من Amnezia? @@ -1471,7 +1471,7 @@ And if you don't like the app, all the more support it - the donation will - Remove server from application + Remove this server from the app احذف خادم من التطبيق @@ -1496,7 +1496,7 @@ And if you don't like the app, all the more support it - the donation will - Clear server from Amnezia software + Clear server Amnezia-installed services احذف خادم من Amnezia diff --git a/client/translations/amneziavpn_fa_IR.ts b/client/translations/amneziavpn_fa_IR.ts index 1c632bf3..7a880048 100644 --- a/client/translations/amneziavpn_fa_IR.ts +++ b/client/translations/amneziavpn_fa_IR.ts @@ -1479,7 +1479,7 @@ Already installed containers were found on the server. All installed containers - Do you want to clear server from Amnezia software? + Do you want to clear server Amnezia-installed services? آیا می‌خواهید سرور را از نرم‌افزار Amnezia پاک کنید؟ @@ -1494,11 +1494,11 @@ Already installed containers were found on the server. All installed containers - Remove server from application + Remove this server from the app حذف کردن سرور از نرم‎افزار - Remove server? + Remove server from application? حذف سرور؟ @@ -1508,11 +1508,11 @@ Already installed containers were found on the server. All installed containers - Clear server from Amnezia software + Clear server Amnezia-installed services پاک کردن سرور از نرم‎افزار Amnezia - Clear server from Amnezia software? + Clear server Amnezia-installed services? سرور از نرم‎افزار Amnezia پاک شود؟ diff --git a/client/translations/amneziavpn_my_MM.ts b/client/translations/amneziavpn_my_MM.ts index 6fd3a0a6..3181920a 100644 --- a/client/translations/amneziavpn_my_MM.ts +++ b/client/translations/amneziavpn_my_MM.ts @@ -1398,7 +1398,7 @@ Already installed containers were found on the server. All installed containers - Do you want to clear server from Amnezia software? + Do you want to clear server Amnezia-installed services? ဆာဗာကို Amnezia ဆော့ဖ်ဝဲလ်မှ ရှင်းလင်းလိုပါသလား? @@ -1413,7 +1413,7 @@ Already installed containers were found on the server. All installed containers - Remove server from application + Remove this server from the app ဆာဗာကို အပလီကေးရှင်းမှဖယ်ရှားမည် @@ -1423,7 +1423,7 @@ Already installed containers were found on the server. All installed containers - Clear server from Amnezia software + Clear server Amnezia-installed services ဆာဗာကို Amnezia ဆော့ဖ်ဝဲလ်မှ ရှင်းလင်းမည် diff --git a/client/translations/amneziavpn_ru.ts b/client/translations/amneziavpn_ru.ts index eadc2a14..a8d78c86 100644 --- a/client/translations/amneziavpn_ru.ts +++ b/client/translations/amneziavpn_ru.ts @@ -1459,7 +1459,7 @@ Already installed containers were found on the server. All installed containers - Do you want to clear server from Amnezia software? + Do you want to clear server Amnezia-installed services? Вы хотите очистить сервер от всех сервисов Amnezia? @@ -1474,12 +1474,12 @@ Already installed containers were found on the server. All installed containers - Remove server from application + Remove this server from the app Удалить сервер из приложения - Remove server? - Удалить сервер? + Remove server from application? + Удалить сервер из приложения? @@ -1488,11 +1488,11 @@ Already installed containers were found on the server. All installed containers - Clear server from Amnezia software + Clear server Amnezia-installed services Очистить сервер от протоколов и сервисов Amnezia - Clear server from Amnezia software? + Clear server Amnezia-installed services? Удалить все сервисы и протоколы Amnezia с сервера? @@ -1776,7 +1776,7 @@ and will not be shared or disclosed to the Amnezia or any third parties Configure your server - Настроить ваш сервер + Настроить свой сервер diff --git a/client/translations/amneziavpn_zh_CN.ts b/client/translations/amneziavpn_zh_CN.ts index 8d1a1e65..e8a87aea 100644 --- a/client/translations/amneziavpn_zh_CN.ts +++ b/client/translations/amneziavpn_zh_CN.ts @@ -1493,7 +1493,7 @@ And if you don't like the app, all the more support it - the donation will - Do you want to clear server from Amnezia software? + Do you want to clear server Amnezia-installed services? 您要清除服务器上的Amnezia软件吗? @@ -1541,7 +1541,7 @@ And if you don't like the app, all the more support it - the donation will - Remove server from application + Remove this server from the app 移除本地服务器信息 @@ -1560,7 +1560,7 @@ And if you don't like the app, all the more support it - the donation will - Remove server? + Remove server from application? 移除本地服务器信息? @@ -1570,11 +1570,11 @@ And if you don't like the app, all the more support it - the donation will - Clear server from Amnezia software + Clear server Amnezia-installed services 清理Amnezia中服务器信息 - Clear server from Amnezia software? + Clear server Amnezia-installed services? 清理Amnezia中服务器信息 diff --git a/client/ui/qml/Pages2/PageSettingsServerData.qml b/client/ui/qml/Pages2/PageSettingsServerData.qml index 96945213..c88cecce 100644 --- a/client/ui/qml/Pages2/PageSettingsServerData.qml +++ b/client/ui/qml/Pages2/PageSettingsServerData.qml @@ -166,7 +166,7 @@ PageType { LabelWithButtonType { Layout.fillWidth: true - text: qsTr("Remove server from application") + text: qsTr("Remove this server from the app") textColor: "#EB5757" clickedFunction: function() { @@ -196,11 +196,11 @@ PageType { visible: content.isServerWithWriteAccess Layout.fillWidth: true - text: qsTr("Clear server from Amnezia software") + text: qsTr("Clear server Amnezia-installed services") textColor: "#EB5757" clickedFunction: function() { - var headerText = qsTr("Do you want to clear server from Amnezia software?") + var headerText = qsTr("Do you want to clear server Amnezia-installed services?") var descriptionText = qsTr("All containers will be deleted on the server. This means that configuration files, keys and certificates will be deleted.") var yesButtonText = qsTr("Continue") var noButtonText = qsTr("Cancel") From ebd3449b4a0df96bb36ebe1fd3a77819fcf322ab Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 7 Mar 2024 09:16:53 +0300 Subject: [PATCH 13/28] extended debug output for api request --- client/ui/controllers/apiController.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ui/controllers/apiController.cpp b/client/ui/controllers/apiController.cpp index 036a580d..f9df5f4b 100644 --- a/client/ui/controllers/apiController.cpp +++ b/client/ui/controllers/apiController.cpp @@ -138,7 +138,10 @@ void ApiController::updateServerConfigFromApi() serverConfig.insert(config_key::defaultContainer, defaultContainer); m_serversModel->editServer(serverConfig, m_serversModel->getDefaultServerIndex()); } else { + QString err = reply->errorString(); + qDebug() << QString::fromUtf8(reply->readAll()); qDebug() << reply->error(); + qDebug() << err; qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); emit errorOccurred(errorString(ApiConfigDownloadError)); m_isConfigUpdateStarted = false; From 36af7cf471f9ee76a60f688a684c2125c78a91bc Mon Sep 17 00:00:00 2001 From: albexk Date: Thu, 7 Mar 2024 14:27:21 +0300 Subject: [PATCH 14/28] Bump Android version code to 47 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 77ec33b3..1b1433e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d") set(RELEASE_DATE "${CURRENT_DATE}") set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) -set(APP_ANDROID_VERSION_CODE 46) +set(APP_ANDROID_VERSION_CODE 47) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(MZ_PLATFORM_NAME "linux") From deaf618520a444b48b267cb898917f4391b92424 Mon Sep 17 00:00:00 2001 From: Artem Romanovich Date: Sat, 9 Mar 2024 00:21:57 +0300 Subject: [PATCH 15/28] added commit hash in logger --- client/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/main.cpp b/client/main.cpp index 2b0e264b..54d26bdb 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -61,7 +61,7 @@ int main(int argc, char *argv[]) if (doExec) { app.init(); - qInfo().noquote() << QString("Started %1 version %2").arg(APPLICATION_NAME, APP_VERSION); + qInfo().noquote() << QString("Started %1 version %2 %3").arg(APPLICATION_NAME, APP_VERSION, GIT_COMMIT_HASH); qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture()); return app.exec(); From cb9a25006ce5cfa73c2b50fb2587cc3f62a1f358 Mon Sep 17 00:00:00 2001 From: Garegin866 Date: Tue, 12 Mar 2024 00:02:47 +0400 Subject: [PATCH 16/28] - Removed additional focus frames for buttons inside text fields. - For mobile platforms, disabled auto-focus on the first element when navigating on the page. --- client/ui/qml/Components/ShareConnectionDrawer.qml | 2 +- client/ui/qml/Controls2/PageType.qml | 4 +++- client/ui/qml/Controls2/TextFieldWithHeaderType.qml | 1 + client/ui/qml/Pages2/PageSettingsServerInfo.qml | 1 + client/ui/qml/Pages2/PageShare.qml | 1 + 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/client/ui/qml/Components/ShareConnectionDrawer.qml b/client/ui/qml/Components/ShareConnectionDrawer.qml index d337242f..1f7db930 100644 --- a/client/ui/qml/Components/ShareConnectionDrawer.qml +++ b/client/ui/qml/Components/ShareConnectionDrawer.qml @@ -40,7 +40,7 @@ DrawerType2 { Connections { target: root - + enabled: !GC.isMobile() function onOpened() { header.forceActiveFocus() } diff --git a/client/ui/qml/Controls2/PageType.qml b/client/ui/qml/Controls2/PageType.qml index 706a32e1..2ca75e9d 100644 --- a/client/ui/qml/Controls2/PageType.qml +++ b/client/ui/qml/Controls2/PageType.qml @@ -2,6 +2,8 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts +import "../Config" + Item { id: root @@ -32,6 +34,6 @@ Item { } } repeat: false // Stop the timer after one trigger - running: true // Start the timer + running: !GC.isMobile() // Start the timer } } diff --git a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml index 606aa83e..6069bc38 100644 --- a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml +++ b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml @@ -137,6 +137,7 @@ Item { // textColor: "#D7D8DB" // borderWidth: 0 + focusPolicy: Qt.NoFocus text: root.buttonText imageSource: root.buttonImageSource diff --git a/client/ui/qml/Pages2/PageSettingsServerInfo.qml b/client/ui/qml/Pages2/PageSettingsServerInfo.qml index dc17d98d..1ace3c3e 100644 --- a/client/ui/qml/Pages2/PageSettingsServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsServerInfo.qml @@ -93,6 +93,7 @@ PageType { Connections { target: serverNameEditDrawer + enabled: !GC.isMobile() function onOpened() { serverName.textField.forceActiveFocus() } diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 44fe145f..70b03d7f 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -648,6 +648,7 @@ PageType { Connections { target: clientNameEditDrawer + enabled: !GC.isMobile() function onOpened() { clientNameEditor.textField.forceActiveFocus() } From 78c7893f90026f77b20e1ed737e923d9551aa1c0 Mon Sep 17 00:00:00 2001 From: albexk Date: Tue, 12 Mar 2024 17:17:38 +0300 Subject: [PATCH 17/28] Remove shadowsocks libs from Android build --- client/cmake/android.cmake | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/cmake/android.cmake b/client/cmake/android.cmake index 9458bd08..ec47a38b 100644 --- a/client/cmake/android.cmake +++ b/client/cmake/android.cmake @@ -45,9 +45,6 @@ foreach(abi IN ITEMS ${QT_ANDROID_ABIS}) ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/amneziawg/android/${abi}/libwg.so ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/amneziawg/android/${abi}/libwg-go.so ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/amneziawg/android/${abi}/libwg-quick.so - ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/shadowsocks/android/${abi}/libredsocks.so - ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/shadowsocks/android/${abi}/libsslocal.so - ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/shadowsocks/android/${abi}/libtun2socks.so ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/libck-ovpn-plugin.so ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/libovpn3.so ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/libovpnutil.so From fceccaefcc22a8dddea0f22659bc12ced20c70d8 Mon Sep 17 00:00:00 2001 From: Igor Sorokin Date: Tue, 12 Mar 2024 19:57:45 +0300 Subject: [PATCH 18/28] Add AllowedIPs config change --- client/platforms/ios/ios_controller.mm | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/client/platforms/ios/ios_controller.mm b/client/platforms/ios/ios_controller.mm index b8ce0372..c1f0f643 100644 --- a/client/platforms/ios/ios_controller.mm +++ b/client/platforms/ios/ios_controller.mm @@ -449,15 +449,8 @@ bool IosController::setupWireGuard() wgConfig.insert(config_key::splitTunnelSites, splitTunnelSites); - if (config.contains(config_key::allowed_ips)) { - QJsonArray allowed_ips; - QStringList allowed_ips_list = config[config_key::allowed_ips].toString().split(", "); - - for(int index = 0; index < allowed_ips_list.length(); index++) { - allowed_ips.append(allowed_ips_list[index]); - } - - wgConfig.insert(config_key::allowed_ips, allowed_ips); + if (config.contains(config_key::allowed_ips) && config[config_key::allowed_ips].isArray()) { + wgConfig.insert(config_key::allowed_ips, config[config_key::allowed_ips]); } else { QJsonArray allowed_ips { "0.0.0.0/0", "::/0" }; wgConfig.insert(config_key::allowed_ips, allowed_ips); @@ -494,15 +487,8 @@ bool IosController::setupAwg() wgConfig.insert(config_key::splitTunnelSites, splitTunnelSites); - if (config.contains(config_key::allowed_ips)) { - QJsonArray allowed_ips; - QStringList allowed_ips_list = config[config_key::allowed_ips].toString().split(", "); - - for(int index = 0; index < allowed_ips_list.length(); index++) { - allowed_ips.append(allowed_ips_list[index]); - } - - wgConfig.insert(config_key::allowed_ips, allowed_ips); + if (config.contains(config_key::allowed_ips) && config[config_key::allowed_ips].isArray()) { + wgConfig.insert(config_key::allowed_ips, config[config_key::allowed_ips]); } else { QJsonArray allowed_ips { "0.0.0.0/0", "::/0" }; wgConfig.insert(config_key::allowed_ips, allowed_ips); From 9189b53a0db782b42ec823d4f5699082e28c5356 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Tue, 12 Mar 2024 23:42:02 +0500 Subject: [PATCH 19/28] fixed display of hostName on the sftp settings page --- client/ui/qml/Pages2/PageServiceSftpSettings.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/qml/Pages2/PageServiceSftpSettings.qml b/client/ui/qml/Pages2/PageServiceSftpSettings.qml index 9a0e5ff1..81fd8c77 100644 --- a/client/ui/qml/Pages2/PageServiceSftpSettings.qml +++ b/client/ui/qml/Pages2/PageServiceSftpSettings.qml @@ -88,7 +88,7 @@ PageType { Layout.topMargin: 32 text: qsTr("Host") - descriptionText: ServersModel.getProcessedServerData("HostName") + descriptionText: ServersModel.getProcessedServerData("hostName") descriptionOnTop: true From 0a90fd110d15f3041c4b0fbdb53fdeb5dc22783b Mon Sep 17 00:00:00 2001 From: Andrey Zaharow <59512038+andr13@users.noreply.github.com> Date: Wed, 13 Mar 2024 01:17:18 +0200 Subject: [PATCH 20/28] Add RU translation for Error 1101 text (#683) * Add RU translation for Error 1101 text --- client/translations/amneziavpn_ru.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/translations/amneziavpn_ru.ts b/client/translations/amneziavpn_ru.ts index a8d78c86..25f3178d 100644 --- a/client/translations/amneziavpn_ru.ts +++ b/client/translations/amneziavpn_ru.ts @@ -2674,7 +2674,7 @@ and will not be shared or disclosed to the Amnezia or any third parties This config has already been added to the application - + Этот конфиг уже был добавлен в приложение From c5a5bfde696667dde0011fc231651f0fdc19eb55 Mon Sep 17 00:00:00 2001 From: Nethius Date: Thu, 14 Mar 2024 04:22:10 +0700 Subject: [PATCH 21/28] extended the validation of the contents of the imported file (#670) Extended the validation of the contents of the imported file --- client/ui/controllers/importController.cpp | 119 +++++++++++------- client/ui/controllers/importController.h | 11 +- client/ui/controllers/settingsController.cpp | 5 + client/ui/controllers/settingsController.h | 1 + .../Pages2/PageSetupWizardConfigSource.qml | 7 +- client/ui/qml/Pages2/PageSetupWizardStart.qml | 14 +++ .../ui/qml/Pages2/PageSetupWizardTextKey.qml | 2 +- client/ui/qml/Pages2/PageStart.qml | 8 ++ 8 files changed, 109 insertions(+), 58 deletions(-) diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index f7c1ea20..b33954bf 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -18,7 +18,9 @@ namespace enum class ConfigTypes { Amnezia, OpenVpn, - WireGuard + WireGuard, + Backup, + Invalid }; ConfigTypes checkConfigFormat(const QString &config) @@ -32,15 +34,23 @@ namespace const QString wireguardConfigPatternSectionInterface = "[Interface]"; const QString wireguardConfigPatternSectionPeer = "[Peer]"; - if (config.contains(openVpnConfigPatternCli) - && (config.contains(openVpnConfigPatternProto1) || config.contains(openVpnConfigPatternProto2)) - && (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) { + const QString amneziaConfigPattern = "containers"; + const QString amneziaFreeConfigPattern = "api_key"; + const QString backupPattern = "Servers/serversList"; + + if (config.contains(backupPattern)) { + return ConfigTypes::Backup; + } else if (config.contains(amneziaConfigPattern) || config.contains(amneziaFreeConfigPattern)) { + return ConfigTypes::Amnezia; + } else if (config.contains(openVpnConfigPatternCli) + && (config.contains(openVpnConfigPatternProto1) || config.contains(openVpnConfigPatternProto2)) + && (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) { return ConfigTypes::OpenVpn; } else if (config.contains(wireguardConfigPatternSectionInterface) && config.contains(wireguardConfigPatternSectionPeer)) { return ConfigTypes::WireGuard; } - return ConfigTypes::Amnezia; + return ConfigTypes::Invalid; } #if defined Q_OS_ANDROID @@ -58,34 +68,65 @@ ImportController::ImportController(const QSharedPointer &serversMo #endif } -void ImportController::extractConfigFromFile(const QString &fileName) +bool ImportController::extractConfigFromFile(const QString &fileName) { QFile file(fileName); if (file.open(QIODevice::ReadOnly)) { QString data = file.readAll(); - extractConfigFromData(data); m_configFileName = QFileInfo(file.fileName()).fileName(); + return extractConfigFromData(data); } + + emit importErrorOccurred(tr("Unable to open file")); + return false; } -void ImportController::extractConfigFromData(QString data) +bool ImportController::extractConfigFromData(QString data) { - auto configFormat = checkConfigFormat(data); - if (configFormat == ConfigTypes::OpenVpn) { - m_config = extractOpenVpnConfig(data); - } else if (configFormat == ConfigTypes::WireGuard) { - m_config = extractWireGuardConfig(data); - } else { - m_config = extractAmneziaConfig(data); - } -} + QString config = data; + auto configFormat = checkConfigFormat(config); + if (configFormat == ConfigTypes::Invalid) { + data.replace("vpn://", ""); + QByteArray ba = + QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); + QByteArray ba_uncompressed = qUncompress(ba); + if (!ba_uncompressed.isEmpty()) { + ba = ba_uncompressed; + } -void ImportController::extractConfigFromCode(QString code) -{ - m_config = extractAmneziaConfig(code); - m_configFileName = ""; + config = ba; + configFormat = checkConfigFormat(config); + } + + switch (configFormat) { + case ConfigTypes::OpenVpn: { + m_config = extractOpenVpnConfig(config); + return true; + } + case ConfigTypes::WireGuard: { + m_config = extractWireGuardConfig(config); + return true; + } + case ConfigTypes::Amnezia: { + m_config = QJsonDocument::fromJson(config.toUtf8()).object(); + return true; + } + case ConfigTypes::Backup: { + if (!m_serversModel->getServersCount()) { + emit restoreAppConfig(config.toUtf8()); + } else { + emit importErrorOccurred(tr("Invalid configuration file")); + } + break; + } + case ConfigTypes::Invalid: { + emit importErrorOccurred(tr("Invalid configuration file")); + break; + } + } + return false; } bool ImportController::extractConfigFromQr(const QByteArray &data) @@ -139,28 +180,13 @@ void ImportController::importConfig() } else { qDebug() << "Failed to import profile"; qDebug().noquote() << QJsonDocument(m_config).toJson(); - emit importErrorOccurred(errorString(ErrorCode::ImportInvalidConfigError)); + emit importErrorOccurred(errorString(ErrorCode::ImportInvalidConfigError), false); } m_config = {}; m_configFileName.clear(); } -QJsonObject ImportController::extractAmneziaConfig(QString &data) -{ - data.replace("vpn://", ""); - QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); - - QByteArray ba_uncompressed = qUncompress(ba); - if (!ba_uncompressed.isEmpty()) { - ba = ba_uncompressed; - } - - QJsonObject config = QJsonDocument::fromJson(ba).object(); - - return config; -} - QJsonObject ImportController::extractOpenVpnConfig(const QString &data) { QJsonObject openVpnConfig; @@ -229,8 +255,8 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data) if (hostNameAndPortMatch.hasCaptured(1)) { hostName = hostNameAndPortMatch.captured(1); } else { - qDebug() << "Failed to import profile"; - emit importErrorOccurred(errorString(ErrorCode::ImportInvalidConfigError)); + qDebug() << "Key parameter 'Endpoint' is missing"; + emit importErrorOccurred(errorString(ErrorCode::ImportInvalidConfigError), false); } if (hostNameAndPortMatch.hasCaptured(2)) { @@ -242,10 +268,11 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data) lastConfig[config_key::hostName] = hostName; lastConfig[config_key::port] = port.toInt(); -// if (!configMap.value("PrivateKey").isEmpty() && !configMap.value("Address").isEmpty() -// && !configMap.value("PresharedKey").isEmpty() && !configMap.value("PublicKey").isEmpty()) { + if (!configMap.value("PrivateKey").isEmpty() && !configMap.value("Address").isEmpty() + && !configMap.value("PublicKey").isEmpty()) { lastConfig[config_key::client_priv_key] = configMap.value("PrivateKey"); lastConfig[config_key::client_ip] = configMap.value("Address"); + if (!configMap.value("PresharedKey").isEmpty()) { lastConfig[config_key::psk_key] = configMap.value("PresharedKey"); } else if (!configMap.value("PreSharedKey").isEmpty()) { @@ -253,11 +280,11 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data) } lastConfig[config_key::server_pub_key] = configMap.value("PublicKey"); -// } else { -// qDebug() << "Failed to import profile"; -// emit importErrorOccurred(errorString(ErrorCode::ImportInvalidConfigError)); -// return QJsonObject(); -// } + } else { + qDebug() << "One of the key parameters is missing (PrivateKey, Address, PublicKey)"; + emit importErrorOccurred(errorString(ErrorCode::ImportInvalidConfigError)); + return QJsonObject(); + } QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configMap.value("AllowedIPs").split(",")); diff --git a/client/ui/controllers/importController.h b/client/ui/controllers/importController.h index 5731cae8..a8d91124 100644 --- a/client/ui/controllers/importController.h +++ b/client/ui/controllers/importController.h @@ -18,9 +18,8 @@ public: public slots: void importConfig(); - void extractConfigFromFile(const QString &fileName); - void extractConfigFromData(QString data); - void extractConfigFromCode(QString code); + bool extractConfigFromFile(const QString &fileName); + bool extractConfigFromData(QString data); bool extractConfigFromQr(const QByteArray &data); QString getConfig(); QString getConfigFileName(); @@ -39,12 +38,14 @@ public slots: signals: void importFinished(); - void importErrorOccurred(const QString &errorMessage, bool goToPageHome = false); + void importErrorOccurred(const QString &errorMessage, bool goToPageHome); + void importErrorOccurred(const QString &errorMessage); void qrDecodingFinished(); + void restoreAppConfig(const QByteArray &data); + private: - QJsonObject extractAmneziaConfig(QString &data); QJsonObject extractOpenVpnConfig(const QString &data); QJsonObject extractWireGuardConfig(const QString &data); diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index 658bbb19..f559f1df 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -113,6 +113,11 @@ void SettingsController::restoreAppConfig(const QString &fileName) QByteArray data = file.readAll(); + restoreAppConfigFromData(data); +} + +void SettingsController::restoreAppConfigFromData(const QByteArray &data) +{ bool ok = m_settings->restoreAppConfig(data); if (ok) { m_serversModel->resetModel(); diff --git a/client/ui/controllers/settingsController.h b/client/ui/controllers/settingsController.h index 5b30b095..36247a55 100644 --- a/client/ui/controllers/settingsController.h +++ b/client/ui/controllers/settingsController.h @@ -41,6 +41,7 @@ public slots: void backupAppConfig(const QString &fileName); void restoreAppConfig(const QString &fileName); + void restoreAppConfigFromData(const QByteArray &data); QString getAppVersion(); diff --git a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml index 33aee0f1..6ed8f5ab 100644 --- a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml +++ b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml @@ -73,12 +73,7 @@ PageType { "Config files (*.vpn *.ovpn *.conf)" var fileName = SystemController.getFileName(qsTr("Open config file"), nameFilter) if (fileName !== "") { - if (fileName.indexOf(".backup") !== -1 && !ServersModel.getServersCount()) { - PageController.showBusyIndicator(true) - SettingsController.restoreAppConfig(fileName) - PageController.showBusyIndicator(false) - } else { - ImportController.extractConfigFromFile(fileName) + if (ImportController.extractConfigFromFile(fileName)) { PageController.goToPage(PageEnum.PageSetupWizardViewConfig) } } diff --git a/client/ui/qml/Pages2/PageSetupWizardStart.qml b/client/ui/qml/Pages2/PageSetupWizardStart.qml index 89d4dc12..b12589c8 100644 --- a/client/ui/qml/Pages2/PageSetupWizardStart.qml +++ b/client/ui/qml/Pages2/PageSetupWizardStart.qml @@ -81,6 +81,20 @@ PageType { } } + Connections { + target: ImportController + + function onRestoreAppConfig(data) { + PageController.showBusyIndicator(true) + SettingsController.restoreAppConfigFromData(data) + PageController.showBusyIndicator(false) + } + + function onImportErrorOccurred(errorMessage) { + PageController.showErrorMessage(errorMessage) + } + } + FlickableType { id: fl anchors.top: parent.top diff --git a/client/ui/qml/Pages2/PageSetupWizardTextKey.qml b/client/ui/qml/Pages2/PageSetupWizardTextKey.qml index 4a322042..30cd8b89 100644 --- a/client/ui/qml/Pages2/PageSetupWizardTextKey.qml +++ b/client/ui/qml/Pages2/PageSetupWizardTextKey.qml @@ -77,7 +77,7 @@ PageType { text: qsTr("Continue") clickedFunc: function() { - ImportController.extractConfigFromCode(textKey.textFieldText) + ImportController.extractConfigFromData(textKey.textFieldText) PageController.goToPage(PageEnum.PageSetupWizardViewConfig) } } diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index 3fd2d73c..44a2453d 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -121,6 +121,14 @@ PageType { } } + Connections { + target: ImportController + + function onImportErrorOccurred(errorMessage) { + PageController.showErrorMessage(errorMessage) + } + } + StackViewType { id: tabBarStackView From 915fb6759a1fa9ff5c48a7530d1706ddb40f772e Mon Sep 17 00:00:00 2001 From: albexk Date: Thu, 14 Mar 2024 00:22:56 +0300 Subject: [PATCH 22/28] Add Android openssl3 libs, fix https connection error (#685) Add Android openssl3 libs, fix https connection error --- CMakeLists.txt | 4 ++-- client/3rd-prebuilt | 2 +- client/cmake/android.cmake | 2 ++ client/main.cpp | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b1433e0..f8e1391e 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.4.1.2 +project(${PROJECT} VERSION 4.4.1.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 47) +set(APP_ANDROID_VERSION_CODE 48) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(MZ_PLATFORM_NAME "linux") diff --git a/client/3rd-prebuilt b/client/3rd-prebuilt index 2fa21880..629fdf77 160000 --- a/client/3rd-prebuilt +++ b/client/3rd-prebuilt @@ -1 +1 @@ -Subproject commit 2fa21880b9e5059cf9e8856c778bad97dcad3c91 +Subproject commit 629fdf77735506c904bb1449a3ed73de7d1f93f6 diff --git a/client/cmake/android.cmake b/client/cmake/android.cmake index ec47a38b..fe4a607c 100644 --- a/client/cmake/android.cmake +++ b/client/cmake/android.cmake @@ -50,5 +50,7 @@ foreach(abi IN ITEMS ${QT_ANDROID_ABIS}) ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/libovpnutil.so ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/librsapss.so ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/libssh/android/${abi}/libssh.so + ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openssl3/android/${abi}/libcrypto_3.so + ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openssl3/android/${abi}/libssl_3.so ) endforeach() diff --git a/client/main.cpp b/client/main.cpp index 54d26bdb..77c91fbc 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -26,9 +26,10 @@ int main(int argc, char *argv[]) AllowSetForegroundWindow(ASFW_ANY); #endif -// QTBUG-95974 QTBUG-95764 QTBUG-102168 #ifdef Q_OS_ANDROID + // QTBUG-95974 QTBUG-95764 QTBUG-102168 qputenv("QT_ANDROID_DISABLE_ACCESSIBILITY", "1"); + qputenv("ANDROID_OPENSSL_SUFFIX", "_3"); #endif #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) From 9a81f13f81983cce62ae4b16122305cb1c6c0908 Mon Sep 17 00:00:00 2001 From: Andrey Zaharow Date: Wed, 13 Mar 2024 22:44:09 +0100 Subject: [PATCH 23/28] Short translated text --- client/translations/amneziavpn_my_MM.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/translations/amneziavpn_my_MM.ts b/client/translations/amneziavpn_my_MM.ts index 3181920a..10026e42 100644 --- a/client/translations/amneziavpn_my_MM.ts +++ b/client/translations/amneziavpn_my_MM.ts @@ -767,7 +767,7 @@ Already installed containers were found on the server. All installed containers Remove SFTP and all data stored there - SFTP နှင့် ထိုနေရာတွင် သိမ်းဆည်းထားသည့် ဒေတာအားလုံးကို ဖယ်ရှားမည် + SFTP ဖယ်ရှားပါ From 33f49bfddb060f815f223f17a5e29998d3841f52 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 14 Mar 2024 12:55:33 +0500 Subject: [PATCH 24/28] added error handling for importing a native config --- client/ui/controllers/importController.cpp | 6 +++--- client/ui/qml/Pages2/PageSetupWizardTextKey.qml | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index b33954bf..9522cfbe 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -103,15 +103,15 @@ bool ImportController::extractConfigFromData(QString data) switch (configFormat) { case ConfigTypes::OpenVpn: { m_config = extractOpenVpnConfig(config); - return true; + return m_config.empty() ? false : true; } case ConfigTypes::WireGuard: { m_config = extractWireGuardConfig(config); - return true; + return m_config.empty() ? false : true; } case ConfigTypes::Amnezia: { m_config = QJsonDocument::fromJson(config.toUtf8()).object(); - return true; + return m_config.empty() ? false : true; } case ConfigTypes::Backup: { if (!m_serversModel->getServersCount()) { diff --git a/client/ui/qml/Pages2/PageSetupWizardTextKey.qml b/client/ui/qml/Pages2/PageSetupWizardTextKey.qml index 30cd8b89..064d30ed 100644 --- a/client/ui/qml/Pages2/PageSetupWizardTextKey.qml +++ b/client/ui/qml/Pages2/PageSetupWizardTextKey.qml @@ -77,8 +77,9 @@ PageType { text: qsTr("Continue") clickedFunc: function() { - ImportController.extractConfigFromData(textKey.textFieldText) - PageController.goToPage(PageEnum.PageSetupWizardViewConfig) + if (ImportController.extractConfigFromData(textKey.textFieldText)) { + PageController.goToPage(PageEnum.PageSetupWizardViewConfig) + } } } } From f51077b2be77a88c404f564ae5b03359841065c0 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 14 Mar 2024 15:59:16 +0500 Subject: [PATCH 25/28] fixed checking credentials for spaces --- client/ui/qml/Pages2/PageSetupWizardCredentials.qml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml index c256f3a4..3e6205c6 100644 --- a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml +++ b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml @@ -56,8 +56,8 @@ PageType { regularExpression: InstallController.ipAddressPortRegExp() } - onFocusChanged: { - textField.text = textField.text.replace(/^\s+|\s+$/g, ''); + textField.onFocusChanged: { + textField.text = textField.text.replace(/^\s+|\s+$/g, '') } KeyNavigation.tab: username.textField @@ -70,6 +70,10 @@ PageType { headerText: qsTr("Login to connect via SSH") textFieldPlaceholderText: "root" + textField.onFocusChanged: { + textField.text = textField.text.replace(/^\s+|\s+$/g, '') + } + KeyNavigation.tab: secretData.textField } @@ -88,8 +92,8 @@ PageType { hidePassword = !hidePassword } - onFocusChanged: { - textField.text = textField.text.replace(/^\s+|\s+$/g, ''); + textField.onFocusChanged: { + textField.text = textField.text.replace(/^\s+|\s+$/g, '') } KeyNavigation.tab: continueButton From b7c513c05fb1c0381b2a45f8fd58cc90719d3686 Mon Sep 17 00:00:00 2001 From: Dan Nguyen Date: Sun, 17 Mar 2024 07:09:57 +0700 Subject: [PATCH 26/28] ISSUE: Service is crashed after disconnecting ROOT CAUSE: When disconnecting service, m_logworker is deleted in thread which does not have affinity with m_logworker. The time m_logworker is deleted, it may be used by m_logthread and make the service crashed ACTION: Connect signal finished() of m_logthread to deleteLater() slot of m_logworker to safety delete it. --- client/platforms/windows/daemon/windowstunnelservice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/platforms/windows/daemon/windowstunnelservice.cpp b/client/platforms/windows/daemon/windowstunnelservice.cpp index 37f81f26..8d2e08b6 100644 --- a/client/platforms/windows/daemon/windowstunnelservice.cpp +++ b/client/platforms/windows/daemon/windowstunnelservice.cpp @@ -58,7 +58,6 @@ void WindowsTunnelService::stop() { if (m_logworker) { m_logthread.quit(); m_logthread.wait(); - delete m_logworker; m_logworker = nullptr; } } @@ -104,6 +103,7 @@ bool WindowsTunnelService::start(const QString& configData) { m_logworker = new WindowsTunnelLogger(WindowsCommons::tunnelLogFile()); m_logworker->moveToThread(&m_logthread); + connect(&m_logthread, &QThread::finished, m_logworker, &QObject::deleteLater); m_logthread.start(); SC_HANDLE scm = (SC_HANDLE)m_scm; From e646b85e5657490467f67e50ce376f9867c09dbb Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Mon, 18 Mar 2024 12:41:53 +0200 Subject: [PATCH 27/28] Setup MTU for WG/AWG protocol (#576) Setup MTU for AWG/WG protocol --- client/android/awg/src/main/kotlin/Awg.kt | 2 +- .../wireguard/src/main/kotlin/Wireguard.kt | 13 +- client/configurators/awg_configurator.cpp | 2 + .../configurators/wireguard_configurator.cpp | 3 + client/core/controllers/serverController.cpp | 28 ++- client/daemon/daemon.cpp | 13 ++ client/daemon/interfaceconfig.cpp | 6 + client/daemon/interfaceconfig.h | 1 + client/mozilla/localsocketcontroller.cpp | 3 +- client/platforms/ios/Log.swift | 2 +- client/platforms/ios/WGConfig.swift | 7 +- client/platforms/ios/ios_controller.mm | 41 ++++- .../platforms/linux/daemon/iputilslinux.cpp | 9 +- .../platforms/macos/daemon/iputilsmacos.cpp | 7 +- client/protocols/protocols_defs.h | 5 + client/resources.qrc | 1 + client/ui/controllers/importController.cpp | 4 + client/ui/controllers/installController.cpp | 40 +++- client/ui/controllers/installController.h | 2 + client/ui/models/protocols/awgConfigModel.cpp | 80 ++++++-- client/ui/models/protocols/awgConfigModel.h | 22 +++ .../models/protocols/wireguardConfigModel.cpp | 55 +++++- .../models/protocols/wireguardConfigModel.h | 14 +- .../Components/SettingsContainersListView.qml | 8 +- .../ui/qml/Pages2/PageProtocolAwgSettings.qml | 22 ++- .../qml/Pages2/PageProtocolCloakSettings.qml | 2 +- .../Pages2/PageProtocolOpenVpnSettings.qml | 2 +- .../PageProtocolShadowSocksSettings.qml | 2 +- .../Pages2/PageProtocolWireGuardSettings.qml | 172 ++++++++++++++++++ 29 files changed, 512 insertions(+), 56 deletions(-) create mode 100644 client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml diff --git a/client/android/awg/src/main/kotlin/Awg.kt b/client/android/awg/src/main/kotlin/Awg.kt index c21caaff..5a3255c4 100644 --- a/client/android/awg/src/main/kotlin/Awg.kt +++ b/client/android/awg/src/main/kotlin/Awg.kt @@ -64,7 +64,7 @@ class Awg : Wireguard() { val configDataJson = config.getJSONObject("awg_config_data") val configData = parseConfigData(configDataJson.getString("config")) return AwgConfig.build { - configWireguard(configData) + configWireguard(configData, configDataJson) configSplitTunneling(config) configData["Jc"]?.let { setJc(it.toInt()) } configData["Jmin"]?.let { setJmin(it.toInt()) } diff --git a/client/android/wireguard/src/main/kotlin/Wireguard.kt b/client/android/wireguard/src/main/kotlin/Wireguard.kt index 40fa4ec6..a71d7936 100644 --- a/client/android/wireguard/src/main/kotlin/Wireguard.kt +++ b/client/android/wireguard/src/main/kotlin/Wireguard.kt @@ -92,12 +92,12 @@ open class Wireguard : Protocol() { val configDataJson = config.getJSONObject("wireguard_config_data") val configData = parseConfigData(configDataJson.getString("config")) return WireguardConfig.build { - configWireguard(configData) + configWireguard(configData, configDataJson) configSplitTunneling(config) } } - protected fun WireguardConfig.Builder.configWireguard(configData: Map) { + protected fun WireguardConfig.Builder.configWireguard(configData: Map, configDataJson: JSONObject) { configData["Address"]?.split(",")?.map { address -> InetNetwork.parse(address.trim()) }?.forEach(::addAddress) @@ -118,7 +118,14 @@ open class Wireguard : Protocol() { if (routes.any { it !in defRoutes }) disableSplitTunneling() addRoutes(routes) - configData["MTU"]?.let { setMtu(it.toInt()) } + configDataJson.optString("mtu").let { mtu -> + if (mtu.isNotEmpty()) { + setMtu(mtu.toInt()) + } else { + configData["MTU"]?.let { setMtu(it.toInt()) } + } + } + configData["Endpoint"]?.let { setEndpoint(InetEndpoint.parse(it)) } configData["PersistentKeepalive"]?.let { setPersistentKeepalive(it.toInt()) } configData["PrivateKey"]?.let { setPrivateKeyHex(it.base64ToHex()) } diff --git a/client/configurators/awg_configurator.cpp b/client/configurators/awg_configurator.cpp index 5b452755..186d2009 100644 --- a/client/configurators/awg_configurator.cpp +++ b/client/configurators/awg_configurator.cpp @@ -41,6 +41,8 @@ QString AwgConfigurator::genAwgConfig(const ServerCredentials &credentials, Dock jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader); jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader); jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader); + jsonConfig[config_key::mtu] = containerConfig.value(ProtocolProps::protoToString(Proto::Awg)).toObject(). + value(config_key::mtu).toString(protocols::awg::defaultMtu); return QJsonDocument(jsonConfig).toJson(); } diff --git a/client/configurators/wireguard_configurator.cpp b/client/configurators/wireguard_configurator.cpp index f28ac539..03c9a5b9 100644 --- a/client/configurators/wireguard_configurator.cpp +++ b/client/configurators/wireguard_configurator.cpp @@ -194,6 +194,7 @@ QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &crede config.replace("$WIREGUARD_SERVER_PUBLIC_KEY", connData.serverPubKey); config.replace("$WIREGUARD_PSK", connData.pskKey); + const QJsonObject &wireguarConfig = containerConfig.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject(); QJsonObject jConfig; jConfig[config_key::config] = config; @@ -205,6 +206,8 @@ QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &crede jConfig[config_key::psk_key] = connData.pskKey; jConfig[config_key::server_pub_key] = connData.serverPubKey; + jConfig[config_key::mtu] = wireguarConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu); + clientId = connData.clientPubKey; return QJsonDocument(jConfig).toJson(); diff --git a/client/core/controllers/serverController.cpp b/client/core/controllers/serverController.cpp index 99ee5b11..81f638e7 100644 --- a/client/core/controllers/serverController.cpp +++ b/client/core/controllers/serverController.cpp @@ -359,7 +359,33 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c } if (container == DockerContainer::Awg) { - return true; + if ((oldProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort) + != newProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort)) + || (oldProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount) + != newProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount)) + || (oldProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize) + != newProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize)) + || (oldProtoConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize) + != newProtoConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize)) + || (oldProtoConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize) + != newProtoConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize)) + || (oldProtoConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize) + != newProtoConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize)) + || (oldProtoConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader) + != newProtoConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader)) + || (oldProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader) + != newProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader)) + || (oldProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader) + != newProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader)) + || (oldProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader) + != newProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader))) + return true; + } + + if (container == DockerContainer::WireGuard){ + if (oldProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort) + != newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)) + return true; } return false; diff --git a/client/daemon/daemon.cpp b/client/daemon/daemon.cpp index b85b2c33..b0e47817 100644 --- a/client/daemon/daemon.cpp +++ b/client/daemon/daemon.cpp @@ -251,6 +251,19 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) { GETVALUE("serverPskKey", config.m_serverPskKey, String); GETVALUE("serverPort", config.m_serverPort, Double); + if (!obj.contains("deviceMTU") || obj.value("deviceMTU").toString().toInt() == 0) + { + config.m_deviceMTU = 1420; + } else { + config.m_deviceMTU = obj.value("deviceMTU").toString().toInt(); +#ifdef Q_OS_WINDOWS +// For Windows min MTU value is 1280 (the smallest MTU legal with IPv6). + if (config.m_deviceMTU < 1280) { + config.m_deviceMTU = 1280; + } +#endif + } + config.m_deviceIpv4Address = obj.value("deviceIpv4Address").toString(); config.m_deviceIpv6Address = obj.value("deviceIpv6Address").toString(); if (config.m_deviceIpv4Address.isNull() && diff --git a/client/daemon/interfaceconfig.cpp b/client/daemon/interfaceconfig.cpp index 8aa06b9b..b2ad31c6 100644 --- a/client/daemon/interfaceconfig.cpp +++ b/client/daemon/interfaceconfig.cpp @@ -23,6 +23,7 @@ QJsonObject InterfaceConfig::toJson() const { json.insert("serverIpv4AddrIn", QJsonValue(m_serverIpv4AddrIn)); json.insert("serverIpv6AddrIn", QJsonValue(m_serverIpv6AddrIn)); json.insert("serverPort", QJsonValue((double)m_serverPort)); + json.insert("deviceMTU", QJsonValue(m_deviceMTU)); if ((m_hopType == InterfaceConfig::MultiHopExit) || (m_hopType == InterfaceConfig::SingleHop)) { json.insert("serverIpv4Gateway", QJsonValue(m_serverIpv4Gateway)); @@ -85,8 +86,13 @@ QString InterfaceConfig::toWgConf(const QMap& extra) const { if (addresses.isEmpty()) { return ""; } + out << "Address = " << addresses.join(", ") << "\n"; + if (m_deviceMTU) { + out << "MTU = " << m_deviceMTU << "\n"; + } + if (!m_dnsServer.isNull()) { QStringList dnsServers(m_dnsServer); // If the DNS is not the Gateway, it's a user defined DNS diff --git a/client/daemon/interfaceconfig.h b/client/daemon/interfaceconfig.h index 29aef085..f9869661 100644 --- a/client/daemon/interfaceconfig.h +++ b/client/daemon/interfaceconfig.h @@ -33,6 +33,7 @@ class InterfaceConfig { QString m_serverIpv6AddrIn; QString m_dnsServer; int m_serverPort = 0; + int m_deviceMTU = 1420; QList m_allowedIPAddressRanges; QStringList m_excludedAddresses; QStringList m_vpnDisabledApps; diff --git a/client/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index 54f87a12..68a7963b 100644 --- a/client/mozilla/localsocketcontroller.cpp +++ b/client/mozilla/localsocketcontroller.cpp @@ -132,8 +132,9 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { json.insert("serverPskKey", wgConfig.value(amnezia::config_key::psk_key)); json.insert("serverIpv4AddrIn", wgConfig.value(amnezia::config_key::hostName)); // json.insert("serverIpv6AddrIn", QJsonValue(hop.m_server.ipv6AddrIn())); - json.insert("serverPort", wgConfig.value(amnezia::config_key::port).toInt()); + json.insert("deviceMTU", wgConfig.value(amnezia::config_key::mtu)); + json.insert("serverPort", wgConfig.value(amnezia::config_key::port).toInt()); json.insert("serverIpv4Gateway", wgConfig.value(amnezia::config_key::hostName)); // json.insert("serverIpv6Gateway", QJsonValue(hop.m_server.ipv6Gateway())); json.insert("dnsServer", rawConfig.value(amnezia::config_key::dns1)); diff --git a/client/platforms/ios/Log.swift b/client/platforms/ios/Log.swift index f7f48b87..d9512dc6 100644 --- a/client/platforms/ios/Log.swift +++ b/client/platforms/ios/Log.swift @@ -38,7 +38,7 @@ struct Log { init(_ str: String) { self.records = str.split(whereSeparator: \.isNewline) .compactMap { - Record(String($0))! + Record(String($0)) } } diff --git a/client/platforms/ios/WGConfig.swift b/client/platforms/ios/WGConfig.swift index 15f99100..b703834a 100644 --- a/client/platforms/ios/WGConfig.swift +++ b/client/platforms/ios/WGConfig.swift @@ -7,6 +7,7 @@ struct WGConfig: Decodable { let initPacketJunkSize, responsePacketJunkSize: String? let dns1: String let dns2: String + let mtu: String let hostName: String let port: Int let clientIP: String @@ -25,6 +26,7 @@ struct WGConfig: Decodable { case initPacketJunkSize = "S1", responsePacketJunkSize = "S2" case dns1 case dns2 + case mtu case hostName case port case clientIP = "client_ip" @@ -58,6 +60,7 @@ struct WGConfig: Decodable { [Interface] Address = \(clientIP) DNS = \(dns1), \(dns2) + MTU = \(mtu) PrivateKey = \(clientPrivateKey) \(settings) [Peer] @@ -74,6 +77,7 @@ struct WGConfig: Decodable { [Interface] Address = \(clientIP) DNS = \(dns1), \(dns2) + MTU = \(mtu) PrivateKey = *** \(settings) [Peer] @@ -88,10 +92,11 @@ struct WGConfig: Decodable { struct OpenVPNConfig: Decodable { let config: String + let mtu: String let splitTunnelType: Int let splitTunnelSites: [String] var str: String { - "splitTunnelType: \(splitTunnelType) splitTunnelSites: \(splitTunnelSites) config: \(config)" + "splitTunnelType: \(splitTunnelType) splitTunnelSites: \(splitTunnelSites) mtu: \(mtu) config: \(config)" } } diff --git a/client/platforms/ios/ios_controller.mm b/client/platforms/ios/ios_controller.mm index c1f0f643..0800acc8 100644 --- a/client/platforms/ios/ios_controller.mm +++ b/client/platforms/ios/ios_controller.mm @@ -358,6 +358,13 @@ bool IosController::setupOpenVPN() QJsonObject openVPNConfig {}; openVPNConfig.insert(config_key::config, ovpnConfig); + + if (ovpn.contains(config_key::mtu)) { + openVPNConfig.insert(config_key::mtu, ovpn[config_key::mtu]); + } else { + openVPNConfig.insert(config_key::mtu, protocols::openvpn::defaultMtu); + } + openVPNConfig.insert(config_key::splitTunnelType, m_rawConfig[config_key::splitTunnelType]); QJsonArray splitTunnelSites = m_rawConfig[config_key::splitTunnelSites].toArray(); @@ -410,7 +417,12 @@ bool IosController::setupCloak() QJsonObject openVPNConfig {}; openVPNConfig.insert(config_key::config, ovpnConfig); - openVPNConfig.insert(config_key::splitTunnelType, m_rawConfig[config_key::splitTunnelType]); + + if (ovpn.contains(config_key::mtu)) { + openVPNConfig.insert(config_key::mtu, ovpn[config_key::mtu]); + } else { + openVPNConfig.insert(config_key::mtu, protocols::openvpn::defaultMtu); + } QJsonArray splitTunnelSites = m_rawConfig[config_key::splitTunnelSites].toArray(); @@ -433,6 +445,13 @@ bool IosController::setupWireGuard() QJsonObject wgConfig {}; wgConfig.insert(config_key::dns1, m_rawConfig[config_key::dns1]); wgConfig.insert(config_key::dns2, m_rawConfig[config_key::dns2]); + + if (config.contains(config_key::mtu)) { + wgConfig.insert(config_key::mtu, config[config_key::mtu]); + } else { + wgConfig.insert(config_key::mtu, protocols::wireguard::defaultMtu); + } + wgConfig.insert(config_key::hostName, config[config_key::hostName]); wgConfig.insert(config_key::port, config[config_key::port]); wgConfig.insert(config_key::client_ip, config[config_key::client_ip]); @@ -456,7 +475,11 @@ bool IosController::setupWireGuard() wgConfig.insert(config_key::allowed_ips, allowed_ips); } - wgConfig.insert("persistent_keep_alive", "25"); + if (config.contains(config_key::persistent_keep_alive)) { + wgConfig.insert(config_key::persistent_keep_alive, config[config_key::persistent_keep_alive]); + } else { + wgConfig.insert(config_key::persistent_keep_alive, "25"); + } QJsonDocument wgConfigDoc(wgConfig); QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact)); @@ -471,6 +494,13 @@ bool IosController::setupAwg() QJsonObject wgConfig {}; wgConfig.insert(config_key::dns1, m_rawConfig[config_key::dns1]); wgConfig.insert(config_key::dns2, m_rawConfig[config_key::dns2]); + + if (config.contains(config_key::mtu)) { + wgConfig.insert(config_key::mtu, config[config_key::mtu]); + } else { + wgConfig.insert(config_key::mtu, protocols::awg::defaultMtu); + } + wgConfig.insert(config_key::hostName, config[config_key::hostName]); wgConfig.insert(config_key::port, config[config_key::port]); wgConfig.insert(config_key::client_ip, config[config_key::client_ip]); @@ -494,7 +524,12 @@ bool IosController::setupAwg() wgConfig.insert(config_key::allowed_ips, allowed_ips); } - wgConfig.insert("persistent_keep_alive", "25"); + if (config.contains(config_key::persistent_keep_alive)) { + wgConfig.insert(config_key::persistent_keep_alive, config[config_key::persistent_keep_alive]); + } else { + wgConfig.insert(config_key::persistent_keep_alive, "25"); + } + wgConfig.insert(config_key::initPacketMagicHeader, config[config_key::initPacketMagicHeader]); wgConfig.insert(config_key::responsePacketMagicHeader, config[config_key::responsePacketMagicHeader]); wgConfig.insert(config_key::underloadPacketMagicHeader, config[config_key::underloadPacketMagicHeader]); diff --git a/client/platforms/linux/daemon/iputilslinux.cpp b/client/platforms/linux/daemon/iputilslinux.cpp index 9a51caad..f0f2fbab 100644 --- a/client/platforms/linux/daemon/iputilslinux.cpp +++ b/client/platforms/linux/daemon/iputilslinux.cpp @@ -16,9 +16,6 @@ #include "leakdetector.h" #include "logger.h" -constexpr uint32_t ETH_MTU = 1500; -constexpr uint32_t WG_MTU_OVERHEAD = 80; - namespace { Logger logger("IPUtilsLinux"); } @@ -38,8 +35,6 @@ bool IPUtilsLinux::addInterfaceIPs(const InterfaceConfig& config) { } bool IPUtilsLinux::setMTUAndUp(const InterfaceConfig& config) { - Q_UNUSED(config); - // Create socket file descriptor to perform the ioctl operations on int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (sockfd < 0) { @@ -56,10 +51,10 @@ bool IPUtilsLinux::setMTUAndUp(const InterfaceConfig& config) { // FIXME: We need to know how many layers deep this particular // interface is into a tunnel to work effectively. Otherwise // we will run into fragmentation issues. - ifr.ifr_mtu = ETH_MTU - WG_MTU_OVERHEAD; + ifr.ifr_mtu = config.m_deviceMTU; int ret = ioctl(sockfd, SIOCSIFMTU, &ifr); if (ret) { - logger.error() << "Failed to set MTU -- Return code: " << ret; + logger.error() << "Failed to set MTU -- " << config.m_deviceMTU << " -- Return code: " << ret; return false; } diff --git a/client/platforms/macos/daemon/iputilsmacos.cpp b/client/platforms/macos/daemon/iputilsmacos.cpp index 0af093a4..0599e2eb 100644 --- a/client/platforms/macos/daemon/iputilsmacos.cpp +++ b/client/platforms/macos/daemon/iputilsmacos.cpp @@ -20,9 +20,6 @@ #include "logger.h" #include "macosdaemon.h" -constexpr uint32_t ETH_MTU = 1500; -constexpr uint32_t WG_MTU_OVERHEAD = 80; - namespace { Logger logger("IPUtilsMacos"); } @@ -56,10 +53,10 @@ bool IPUtilsMacos::setMTUAndUp(const InterfaceConfig& config) { // MTU strncpy(ifr.ifr_name, qPrintable(ifname), IFNAMSIZ); - ifr.ifr_mtu = ETH_MTU - WG_MTU_OVERHEAD; + ifr.ifr_mtu = config.m_deviceMTU; int ret = ioctl(sockfd, SIOCSIFMTU, &ifr); if (ret) { - logger.error() << "Failed to set MTU:" << strerror(errno); + logger.error() << "Failed to set MTU -- " << config.m_deviceMTU << " -- Return code: " << ret; return false; } diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index 8ab5594f..f75ed39e 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -45,7 +45,9 @@ namespace amnezia constexpr char server_priv_key[] = "server_priv_key"; constexpr char server_pub_key[] = "server_pub_key"; constexpr char psk_key[] = "psk_key"; + constexpr char mtu[] = "mtu"; constexpr char allowed_ips[] = "allowed_ips"; + constexpr char persistent_keep_alive[] = "persistent_keep_alive"; constexpr char client_ip[] = "client_ip"; // internal ip address @@ -103,6 +105,7 @@ namespace amnezia constexpr char defaultSubnetAddress[] = "10.8.0.0"; constexpr char defaultSubnetMask[] = "255.255.255.0"; constexpr char defaultSubnetCidr[] = "24"; + constexpr char defaultMtu[] = "1500"; constexpr char serverConfigPath[] = "/opt/amnezia/openvpn/server.conf"; constexpr char caCertPath[] = "/opt/amnezia/openvpn/pki/ca.crt"; @@ -149,6 +152,7 @@ namespace amnezia constexpr char defaultSubnetCidr[] = "24"; constexpr char defaultPort[] = "51820"; + constexpr char defaultMtu[] = "1420"; constexpr char serverConfigPath[] = "/opt/amnezia/wireguard/wg0.conf"; constexpr char serverPublicKeyPath[] = "/opt/amnezia/wireguard/wireguard_server_public_key.key"; constexpr char serverPskKeyPath[] = "/opt/amnezia/wireguard/wireguard_psk.key"; @@ -164,6 +168,7 @@ namespace amnezia namespace awg { constexpr char defaultPort[] = "55424"; + constexpr char defaultMtu[] = "1420"; constexpr char serverConfigPath[] = "/opt/amnezia/awg/wg0.conf"; constexpr char serverPublicKeyPath[] = "/opt/amnezia/awg/wireguard_server_public_key.key"; diff --git a/client/resources.qrc b/client/resources.qrc index b9a69023..2ff7671a 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -224,6 +224,7 @@ ui/qml/Pages2/PageShareFullAccess.qml images/controls/close.svg images/controls/search.svg + ui/qml/Pages2/PageProtocolWireGuardSettings.qml ui/qml/Components/HomeSplitTunnelingDrawer.qml images/controls/split-tunneling.svg ui/qml/Controls2/DrawerType2.qml diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index 9522cfbe..098d77a7 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -286,6 +286,10 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data) return QJsonObject(); } + if (!configMap.value("MTU").isEmpty()) { + lastConfig[config_key::mtu] = configMap.value("MTU"); + } + QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configMap.value("AllowedIPs").split(",")); lastConfig[config_key::allowed_ips] = allowedIpsJsonArray; diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index 25ce155f..e25d9c06 100644 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -10,6 +10,8 @@ #include "core/errorstrings.h" #include "core/controllers/serverController.h" #include "utilities.h" +#include "ui/models/protocols/awgConfigModel.h" +#include "ui/models/protocols/wireguardConfigModel.h" namespace { @@ -273,12 +275,16 @@ void InstallController::updateContainer(QJsonObject config) const DockerContainer container = ContainerProps::containerFromString(config.value(config_key::container).toString()); QJsonObject oldContainerConfig = m_containersModel->getContainerConfig(container); + ErrorCode errorCode = ErrorCode::NoError; - ServerController serverController(m_settings); - connect(&serverController, &ServerController::serverIsBusy, this, &InstallController::serverIsBusy); - connect(this, &InstallController::cancelInstallation, &serverController, &ServerController::cancelInstallation); + if (isUpdateDockerContainerRequired(container, oldContainerConfig, config)) { + ServerController serverController(m_settings); + connect(&serverController, &ServerController::serverIsBusy, this, &InstallController::serverIsBusy); + connect(this, &InstallController::cancelInstallation, &serverController, &ServerController::cancelInstallation); + + errorCode = serverController.updateContainer(serverCredentials, container, oldContainerConfig, config); + } - auto errorCode = serverController.updateContainer(serverCredentials, container, oldContainerConfig, config); if (errorCode == ErrorCode::NoError) { m_serversModel->updateContainerConfig(container, config); m_protocolModel->updateModel(config); @@ -514,3 +520,29 @@ void InstallController::addEmptyServer() emit installServerFinished(tr("Server added successfully")); } + +bool InstallController::isUpdateDockerContainerRequired(const DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig) +{ + Proto mainProto = ContainerProps::defaultProtocol(container); + + const QJsonObject &oldProtoConfig = oldConfig.value(ProtocolProps::protoToString(mainProto)).toObject(); + const QJsonObject &newProtoConfig = newConfig.value(ProtocolProps::protoToString(mainProto)).toObject(); + + if (container == DockerContainer::Awg) { + const AwgConfig oldConfig(oldProtoConfig); + const AwgConfig newConfig(newProtoConfig); + + if (!oldConfig.hasEqualServerSettings(newConfig)) { + return true; + } + } else if (container == DockerContainer::WireGuard) { + const WgConfig oldConfig(oldProtoConfig); + const WgConfig newConfig(newProtoConfig); + + if (!oldConfig.hasEqualServerSettings(newConfig)) { + return true; + } + } + + return false; +} diff --git a/client/ui/controllers/installController.h b/client/ui/controllers/installController.h index 6b5295dc..49090349 100644 --- a/client/ui/controllers/installController.h +++ b/client/ui/controllers/installController.h @@ -76,6 +76,8 @@ private: void installContainer(DockerContainer container, QJsonObject &config); bool isServerAlreadyExists(); + bool isUpdateDockerContainerRequired(const DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig); + QSharedPointer m_serversModel; QSharedPointer m_containersModel; QSharedPointer m_protocolModel; diff --git a/client/ui/models/protocols/awgConfigModel.cpp b/client/ui/models/protocols/awgConfigModel.cpp index 7d0277b9..ba36c2a9 100644 --- a/client/ui/models/protocols/awgConfigModel.cpp +++ b/client/ui/models/protocols/awgConfigModel.cpp @@ -22,6 +22,7 @@ bool AwgConfigModel::setData(const QModelIndex &index, const QVariant &value, in switch (role) { case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; + case Roles::MtuRole: m_protocolConfig.insert(config_key::mtu, value.toString()); break; case Roles::JunkPacketCountRole: m_protocolConfig.insert(config_key::junkPacketCount, value.toString()); break; case Roles::JunkPacketMinSizeRole: m_protocolConfig.insert(config_key::junkPacketMinSize, value.toString()); break; case Roles::JunkPacketMaxSizeRole: m_protocolConfig.insert(config_key::junkPacketMaxSize, value.toString()); break; @@ -57,6 +58,7 @@ QVariant AwgConfigModel::data(const QModelIndex &index, int role) const switch (role) { case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(); + case Roles::MtuRole: return m_protocolConfig.value(config_key::mtu).toString(); case Roles::JunkPacketCountRole: return m_protocolConfig.value(config_key::junkPacketCount); case Roles::JunkPacketMinSizeRole: return m_protocolConfig.value(config_key::junkPacketMinSize); case Roles::JunkPacketMaxSizeRole: return m_protocolConfig.value(config_key::junkPacketMaxSize); @@ -80,25 +82,21 @@ void AwgConfigModel::updateModel(const QJsonObject &config) QJsonObject protocolConfig = config.value(config_key::awg).toObject(); - m_protocolConfig[config_key::port] = - protocolConfig.value(config_key::port).toString(protocols::awg::defaultPort); + m_protocolConfig[config_key::last_config] = protocolConfig.value(config_key::last_config); + m_protocolConfig[config_key::port] = protocolConfig.value(config_key::port).toString(protocols::awg::defaultPort); + m_protocolConfig[config_key::mtu] = protocolConfig.value(config_key::mtu).toString(protocols::awg::defaultMtu); m_protocolConfig[config_key::junkPacketCount] = protocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount); m_protocolConfig[config_key::junkPacketMinSize] = - protocolConfig.value(config_key::junkPacketMinSize) - .toString(protocols::awg::defaultJunkPacketMinSize); + protocolConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize); m_protocolConfig[config_key::junkPacketMaxSize] = - protocolConfig.value(config_key::junkPacketMaxSize) - .toString(protocols::awg::defaultJunkPacketMaxSize); + protocolConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize); m_protocolConfig[config_key::initPacketJunkSize] = - protocolConfig.value(config_key::initPacketJunkSize) - .toString(protocols::awg::defaultInitPacketJunkSize); + protocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize); m_protocolConfig[config_key::responsePacketJunkSize] = - protocolConfig.value(config_key::responsePacketJunkSize) - .toString(protocols::awg::defaultResponsePacketJunkSize); + protocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize); m_protocolConfig[config_key::initPacketMagicHeader] = - protocolConfig.value(config_key::initPacketMagicHeader) - .toString(protocols::awg::defaultInitPacketMagicHeader); + protocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader); m_protocolConfig[config_key::responsePacketMagicHeader] = protocolConfig.value(config_key::responsePacketMagicHeader) .toString(protocols::awg::defaultResponsePacketMagicHeader); @@ -114,6 +112,19 @@ void AwgConfigModel::updateModel(const QJsonObject &config) QJsonObject AwgConfigModel::getConfig() { + const AwgConfig oldConfig(m_fullConfig.value(config_key::awg).toObject()); + const AwgConfig newConfig(m_protocolConfig); + + if (!oldConfig.hasEqualServerSettings(newConfig)) { + m_protocolConfig.remove(config_key::last_config); + } else { + auto lastConfig = m_protocolConfig.value(config_key::last_config).toString(); + QJsonObject jsonConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object(); + jsonConfig[config_key::mtu] = newConfig.mtu; + + m_protocolConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson()); + } + m_fullConfig.insert(config_key::awg, m_protocolConfig); return m_fullConfig; } @@ -123,6 +134,7 @@ QHash AwgConfigModel::roleNames() const QHash roles; roles[PortRole] = "port"; + roles[MtuRole] = "mtu"; roles[JunkPacketCountRole] = "junkPacketCount"; roles[JunkPacketMinSizeRole] = "junkPacketMinSize"; roles[JunkPacketMaxSizeRole] = "junkPacketMaxSize"; @@ -135,3 +147,47 @@ QHash AwgConfigModel::roleNames() const return roles; } + +AwgConfig::AwgConfig(const QJsonObject &jsonConfig) +{ + port = jsonConfig.value(config_key::port).toString(protocols::awg::defaultPort); + mtu = jsonConfig.value(config_key::mtu).toString(protocols::awg::defaultMtu); + junkPacketCount = jsonConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount); + junkPacketMinSize = + jsonConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize); + junkPacketMaxSize = + jsonConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize); + initPacketJunkSize = + jsonConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize); + responsePacketJunkSize = + jsonConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize); + initPacketMagicHeader = + jsonConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader); + responsePacketMagicHeader = jsonConfig.value(config_key::responsePacketMagicHeader) + .toString(protocols::awg::defaultResponsePacketMagicHeader); + underloadPacketMagicHeader = jsonConfig.value(config_key::underloadPacketMagicHeader) + .toString(protocols::awg::defaultUnderloadPacketMagicHeader); + transportPacketMagicHeader = jsonConfig.value(config_key::transportPacketMagicHeader) + .toString(protocols::awg::defaultTransportPacketMagicHeader); +} + +bool AwgConfig::hasEqualServerSettings(const AwgConfig &other) const +{ + if (port != other.port || junkPacketCount != other.junkPacketCount || junkPacketMinSize != other.junkPacketMinSize + || junkPacketMaxSize != other.junkPacketMaxSize || initPacketJunkSize != other.initPacketJunkSize + || responsePacketJunkSize != other.responsePacketJunkSize || initPacketMagicHeader != other.initPacketMagicHeader + || responsePacketMagicHeader != other.responsePacketMagicHeader + || underloadPacketMagicHeader != other.underloadPacketMagicHeader + || transportPacketMagicHeader != other.transportPacketMagicHeader) { + return false; + } + return true; +} + +bool AwgConfig::hasEqualClientSettings(const AwgConfig &other) const +{ + if (mtu != other.mtu) { + return false; + } + return true; +} diff --git a/client/ui/models/protocols/awgConfigModel.h b/client/ui/models/protocols/awgConfigModel.h index e67a3708..e41d172d 100644 --- a/client/ui/models/protocols/awgConfigModel.h +++ b/client/ui/models/protocols/awgConfigModel.h @@ -6,6 +6,27 @@ #include "containers/containers_defs.h" +struct AwgConfig +{ + AwgConfig(const QJsonObject &jsonConfig); + + QString port; + QString mtu; + QString junkPacketCount; + QString junkPacketMinSize; + QString junkPacketMaxSize; + QString initPacketJunkSize; + QString responsePacketJunkSize; + QString initPacketMagicHeader; + QString responsePacketMagicHeader; + QString underloadPacketMagicHeader; + QString transportPacketMagicHeader; + + bool hasEqualServerSettings(const AwgConfig &other) const; + bool hasEqualClientSettings(const AwgConfig &other) const; + +}; + class AwgConfigModel : public QAbstractListModel { Q_OBJECT @@ -13,6 +34,7 @@ class AwgConfigModel : public QAbstractListModel public: enum Roles { PortRole = Qt::UserRole + 1, + MtuRole, JunkPacketCountRole, JunkPacketMinSizeRole, JunkPacketMaxSizeRole, diff --git a/client/ui/models/protocols/wireguardConfigModel.cpp b/client/ui/models/protocols/wireguardConfigModel.cpp index 15e89865..60f6685f 100644 --- a/client/ui/models/protocols/wireguardConfigModel.cpp +++ b/client/ui/models/protocols/wireguardConfigModel.cpp @@ -1,5 +1,7 @@ #include "wireguardConfigModel.h" +#include + #include "protocols/protocols_defs.h" WireGuardConfigModel::WireGuardConfigModel(QObject *parent) : QAbstractListModel(parent) @@ -19,8 +21,8 @@ bool WireGuardConfigModel::setData(const QModelIndex &index, const QVariant &val } switch (role) { - case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; - case Roles::CipherRole: m_protocolConfig.insert(config_key::cipher, value.toString()); break; + case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; + case Roles::MtuRole: m_protocolConfig.insert(config_key::mtu, value.toString()); break; } emit dataChanged(index, index, QList { role }); @@ -34,9 +36,8 @@ QVariant WireGuardConfigModel::data(const QModelIndex &index, int role) const } switch (role) { - case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort); - case Roles::CipherRole: - return m_protocolConfig.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher); + case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(); + case Roles::MtuRole: return m_protocolConfig.value(config_key::mtu).toString(); } return QVariant(); @@ -50,11 +51,31 @@ void WireGuardConfigModel::updateModel(const QJsonObject &config) m_fullConfig = config; QJsonObject protocolConfig = config.value(config_key::wireguard).toObject(); + m_protocolConfig[config_key::last_config] = protocolConfig.value(config_key::last_config); + m_protocolConfig[config_key::port] = + protocolConfig.value(config_key::port).toString(protocols::wireguard::defaultPort); + + m_protocolConfig[config_key::mtu] = + protocolConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu); + endResetModel(); } QJsonObject WireGuardConfigModel::getConfig() { + const WgConfig oldConfig(m_fullConfig.value(config_key::awg).toObject()); + const WgConfig newConfig(m_protocolConfig); + + if (!oldConfig.hasEqualServerSettings(newConfig)) { + m_protocolConfig.remove(config_key::last_config); + } else { + auto lastConfig = m_protocolConfig.value(config_key::last_config).toString(); + QJsonObject jsonConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object(); + jsonConfig[config_key::mtu] = newConfig.mtu; + + m_protocolConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson()); + } + m_fullConfig.insert(config_key::wireguard, m_protocolConfig); return m_fullConfig; } @@ -64,7 +85,29 @@ QHash WireGuardConfigModel::roleNames() const QHash roles; roles[PortRole] = "port"; - roles[CipherRole] = "cipher"; + roles[MtuRole] = "mtu"; return roles; } + +WgConfig::WgConfig(const QJsonObject &jsonConfig) +{ + port = jsonConfig.value(config_key::port).toString(protocols::wireguard::defaultPort); + mtu = jsonConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu); +} + +bool WgConfig::hasEqualServerSettings(const WgConfig &other) const +{ + if (port != other.port) { + return false; + } + return true; +} + +bool WgConfig::hasEqualClientSettings(const WgConfig &other) const +{ + if (mtu != other.mtu) { + return false; + } + return true; +} diff --git a/client/ui/models/protocols/wireguardConfigModel.h b/client/ui/models/protocols/wireguardConfigModel.h index 1deeacaf..6cec76dd 100644 --- a/client/ui/models/protocols/wireguardConfigModel.h +++ b/client/ui/models/protocols/wireguardConfigModel.h @@ -6,6 +6,18 @@ #include "containers/containers_defs.h" +struct WgConfig +{ + WgConfig(const QJsonObject &jsonConfig); + + QString port; + QString mtu; + + bool hasEqualServerSettings(const WgConfig &other) const; + bool hasEqualClientSettings(const WgConfig &other) const; + +}; + class WireGuardConfigModel : public QAbstractListModel { Q_OBJECT @@ -13,7 +25,7 @@ class WireGuardConfigModel : public QAbstractListModel public: enum Roles { PortRole = Qt::UserRole + 1, - CipherRole + MtuRole }; explicit WireGuardConfigModel(QObject *parent = nullptr); diff --git a/client/ui/qml/Components/SettingsContainersListView.qml b/client/ui/qml/Components/SettingsContainersListView.qml index 89eb727e..794ab0b3 100644 --- a/client/ui/qml/Components/SettingsContainersListView.qml +++ b/client/ui/qml/Components/SettingsContainersListView.qml @@ -58,10 +58,8 @@ ListView { break } case ContainerEnum.WireGuard: { - ProtocolsModel.updateModel(config) - PageController.goToPage(PageEnum.PageProtocolRaw) - // WireGuardConfigModel.updateModel(config) - // goToPage(PageEnum.PageProtocolWireGuardSettings) + WireGuardConfigModel.updateModel(config) + PageController.goToPage(PageEnum.PageProtocolWireGuardSettings) break } case ContainerEnum.Awg: { @@ -72,8 +70,6 @@ ListView { case ContainerEnum.Ipsec: { ProtocolsModel.updateModel(config) PageController.goToPage(PageEnum.PageProtocolRaw) - // Ikev2ConfigModel.updateModel(config) - // goToPage(PageEnum.PageProtocolIKev2Settings) break } case ContainerEnum.Sftp: { diff --git a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml index 16946fdf..68b838a9 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml @@ -106,6 +106,26 @@ PageType { KeyNavigation.tab: junkPacketCountTextField.textField } + TextFieldWithHeaderType { + id: mtuTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: qsTr("MTU") + textFieldText: mtu + textField.validator: IntValidator { bottom: 576; top: 65535 } + + textField.onEditingFinished: { + if (textFieldText === "") { + textFieldText = "0" + } + if (textFieldText !== mtu) { + mtu = textFieldText + } + } + checkEmptyText: true + } + TextFieldWithHeaderType { id: junkPacketCountTextField Layout.fillWidth: true @@ -337,7 +357,7 @@ PageType { junkPacketCountTextField.errorText === "" && portTextField.errorText === "" - text: qsTr("Save and Restart Amnezia") + text: qsTr("Save") clickedFunc: function() { forceActiveFocus() diff --git a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml index b86397ba..729f248f 100644 --- a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml @@ -174,7 +174,7 @@ PageType { Layout.topMargin: 24 Layout.bottomMargin: 24 - text: qsTr("Save and Restart Amnezia") + text: qsTr("Save") clickedFunc: function() { forceActiveFocus() diff --git a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml index 7fb4634c..2346ee9a 100644 --- a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml @@ -404,7 +404,7 @@ PageType { Layout.topMargin: 24 Layout.bottomMargin: 24 - text: qsTr("Save and Restart Amnezia") + text: qsTr("Save") clickedFunc: function() { forceActiveFocus() diff --git a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml index 5284f807..f9447c78 100644 --- a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml @@ -148,7 +148,7 @@ PageType { Layout.topMargin: 24 Layout.bottomMargin: 24 - text: qsTr("Save and Restart Amnezia") + text: qsTr("Save") clickedFunc: function() { forceActiveFocus() diff --git a/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml new file mode 100644 index 00000000..f9f4a4dc --- /dev/null +++ b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml @@ -0,0 +1,172 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import SortFilterProxyModel 0.2 + +import PageEnum 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" +import "../Components" + +PageType { + id: root + + ColumnLayout { + id: backButton + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.topMargin: 20 + + BackButtonType { + } + } + + FlickableType { + id: fl + anchors.top: backButton.bottom + anchors.bottom: parent.bottom + contentHeight: content.implicitHeight + + Column { + id: content + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + enabled: ServersModel.isCurrentlyProcessedServerHasWriteAccess() + + ListView { + id: listview + + width: parent.width + height: listview.contentItem.height + + clip: true + interactive: false + + model: WireGuardConfigModel + + delegate: Item { + implicitWidth: listview.width + implicitHeight: col.implicitHeight + + ColumnLayout { + id: col + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.leftMargin: 16 + anchors.rightMargin: 16 + + spacing: 0 + + HeaderType { + Layout.fillWidth: true + headerText: qsTr("WG settings") + } + + TextFieldWithHeaderType { + id: portTextField + Layout.fillWidth: true + Layout.topMargin: 40 + + headerText: qsTr("Port") + textFieldText: port + textField.maximumLength: 5 + textField.validator: IntValidator { bottom: 1; top: 65535 } + + textField.onEditingFinished: { + if (textFieldText !== port) { + port = textFieldText + } + } + + checkEmptyText: true + } + + TextFieldWithHeaderType { + id: mtuTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: qsTr("MTU") + textFieldText: mtu + textField.validator: IntValidator { bottom: 576; top: 65535 } + + textField.onEditingFinished: { + if (textFieldText === "") { + textFieldText = "0" + } + if (textFieldText !== mtu) { + mtu = textFieldText + } + } + checkEmptyText: true + } + + BasicButtonType { + Layout.topMargin: 24 + Layout.leftMargin: -8 + implicitHeight: 32 + + defaultColor: "transparent" + hoveredColor: Qt.rgba(1, 1, 1, 0.08) + pressedColor: Qt.rgba(1, 1, 1, 0.12) + textColor: "#EB5757" + + text: qsTr("Remove WG") + + onClicked: { + questionDrawer.headerText = qsTr("Remove WG from server?") + questionDrawer.descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.") + questionDrawer.yesButtonText = qsTr("Continue") + questionDrawer.noButtonText = qsTr("Cancel") + + questionDrawer.yesButtonFunction = function() { + questionDrawer.visible = false + PageController.goToPage(PageEnum.PageDeinstalling) + InstallController.removeCurrentlyProcessedContainer() + } + questionDrawer.noButtonFunction = function() { + questionDrawer.visible = false + } + questionDrawer.visible = true + } + } + + BasicButtonType { + Layout.fillWidth: true + Layout.topMargin: 24 + Layout.bottomMargin: 24 + + enabled: mtuTextField.errorText === "" && + portTextField.errorText === "" + + text: qsTr("Save") + + onClicked: { + forceActiveFocus() + PageController.goToPage(PageEnum.PageSetupWizardInstalling); + InstallController.updateContainer(WireGuardConfigModel.getConfig()) + } + } + } + } + } + } + + QuestionDrawer { + id: questionDrawer + } + } +} From 10caecbffd6b5188409f14cb12524319da68290a Mon Sep 17 00:00:00 2001 From: albexk Date: Mon, 18 Mar 2024 14:20:01 +0300 Subject: [PATCH 28/28] Fix wg reconnection problem after awg connection (#696) * Update Android AWG to 0.2.5 --- CMakeLists.txt | 4 ++-- client/3rd-prebuilt | 2 +- .../android/wireguard/src/main/kotlin/GoBackend.kt | 10 ---------- .../src/main/kotlin/org/amnezia/awg/GoBackend.kt | 10 ++++++++++ .../amnezia/vpn/protocol/wireguard}/Wireguard.kt | 13 +++++++------ .../vpn/protocol/wireguard}/WireguardConfig.kt | 0 6 files changed, 20 insertions(+), 19 deletions(-) delete mode 100644 client/android/wireguard/src/main/kotlin/GoBackend.kt create mode 100644 client/android/wireguard/src/main/kotlin/org/amnezia/awg/GoBackend.kt rename client/android/wireguard/src/main/kotlin/{ => org/amnezia/vpn/protocol/wireguard}/Wireguard.kt (92%) rename client/android/wireguard/src/main/kotlin/{ => org/amnezia/vpn/protocol/wireguard}/WireguardConfig.kt (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index f8e1391e..58ee37c9 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.4.1.3 +project(${PROJECT} VERSION 4.4.1.4 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 48) +set(APP_ANDROID_VERSION_CODE 49) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(MZ_PLATFORM_NAME "linux") diff --git a/client/3rd-prebuilt b/client/3rd-prebuilt index 629fdf77..ab4e6b68 160000 --- a/client/3rd-prebuilt +++ b/client/3rd-prebuilt @@ -1 +1 @@ -Subproject commit 629fdf77735506c904bb1449a3ed73de7d1f93f6 +Subproject commit ab4e6b680dc3d3d81809886c2c25b77750659c1a diff --git a/client/android/wireguard/src/main/kotlin/GoBackend.kt b/client/android/wireguard/src/main/kotlin/GoBackend.kt deleted file mode 100644 index 28e48a75..00000000 --- a/client/android/wireguard/src/main/kotlin/GoBackend.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.amnezia.vpn.protocol.wireguard - -object GoBackend { - external fun wgGetConfig(handle: Int): String? - external fun wgGetSocketV4(handle: Int): Int - external fun wgGetSocketV6(handle: Int): Int - external fun wgTurnOff(handle: Int) - external fun wgTurnOn(ifName: String, tunFd: Int, settings: String): Int - external fun wgVersion(): String -} diff --git a/client/android/wireguard/src/main/kotlin/org/amnezia/awg/GoBackend.kt b/client/android/wireguard/src/main/kotlin/org/amnezia/awg/GoBackend.kt new file mode 100644 index 00000000..5446d8f8 --- /dev/null +++ b/client/android/wireguard/src/main/kotlin/org/amnezia/awg/GoBackend.kt @@ -0,0 +1,10 @@ +package org.amnezia.awg + +object GoBackend { + external fun awgGetConfig(handle: Int): String? + external fun awgGetSocketV4(handle: Int): Int + external fun awgGetSocketV6(handle: Int): Int + external fun awgTurnOff(handle: Int) + external fun awgTurnOn(ifName: String, tunFd: Int, settings: String): Int + external fun awgVersion(): String +} diff --git a/client/android/wireguard/src/main/kotlin/Wireguard.kt b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt similarity index 92% rename from client/android/wireguard/src/main/kotlin/Wireguard.kt rename to client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt index a71d7936..95ea9aca 100644 --- a/client/android/wireguard/src/main/kotlin/Wireguard.kt +++ b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt @@ -4,6 +4,7 @@ import android.content.Context import android.net.VpnService.Builder import java.util.TreeMap import kotlinx.coroutines.flow.MutableStateFlow +import org.amnezia.awg.GoBackend import org.amnezia.vpn.protocol.Protocol import org.amnezia.vpn.protocol.ProtocolState import org.amnezia.vpn.protocol.ProtocolState.CONNECTED @@ -61,7 +62,7 @@ open class Wireguard : Protocol() { override val statistics: Statistics get() { if (tunnelHandle == -1) return Statistics.EMPTY_STATISTICS - val config = GoBackend.wgGetConfig(tunnelHandle) ?: return Statistics.EMPTY_STATISTICS + val config = GoBackend.awgGetConfig(tunnelHandle) ?: return Statistics.EMPTY_STATISTICS return Statistics.build { var optsCount = 0 config.splitToSequence("\n").forEach { line -> @@ -156,8 +157,8 @@ open class Wireguard : Protocol() { if (tunFd == null) { throw VpnStartException("Create VPN interface: permission not granted or revoked") } - Log.v(TAG, "Wg-go backend ${GoBackend.wgVersion()}") - tunnelHandle = GoBackend.wgTurnOn(ifName, tunFd.detachFd(), config.toWgUserspaceString()) + Log.v(TAG, "Wg-go backend ${GoBackend.awgVersion()}") + tunnelHandle = GoBackend.awgTurnOn(ifName, tunFd.detachFd(), config.toWgUserspaceString()) } if (tunnelHandle < 0) { @@ -165,8 +166,8 @@ open class Wireguard : Protocol() { throw VpnStartException("Wireguard tunnel creation error") } - if (!protect(GoBackend.wgGetSocketV4(tunnelHandle)) || !protect(GoBackend.wgGetSocketV6(tunnelHandle))) { - GoBackend.wgTurnOff(tunnelHandle) + if (!protect(GoBackend.awgGetSocketV4(tunnelHandle)) || !protect(GoBackend.awgGetSocketV6(tunnelHandle))) { + GoBackend.awgTurnOff(tunnelHandle) tunnelHandle = -1 throw VpnStartException("Protect VPN interface: permission not granted or revoked") } @@ -179,7 +180,7 @@ open class Wireguard : Protocol() { } val handleToClose = tunnelHandle tunnelHandle = -1 - GoBackend.wgTurnOff(handleToClose) + GoBackend.awgTurnOff(handleToClose) state.value = DISCONNECTED } diff --git a/client/android/wireguard/src/main/kotlin/WireguardConfig.kt b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt similarity index 100% rename from client/android/wireguard/src/main/kotlin/WireguardConfig.kt rename to client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt