Compare commits

..

44 Commits

Author SHA1 Message Date
KsZnak
b930499664 Update README.md 2024-07-09 01:45:14 +03:00
pokamest
3f90ee915d Merge pull request #879 from amnezia-vpn/rename_open_over_ss
Renaming OpenVPN over ShadowsSocks
2024-07-07 16:13:47 +01:00
Nethius
401ad0db0e fixed wg/awg macos firewall rules for 0.0.0.0/0 (#883)
* fixed wg/awg macos/linux firewall rules for 0.0.0.0/0
2024-07-07 14:56:38 +01:00
Vladyslav Miachkov
5945133d30 Create AmneziaStyle qml object (#830) 2024-07-07 11:42:38 +01:00
Nethius
ff4fbde0b0 go to the home page after server installation (#878) 2024-07-07 11:42:14 +01:00
albexk
74ae4f3e67 SSXray for Android (#885) 2024-07-06 16:44:34 +01:00
pokamest
ae4b33d042 Merge pull request #884 from amnezia-vpn/fix/android-xray-config
Fix logging configuration for XRay
2024-07-06 14:10:12 +01:00
albexk
53fa280037 Fix logging configuration for XRay 2024-07-05 18:42:53 +03:00
pokamest
8ecde90bc7 Update README.md, fix crlf 2024-07-04 21:04:56 +01:00
pokamest
34a583f272 Update README.md 2024-07-04 20:58:48 +01:00
pokamest
0612f70c06 Merge pull request #877 from amnezia-vpn/feature/reorder-containers-installing-list
moved xray higher on the list of containers during installation
2024-07-03 14:06:56 +01:00
lunardunno
d0c82efa1c rename OpenVPN over ShadowsSocks 2024-07-03 12:06:31 +04:00
vladimir.kuznetsov
cf8492240e moved xray higher on the list of containers during installation 2024-07-02 22:00:28 +02:00
Boris Verbitckii
2bceb9f7ba Xray and wg fix (#875)
Xray support on iOS fixes
2024-07-01 17:27:53 +01:00
Iurii Egorov
760f935965 iOS Xray support (#864)
Xray for ios
2024-06-30 10:19:38 +01:00
pokamest
eeeb2805c5 Merge pull request #872 from amnezia-vpn/bugfix/torsetup
Fix TorWebsite setup in UI
2024-06-29 21:23:29 +01:00
Mykola Baibuz
9a592d67ad Fix TorWebsite setup in UI 2024-06-28 22:47:22 +03:00
pokamest
ea6618b2f6 Merge pull request #863 from amnezia-vpn/bump
Bump version to 4.6.0.1
2024-06-21 20:14:06 +01:00
albexk
7b092e73ad Bump version to 4.6.0.1 2024-06-21 17:09:48 +03:00
pokamest
b2e25c42c7 Merge pull request #861 from amnezia-vpn/bugfix/xray-socks5-installing
fixed runContainerScript() function
2024-06-21 10:37:30 +01:00
pokamest
c8dd38ac31 Merge pull request #862 from amnezia-vpn/bugfix/translations
fixed ru translations file
2024-06-21 10:37:01 +01:00
vladimir.kuznetsov
563ee4703f fixed ru translations file 2024-06-21 11:16:56 +03:00
vladimir.kuznetsov
beceed81de fixed runContainerScript() function 2024-06-21 11:06:49 +03:00
pokamest
3bf96253db Merge pull request #859 from StrikerRUS/StrikerRUS-patch-2
hotfix for typo introduced in #857
2024-06-20 08:02:28 +01:00
Nikita Titov
da2d0ec203 Update amneziavpn_ru_RU.ts 2024-06-20 01:15:55 +03:00
pokamest
008b858203 Merge pull request #857 from StrikerRUS/trans
update Russian translation
2024-06-19 19:42:25 +01:00
pokamest
130fc8277d Merge pull request #858 from amnezia-vpn/fdroid 2024-06-19 10:41:32 +01:00
albexk
468d3357b8 Update fdroid changelog 2024-06-19 12:10:38 +03:00
StrikerRUS
f1271da527 Merge branch 'dev' into trans 2024-06-19 02:31:04 +03:00
StrikerRUS
249a7c7ca3 update Russian translation 2024-06-19 02:14:22 +03:00
albexk
0094d0ebc4 Add build type for F-Droid 2024-06-18 22:49:05 +03:00
albexk
834b504dff Android XRay (#840)
* Add XRay module
2024-06-18 18:46:21 +01:00
pokamest
a516d0e757 Merge pull request #855 from amnezia-vpn/icons
Update Android icons
2024-06-17 18:20:29 +01:00
albexk
afdfbdbc59 Update Android icons 2024-06-17 18:13:09 +03:00
pokamest
ef712b7054 Merge pull request #841 from amnezia-vpn/bugfix/api-awg-settings-display
fixed display of awg config settings received from api
2024-06-10 12:36:08 +01:00
Nethius
c22f9ff08a added ui for proxy container (#762)
Added proxy container
2024-06-10 12:35:24 +01:00
vladimir.kuznetsov
04fb1825d5 fixed display of awg config settings received from api 2024-06-05 22:19:23 +02:00
pokamest
4f8f873682 Merge pull request #828 from amnezia-vpn/fix/hindi_file_extensions 2024-06-03 08:50:32 +01:00
pokamest
9fe75c6120 Merge pull request #831 from amnezia-vpn/bugfix/wg-show-possible-crash-fix 2024-06-03 08:49:26 +01:00
pokamest
bb7e8f46cb Merge pull request #835 from amnezia-vpn/bugfix/has-split-tunneling 2024-06-03 08:44:04 +01:00
vladimir.kuznetsov
5db0c281ee fixed isDefaultServerDefaultContainerHasSplitTunneling() 2024-05-30 12:42:53 +02:00
Vladyslav Miachkov
aac9bfcea6 Possible wg show crash fix 2024-05-27 18:58:36 +03:00
Mykola Baibuz
e6ee9085a2 Connection string support for XRay protocol (#777)
* Connection string support for XRay protocol
2024-05-27 16:15:55 +01:00
lunardunno
d62ade58a5 update Hindi translation
Fixed handling of file extensions in Hindi translation.
2024-05-27 12:05:53 +04:00
156 changed files with 2693 additions and 1087 deletions

View File

@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.5.3.0
project(${PROJECT} VERSION 4.6.0.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 52)
set(APP_ANDROID_VERSION_CODE 55)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")

View File

@@ -6,10 +6,24 @@
Amnezia is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
![Image](https://github.com/amnezia-vpn/amnezia-client/blob/IMGs-for-Readme/metadata/img-readme/readme-baner.png)
<div style="display: flex; justify-content: center; gap: 10px;">
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.5.3.0/AmneziaVPN_4.5.3.0_x64.exe"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/IMGs-for-Readme/metadata/img-readme/win.png" width="200" style="max-width: 100%;"></a>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.5.3.0/AmneziaVPN_4.5.3.0.dmg"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/IMGs-for-Readme/metadata/img-readme/mac.png" width="200" style="max-width: 100%;"></a>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.5.3.0/AmneziaVPN_Linux_installer.tar.zip"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/IMGs-for-Readme/metadata/img-readme/lin.png" width="200" style="max-width: 100%;"></a>
<a href="https://play.google.com/store/search?q=amnezia+vpn&c=apps"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/IMGs-for-Readme/metadata/img-readme/play.png" width="200" style="max-width: 100%;"></a>
<a href="https://apps.apple.com/us/app/amneziavpn/id1600529900"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/IMGs-for-Readme/metadata/img-readme/apl.png" width="200" style="max-width: 100%;"></a>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.5.3.0/AmneziaVPN-arm64-v8a-release.apk"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/IMGs-for-Readme/metadata/img-readme/andr.png" width="200" style="max-width: 100%;"></a>
</div>
<br>
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
## Features
- Very easy to use - enter your IP address, SSH login, and password, and Amnezia will automatically install VPN docker containers to your server and connect to the VPN.
- OpenVPN, ShadowSocks, WireGuard, and IKEv2 protocols support.
- OpenVPN, Shadowsocks, WireGuard, and IKEv2 protocols support.
- Masking VPN with OpenVPN over Cloak plugin
- Split tunneling support - add any sites to the client to enable VPN only for them (only for desktops)
- Windows, MacOS, Linux, Android, iOS releases.
@@ -18,7 +32,9 @@ Amnezia is an open-source VPN client, with a key feature that enables you to dep
[https://amnezia.org](https://amnezia.org) - project website
[https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
[https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Telegram support channel (English)
[https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Telegram support channel (English)
[https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Telegram support channel (Farsi)
[https://t.me/amnezia_vpn_mm](https://t.me/amnezia_vpn_mm) - Telegram support channel (Myanmar)
[https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Telegram support channel (Russian)
## Tech
@@ -27,7 +43,7 @@ AmneziaVPN uses several open-source projects to work:
- [OpenSSL](https://www.openssl.org/)
- [OpenVPN](https://openvpn.net/)
- [ShadowSocks](https://shadowsocks.org/)
- [Shadowsocks](https://shadowsocks.org/)
- [Qt](https://www.qt.io/)
- [LibSsh](https://libssh.org) - forked from Qt Creator
- and more...
@@ -52,7 +68,7 @@ Check deploy folder for build scripts.
1. First, make sure you have [XCode](https://developer.apple.com/xcode/) installed, at least version 14 or higher.
2. We use QT to generate the XCode project. We need QT version 6.6.1. Install QT for MacOS [here](https://doc.qt.io/qt-6/macos.html) or [QT Online Installer](https://www.qt.io/download-open-source). Required modules:
2. We use QT to generate the XCode project. We need QT version 6.6.2. Install QT for MacOS [here](https://doc.qt.io/qt-6/macos.html) or [QT Online Installer](https://www.qt.io/download-open-source). Required modules:
- MacOS
- iOS
- Qt 5 Compatibility Module
@@ -142,10 +158,10 @@ GPL v3.0
## Donate
Bitcoin: bc1qn9rhsffuxwnhcuuu4qzrwp4upkrq94xnh8r26u
Patreon: [https://www.patreon.com/amneziavpn](https://www.patreon.com/amneziavpn)
USDT BEP20: 0x6abD576765a826f87D1D95183438f9408C901bE4
USDT TRC20: TELAitazF1MZGmiNjTcnxDjEiH5oe7LC9d
XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3
payeer.com: P2561305
ko-fi.com: [https://ko-fi.com/amnezia_vpn](https://ko-fi.com/amnezia_vpn)
## Acknowledgments

View File

@@ -157,6 +157,7 @@ void AmneziaApplication::init()
connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
#endif
m_engine->addImportPath("qrc:/ui/qml/Modules/");
m_engine->load(url);
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
@@ -351,6 +352,9 @@ void AmneziaApplication::initModels()
m_sftpConfigModel.reset(new SftpConfigModel(this));
m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
m_socks5ConfigModel.reset(new Socks5ProxyConfigModel(this));
m_engine->rootContext()->setContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel.get());
m_clientManagementModel.reset(new ClientManagementModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get());
connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked, m_serversModel.get(),

View File

@@ -41,6 +41,7 @@
#include "ui/models/protocols_model.h"
#include "ui/models/servers_model.h"
#include "ui/models/services/sftpConfigModel.h"
#include "ui/models/services/socks5ProxyConfigModel.h"
#include "ui/models/sites_model.h"
#include "ui/models/clientManagementModel.h"
#include "ui/models/appSplitTunnelingModel.h"
@@ -114,6 +115,7 @@ private:
#endif
QScopedPointer<SftpConfigModel> m_sftpConfigModel;
QScopedPointer<Socks5ProxyConfigModel> m_socks5ConfigModel;
QSharedPointer<VpnConnection> m_vpnConnection;
QThread m_vpnConnectionThread;

View File

@@ -136,8 +136,34 @@
</activity>
<service
android:name=".AmneziaVpnService"
android:process=":amneziaVpnService"
android:name=".AwgService"
android:process=":amneziaAwgService"
android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="systemExempted"
android:exported="false"
tools:ignore="ForegroundServicePermission">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
</service>
<service
android:name=".OpenVpnService"
android:process=":amneziaOpenVpnService"
android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="systemExempted"
android:exported="false"
tools:ignore="ForegroundServicePermission">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
</service>
<service
android:name=".XrayService"
android:process=":amneziaXrayService"
android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="systemExempted"
android:exported="false"

View File

@@ -3,6 +3,7 @@ import com.android.build.gradle.internal.api.BaseVariantOutputImpl
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.serialization)
id("property-delegate")
}
@@ -68,6 +69,12 @@ android {
}
signingConfig = signingConfigs["release"]
}
create("fdroid") {
initWith(getByName("release"))
signingConfig = null
matchingFallbacks += "release"
}
}
splits {
@@ -98,7 +105,6 @@ android {
}
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
implementation(project(":qt"))
implementation(project(":utils"))
implementation(project(":protocolApi"))
@@ -106,9 +112,11 @@ dependencies {
implementation(project(":awg"))
implementation(project(":openvpn"))
implementation(project(":cloak"))
implementation(project(":xray"))
implementation(libs.androidx.core)
implementation(libs.androidx.activity)
implementation(libs.kotlinx.coroutines)
implementation(libs.kotlinx.serialization.protobuf)
implementation(libs.bundles.androidx.camera)
implementation(libs.google.mlkit)
implementation(libs.androidx.datastore)

View File

@@ -8,6 +8,7 @@ androidx-camera = "1.3.0"
androidx-security-crypto = "1.1.0-alpha06"
androidx-datastore = "1.1.0-beta01"
kotlinx-coroutines = "1.7.3"
kotlinx-serialization = "1.6.3"
google-mlkit = "17.2.0"
[libraries]
@@ -21,6 +22,7 @@ androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "
androidx-security-crypto = { module = "androidx.security:security-crypto-ktx", version.ref = "androidx-security-crypto" }
androidx-datastore = { module = "androidx.datastore:datastore-preferences", version.ref = "androidx-datastore" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
kotlinx-serialization-protobuf = { module = "org.jetbrains.kotlinx:kotlinx-serialization-protobuf", version.ref = "kotlinx-serialization" }
google-mlkit = { module = "com.google.mlkit:barcode-scanning", version.ref = "google-mlkit" }
[bundles]
@@ -35,3 +37,4 @@ androidx-camera = [
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin"}

View File

@@ -1,15 +1,13 @@
package org.amnezia.vpn.protocol.openvpn
import android.content.Context
import android.net.VpnService.Builder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import net.openvpn.ovpn3.ClientAPI_Config
import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.Protocol
import org.amnezia.vpn.protocol.ProtocolState
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
import org.amnezia.vpn.protocol.Statistics
import org.amnezia.vpn.protocol.VpnStartException
@@ -37,7 +35,6 @@ import org.json.JSONObject
open class OpenVpn : Protocol() {
private lateinit var context: Context
private var openVpnClient: OpenVpnClient? = null
private lateinit var scope: CoroutineScope
@@ -53,10 +50,11 @@ open class OpenVpn : Protocol() {
return Statistics.EMPTY_STATISTICS
}
override fun initialize(context: Context, state: MutableStateFlow<ProtocolState>, onError: (String) -> Unit) {
super.initialize(context, state, onError)
loadSharedLibrary(context, "ovpn3")
this.context = context
override fun internalInit() {
if (!isInitialized) loadSharedLibrary(context, "ovpn3")
if (this::scope.isInitialized) {
scope.cancel()
}
scope = CoroutineScope(Dispatchers.IO)
}

View File

@@ -27,14 +27,21 @@ private const val SPLIT_TUNNEL_EXCLUDE = 2
abstract class Protocol {
abstract val statistics: Statistics
protected lateinit var context: Context
protected lateinit var state: MutableStateFlow<ProtocolState>
protected lateinit var onError: (String) -> Unit
protected var isInitialized: Boolean = false
open fun initialize(context: Context, state: MutableStateFlow<ProtocolState>, onError: (String) -> Unit) {
fun initialize(context: Context, state: MutableStateFlow<ProtocolState>, onError: (String) -> Unit) {
this.context = context
this.state = state
this.onError = onError
internalInit()
isInitialized = true
}
protected abstract fun internalInit()
abstract fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean)
abstract fun stopVpn()

View File

@@ -21,5 +21,5 @@ android {
}
dependencies {
implementation(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar", "*.aar"))))
implementation(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar"))))
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -36,6 +36,8 @@ include(":wireguard")
include(":awg")
include(":openvpn")
include(":cloak")
include(":xray")
include(":xray:libXray")
// get values from gradle or local properties
val androidBuildToolsVersion: String by gradleProperties

View File

@@ -34,6 +34,7 @@ import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
@@ -43,6 +44,8 @@ import org.amnezia.vpn.protocol.getStatus
import org.amnezia.vpn.qt.QtAndroidController
import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.Prefs
import org.json.JSONException
import org.json.JSONObject
import org.qtproject.qt.android.bindings.QtActivity
private const val TAG = "AmneziaActivity"
@@ -59,6 +62,7 @@ class AmneziaActivity : QtActivity() {
private lateinit var mainScope: CoroutineScope
private val qtInitialized = CompletableDeferred<Unit>()
private var vpnProto: VpnProto? = null
private var isWaitingStatus = true
private var isServiceConnected = false
private var isInBoundState = false
@@ -141,6 +145,7 @@ class AmneziaActivity : QtActivity() {
override fun onBindingDied(name: ComponentName?) {
Log.w(TAG, "Binding to the ${name?.flattenToString()} unexpectedly died")
doUnbindService()
QtAndroidController.onServiceDisconnected()
doBindService()
}
}
@@ -153,15 +158,20 @@ class AmneziaActivity : QtActivity() {
super.onCreate(savedInstanceState)
Log.d(TAG, "Create Amnezia activity: $intent")
mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
val proto = mainScope.async(Dispatchers.IO) {
VpnStateStore.getVpnState().vpnProto
}
vpnServiceMessenger = IpcMessenger(
"VpnService",
onDeadObjectException = {
doUnbindService()
QtAndroidController.onServiceDisconnected()
doBindService()
}
)
registerBroadcastReceivers()
intent?.let(::processIntent)
runBlocking { vpnProto = proto.await() }
}
private fun registerBroadcastReceivers() {
@@ -209,13 +219,18 @@ class AmneziaActivity : QtActivity() {
Log.d(TAG, "Start Amnezia activity")
mainScope.launch {
qtInitialized.await()
doBindService()
vpnProto?.let { proto ->
if (AmneziaVpnService.isRunning(applicationContext, proto.processName)) {
doBindService()
}
}
}
}
override fun onStop() {
Log.d(TAG, "Stop Amnezia activity")
doUnbindService()
QtAndroidController.onServiceDisconnected()
super.onStop()
}
@@ -269,10 +284,12 @@ class AmneziaActivity : QtActivity() {
@MainThread
private fun doBindService() {
Log.d(TAG, "Bind service")
Intent(this, AmneziaVpnService::class.java).also {
bindService(it, serviceConnection, BIND_ABOVE_CLIENT and BIND_AUTO_CREATE)
vpnProto?.let { proto ->
Intent(this, proto.serviceClass).also {
bindService(it, serviceConnection, BIND_ABOVE_CLIENT and BIND_AUTO_CREATE)
}
isInBoundState = true
}
isInBoundState = true
}
@MainThread
@@ -280,7 +297,6 @@ class AmneziaActivity : QtActivity() {
if (isInBoundState) {
Log.d(TAG, "Unbind service")
isWaitingStatus = true
QtAndroidController.onServiceDisconnected()
isServiceConnected = false
vpnServiceMessenger.send(Action.UNREGISTER_CLIENT, activityMessenger)
vpnServiceMessenger.reset()
@@ -365,13 +381,32 @@ class AmneziaActivity : QtActivity() {
@MainThread
private fun startVpn(vpnConfig: String) {
if (isServiceConnected) {
connectToVpn(vpnConfig)
} else {
getVpnProto(vpnConfig)?.let { proto ->
Log.d(TAG, "Proto from config: $proto, current proto: $vpnProto")
if (isServiceConnected) {
if (proto.serviceClass == vpnProto?.serviceClass) {
vpnProto = proto
connectToVpn(vpnConfig)
return
}
doUnbindService()
}
vpnProto = proto
isWaitingStatus = false
startVpnService(vpnConfig)
startVpnService(vpnConfig, proto)
doBindService()
}
} ?: QtAndroidController.onServiceError()
}
private fun getVpnProto(vpnConfig: String): VpnProto? = try {
require(vpnConfig.isNotBlank()) { "Blank VPN config" }
VpnProto.get(JSONObject(vpnConfig).getString("protocol"))
} catch (e: JSONException) {
Log.e(TAG, "Invalid VPN config json format: ${e.message}")
null
} catch (e: IllegalArgumentException) {
Log.e(TAG, "Protocol not found: ${e.message}")
null
}
private fun connectToVpn(vpnConfig: String) {
@@ -383,15 +418,15 @@ class AmneziaActivity : QtActivity() {
}
}
private fun startVpnService(vpnConfig: String) {
Log.d(TAG, "Start VPN service")
Intent(this, AmneziaVpnService::class.java).apply {
private fun startVpnService(vpnConfig: String, proto: VpnProto) {
Log.d(TAG, "Start VPN service: $proto")
Intent(this, proto.serviceClass).apply {
putExtra(MSG_VPN_CONFIG, vpnConfig)
}.also {
try {
ContextCompat.startForegroundService(this, it)
} catch (e: SecurityException) {
Log.e(TAG, "Failed to start AmneziaVpnService: $e")
Log.e(TAG, "Failed to start ${proto.serviceClass.simpleName}: $e")
QtAndroidController.onServiceError()
}
}

View File

@@ -39,6 +39,9 @@ class AmneziaTileService : TileService() {
@Volatile
private var isServiceConnected = false
@Volatile
private var vpnProto: VpnProto? = null
private var isInBoundState = false
@Volatile
private var isVpnConfigExists = false
@@ -94,16 +97,21 @@ class AmneziaTileService : TileService() {
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)
scope.launch {
Log.d(TAG, "Start listening")
vpnProto = VpnStateStore.getVpnState().vpnProto
vpnProto.also { proto ->
if (proto != null && AmneziaVpnService.isRunning(applicationContext, proto.processName)) {
Log.d(TAG, "Vpn service is running")
doBindService()
} else {
Log.d(TAG, "Vpn service is not running")
isServiceConnected = false
updateVpnState(DISCONNECTED)
}
}
vpnStateListeningJob = launchVpnStateListening()
}
vpnStateListeningJob = launchVpnStateListening()
}
override fun onStopListening() {
@@ -124,7 +132,7 @@ class AmneziaTileService : TileService() {
}
private fun onClickInternal() {
if (isVpnConfigExists) {
if (isVpnConfigExists && vpnProto != null) {
Log.d(TAG, "Change VPN state")
if (qsTile.state == Tile.STATE_INACTIVE) {
Log.d(TAG, "Start VPN")
@@ -147,10 +155,12 @@ class AmneziaTileService : TileService() {
private fun doBindService() {
Log.d(TAG, "Bind service")
Intent(this, AmneziaVpnService::class.java).also {
bindService(it, serviceConnection, BIND_ABOVE_CLIENT)
vpnProto?.let { proto ->
Intent(this, proto.serviceClass).also {
bindService(it, serviceConnection, BIND_ABOVE_CLIENT)
}
isInBoundState = true
}
isInBoundState = true
}
private fun doUnbindService() {
@@ -180,6 +190,7 @@ class AmneziaTileService : TileService() {
if (VpnService.prepare(applicationContext) != null) {
Intent(this, VpnRequestActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
putExtra(EXTRA_PROTOCOL, vpnProto)
}.also {
startActivityAndCollapseCompat(it)
}
@@ -189,14 +200,16 @@ class AmneziaTileService : TileService() {
}
private fun startVpnService() {
try {
ContextCompat.startForegroundService(
applicationContext,
Intent(this, AmneziaVpnService::class.java)
)
} catch (e: SecurityException) {
Log.e(TAG, "Failed to start AmneziaVpnService: $e")
}
vpnProto?.let { proto ->
try {
ContextCompat.startForegroundService(
applicationContext,
Intent(this, proto.serviceClass)
)
} catch (e: SecurityException) {
Log.e(TAG, "Failed to start ${proto.serviceClass.simpleName}: $e")
}
} ?: Log.e(TAG, "Failed to start vpn service: vpnProto is null")
}
private fun connectToVpn() = vpnServiceMessenger.send(Action.CONNECT)
@@ -220,11 +233,8 @@ class AmneziaTileService : TileService() {
}
}
private fun updateVpnState(state: ProtocolState) {
scope.launch {
VpnStateStore.store { it.copy(protocolState = state) }
}
}
private fun updateVpnState(state: ProtocolState) =
scope.launch { VpnStateStore.store { it.copy(protocolState = state) } }
private fun launchVpnStateListening() =
scope.launch { VpnStateStore.dataFlow().collectLatest(::updateTile) }
@@ -232,9 +242,10 @@ class AmneziaTileService : TileService() {
private fun updateTile(vpnState: VpnState) {
Log.d(TAG, "Update tile: $vpnState")
isVpnConfigExists = vpnState.serverName != null
vpnProto = vpnState.vpnProto
val tile = qsTile ?: return
tile.apply {
label = vpnState.serverName ?: DEFAULT_TILE_LABEL
label = (vpnState.serverName ?: DEFAULT_TILE_LABEL) + (vpnProto?.let { " ${it.label}" } ?: "")
when (val protocolState = vpnState.protocolState) {
CONNECTED -> {
state = Tile.STATE_ACTIVE

View File

@@ -1,5 +1,6 @@
package org.amnezia.vpn
import android.annotation.SuppressLint
import android.app.ActivityManager
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
import android.app.NotificationManager
@@ -39,7 +40,6 @@ import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.LoadLibraryException
import org.amnezia.vpn.protocol.Protocol
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
import org.amnezia.vpn.protocol.ProtocolState.CONNECTING
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
@@ -48,11 +48,7 @@ import org.amnezia.vpn.protocol.ProtocolState.RECONNECTING
import org.amnezia.vpn.protocol.ProtocolState.UNKNOWN
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.putStatus
import org.amnezia.vpn.protocol.wireguard.Wireguard
import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.Prefs
import org.amnezia.vpn.util.net.NetworkState
@@ -63,6 +59,7 @@ import org.json.JSONObject
private const val TAG = "AmneziaVpnService"
const val ACTION_DISCONNECT = "org.amnezia.vpn.action.disconnect"
const val ACTION_CONNECT = "org.amnezia.vpn.action.connect"
const val MSG_VPN_CONFIG = "VPN_CONFIG"
const val MSG_ERROR = "ERROR"
@@ -73,19 +70,18 @@ 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 STATISTICS_SENDING_TIMEOUT = 1000L
private const val TRAFFIC_STATS_UPDATE_TIMEOUT = 1000L
private const val DISCONNECT_TIMEOUT = 5000L
private const val STOP_SERVICE_TIMEOUT = 5000L
class AmneziaVpnService : VpnService() {
@SuppressLint("Registered")
open class AmneziaVpnService : VpnService() {
private lateinit var mainScope: CoroutineScope
private lateinit var connectionScope: CoroutineScope
private var isServiceBound = false
private var protocol: Protocol? = null
private val protocolCache = mutableMapOf<String, Protocol>()
private var vpnProto: VpnProto? = null
private var protocolState = MutableStateFlow(UNKNOWN)
private var serverName: String? = null
private var serverIndex: Int = -1
@@ -105,7 +101,7 @@ class AmneziaVpnService : VpnService() {
// private var statisticsSendingJob: Job? = null
private lateinit var networkState: NetworkState
private lateinit var trafficStats: TrafficStats
private var disconnectReceiver: BroadcastReceiver? = null
private var controlReceiver: BroadcastReceiver? = null
private var notificationStateReceiver: BroadcastReceiver? = null
private var screenOnReceiver: BroadcastReceiver? = null
private var screenOffReceiver: BroadcastReceiver? = null
@@ -116,7 +112,6 @@ class AmneziaVpnService : VpnService() {
private val connectionExceptionHandler = CoroutineExceptionHandler { _, e ->
protocolState.value = DISCONNECTED
protocol = null
when (e) {
is IllegalArgumentException,
is VpnStartException,
@@ -227,7 +222,8 @@ class AmneziaVpnService : VpnService() {
connect(intent?.getStringExtra(MSG_VPN_CONFIG))
}
ServiceCompat.startForeground(
this, NOTIFICATION_ID, serviceNotification.buildNotification(serverName, protocolState.value),
this, NOTIFICATION_ID,
serviceNotification.buildNotification(serverName, vpnProto?.label, protocolState.value),
foregroundServiceTypeCompat
)
return START_REDELIVER_INTENT
@@ -292,9 +288,17 @@ class AmneziaVpnService : VpnService() {
private fun registerBroadcastReceivers() {
Log.d(TAG, "Register broadcast receivers")
disconnectReceiver = registerBroadcastReceiver(ACTION_DISCONNECT, ContextCompat.RECEIVER_NOT_EXPORTED) {
Log.d(TAG, "Broadcast request received: $ACTION_DISCONNECT")
disconnect()
controlReceiver = registerBroadcastReceiver(
arrayOf(ACTION_CONNECT, ACTION_DISCONNECT), ContextCompat.RECEIVER_NOT_EXPORTED
) {
it?.action?.let { action ->
Log.d(TAG, "Broadcast request received: $action")
when (action) {
ACTION_CONNECT -> connect()
ACTION_DISCONNECT -> disconnect()
else -> Log.w(TAG, "Unknown action received: $action")
}
}
}
notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
@@ -340,10 +344,10 @@ class AmneziaVpnService : VpnService() {
private fun unregisterBroadcastReceivers() {
Log.d(TAG, "Unregister broadcast receivers")
unregisterBroadcastReceiver(disconnectReceiver)
unregisterBroadcastReceiver(controlReceiver)
unregisterBroadcastReceiver(notificationStateReceiver)
unregisterScreenStateBroadcastReceivers()
disconnectReceiver = null
controlReceiver = null
notificationStateReceiver = null
}
@@ -356,7 +360,7 @@ class AmneziaVpnService : VpnService() {
protocolState.drop(1).collect { protocolState ->
Log.d(TAG, "Protocol state changed: $protocolState")
serviceNotification.updateNotification(serverName, protocolState)
serviceNotification.updateNotification(serverName, vpnProto?.label, protocolState)
clientMessengers.send {
ServiceEvent.STATUS_CHANGED.packToMessage {
@@ -364,7 +368,7 @@ class AmneziaVpnService : VpnService() {
}
}
VpnStateStore.store { VpnState(protocolState, serverName, serverIndex) }
VpnStateStore.store { VpnState(protocolState, serverName, serverIndex, vpnProto) }
when (protocolState) {
CONNECTED -> {
@@ -421,7 +425,7 @@ class AmneziaVpnService : VpnService() {
@MainThread
private fun enableNotification() {
registerScreenStateBroadcastReceivers()
serviceNotification.updateNotification(serverName, protocolState.value)
serviceNotification.updateNotification(serverName, vpnProto?.label, protocolState.value)
launchTrafficStatsUpdate()
}
@@ -484,8 +488,6 @@ class AmneziaVpnService : VpnService() {
Log.d(TAG, "Start VPN connection")
protocolState.value = CONNECTING
val config = parseConfigToJson(vpnConfig)
saveServerData(config)
if (config == null) {
@@ -494,6 +496,16 @@ class AmneziaVpnService : VpnService() {
return
}
try {
vpnProto = VpnProto.get(config.getString("protocol"))
} catch (e: Exception) {
onError("Invalid VPN config: ${e.message}")
protocolState.value = DISCONNECTED
return
}
protocolState.value = CONNECTING
if (!checkPermission()) {
protocolState.value = DISCONNECTED
return
@@ -503,8 +515,10 @@ class AmneziaVpnService : VpnService() {
disconnectionJob?.join()
disconnectionJob = null
protocol = getProtocol(config.getString("protocol"))
protocol?.startVpn(config, Builder(), ::protect)
vpnProto?.protocol?.let { protocol ->
protocol.initialize(applicationContext, protocolState, ::onError)
protocol.startVpn(config, Builder(), ::protect)
}
}
}
@@ -520,8 +534,8 @@ class AmneziaVpnService : VpnService() {
connectionJob?.join()
connectionJob = null
protocol?.stopVpn()
protocol = null
vpnProto?.protocol?.stopVpn()
try {
withTimeout(DISCONNECT_TIMEOUT) {
// waiting for disconnect state
@@ -543,22 +557,10 @@ class AmneziaVpnService : VpnService() {
protocolState.value = RECONNECTING
connectionJob = connectionScope.launch {
protocol?.reconnectVpn(Builder())
vpnProto?.protocol?.reconnectVpn(Builder())
}
}
@MainThread
private fun getProtocol(protocolName: String): Protocol =
protocolCache[protocolName]
?: when (protocolName) {
"wireguard" -> Wireguard()
"awg" -> Awg()
"openvpn" -> OpenVpn()
"cloak" -> Cloak()
else -> throw IllegalArgumentException("Protocol '$protocolName' not found")
}.apply { initialize(applicationContext, protocolState, ::onError) }
.also { protocolCache[protocolName] = it }
/**
* Utils methods
*/
@@ -603,6 +605,7 @@ class AmneziaVpnService : VpnService() {
if (prepare(applicationContext) != null) {
Intent(this, VpnRequestActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
putExtra(EXTRA_PROTOCOL, vpnProto)
}.also {
startActivity(it)
}
@@ -612,9 +615,9 @@ class AmneziaVpnService : VpnService() {
}
companion object {
fun isRunning(context: Context): Boolean =
fun isRunning(context: Context, processName: String): Boolean =
context.getSystemService<ActivityManager>()!!.runningAppProcesses.any {
it.processName == PROCESS_NAME && it.importance <= IMPORTANCE_FOREGROUND_SERVICE
it.processName == processName && it.importance <= IMPORTANCE_FOREGROUND_SERVICE
}
}
}

View File

@@ -0,0 +1,3 @@
package org.amnezia.vpn
class AwgService : AmneziaVpnService()

View File

@@ -140,7 +140,7 @@ class CameraActivity : ComponentActivity() {
}
}
}.addOnFailureListener {
Log.e(TAG, "Processing QR-code image failed: ${it.message}")
Log.e(TAG, "Processing QR code image failed: ${it.message}")
}.addOnCompleteListener {
imageProxy.close()
}

View File

@@ -0,0 +1,3 @@
package org.amnezia.vpn
class OpenVpnService : AmneziaVpnService()

View File

@@ -59,14 +59,14 @@ class ServiceNotification(private val context: Context) {
formatSpeedString(rxString, txString)
}
fun buildNotification(serverName: String?, state: ProtocolState): Notification {
fun buildNotification(serverName: String?, protocol: String?, state: ProtocolState): Notification {
val speedString = if (state == CONNECTED) zeroSpeed else null
Log.d(TAG, "Build notification: $serverName, $state")
return notificationBuilder
.setSmallIcon(R.drawable.ic_amnezia_round)
.setContentTitle(serverName ?: "AmneziaVPN")
.setContentTitle((serverName ?: "AmneziaVPN") + (protocol?.let { " $it" } ?: ""))
.setContentText(context.getString(state))
.setSubText(speedString)
.setWhen(System.currentTimeMillis())
@@ -96,10 +96,10 @@ class ServiceNotification(private val context: Context) {
}
@SuppressLint("MissingPermission")
fun updateNotification(serverName: String?, state: ProtocolState) {
fun updateNotification(serverName: String?, protocol: String?, state: ProtocolState) {
if (context.isNotificationPermissionGranted()) {
Log.d(TAG, "Update notification: $serverName, $state")
notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, state))
notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, protocol, state))
}
}
@@ -125,7 +125,7 @@ class ServiceNotification(private val context: Context) {
context,
DISCONNECT_REQUEST_CODE,
Intent(ACTION_DISCONNECT).apply {
setPackage("org.amnezia.vpn")
setPackage(context.packageName)
},
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
@@ -135,10 +135,12 @@ class ServiceNotification(private val context: Context) {
DISCONNECTED -> {
Action(
0, context.getString(R.string.connect),
createServicePendingIntent(
PendingIntent.getBroadcast(
context,
CONNECT_REQUEST_CODE,
Intent(context, AmneziaVpnService::class.java),
Intent(ACTION_CONNECT).apply {
setPackage(context.packageName)
},
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
)
@@ -148,13 +150,6 @@ class ServiceNotification(private val context: Context) {
}
}
private val createServicePendingIntent: (Context, Int, Intent, Int) -> PendingIntent =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
PendingIntent::getForegroundService
} else {
PendingIntent::getService
}
companion object {
fun createNotificationChannel(context: Context) {
with(NotificationManagerCompat.from(context)) {

View File

@@ -0,0 +1,75 @@
package org.amnezia.vpn
import org.amnezia.vpn.protocol.Protocol
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.wireguard.Wireguard
import org.amnezia.vpn.protocol.xray.Xray
enum class VpnProto(
val label: String,
val processName: String,
val serviceClass: Class<out AmneziaVpnService>
) {
WIREGUARD(
"WireGuard",
"org.amnezia.vpn:amneziaAwgService",
AwgService::class.java
) {
override fun createProtocol(): Protocol = Wireguard()
},
AWG(
"AmneziaWG",
"org.amnezia.vpn:amneziaAwgService",
AwgService::class.java
) {
override fun createProtocol(): Protocol = Awg()
},
OPENVPN(
"OpenVPN",
"org.amnezia.vpn:amneziaOpenVpnService",
OpenVpnService::class.java
) {
override fun createProtocol(): Protocol = OpenVpn()
},
CLOAK(
"Cloak",
"org.amnezia.vpn:amneziaOpenVpnService",
OpenVpnService::class.java
) {
override fun createProtocol(): Protocol = Cloak()
},
XRAY(
"XRay",
"org.amnezia.vpn:amneziaXrayService",
XrayService::class.java
) {
override fun createProtocol(): Protocol = Xray.instance
},
SSXRAY(
"SSXRay",
"org.amnezia.vpn:amneziaXrayService",
XrayService::class.java
) {
override fun createProtocol(): Protocol = Xray.instance
};
private var _protocol: Protocol? = null
val protocol: Protocol
get() {
if (_protocol == null) _protocol = createProtocol()
return _protocol ?: throw AssertionError("Set to null by another thread")
}
protected abstract fun createProtocol(): Protocol
companion object {
fun get(protocolName: String): VpnProto = VpnProto.valueOf(protocolName.uppercase())
}
}

View File

@@ -7,6 +7,7 @@ import android.content.Intent
import android.content.res.Configuration.UI_MODE_NIGHT_MASK
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.net.VpnService
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.widget.Toast
@@ -18,9 +19,11 @@ import androidx.core.content.getSystemService
import org.amnezia.vpn.util.Log
private const val TAG = "VpnRequestActivity"
const val EXTRA_PROTOCOL = "PROTOCOL"
class VpnRequestActivity : ComponentActivity() {
private var vpnProto: VpnProto? = null
private var userPresentReceiver: BroadcastReceiver? = null
private val requestLauncher =
registerForActivityResult(StartActivityForResult(), ::checkRequestResult)
@@ -28,6 +31,12 @@ class VpnRequestActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "Start request activity")
vpnProto = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.extras?.getSerializable(EXTRA_PROTOCOL, VpnProto::class.java)
} else {
@Suppress("DEPRECATION")
intent.extras?.getSerializable(EXTRA_PROTOCOL) as VpnProto
}
val requestIntent = VpnService.prepare(applicationContext)
if (requestIntent != null) {
if (getSystemService<KeyguardManager>()!!.isKeyguardLocked) {
@@ -66,10 +75,18 @@ class VpnRequestActivity : ComponentActivity() {
private fun onPermissionGranted() {
Toast.makeText(this, resources.getString(R.string.vpnGranted), Toast.LENGTH_LONG).show()
Intent(applicationContext, AmneziaVpnService::class.java).apply {
putExtra(AFTER_PERMISSION_CHECK, true)
}.also {
ContextCompat.startForegroundService(this, it)
vpnProto?.let { proto ->
Intent(applicationContext, proto.serviceClass).apply {
putExtra(AFTER_PERMISSION_CHECK, true)
}.also {
ContextCompat.startForegroundService(this, it)
}
} ?: run {
Intent(this, AmneziaActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}.also {
startActivity(it)
}
}
}

View File

@@ -1,19 +1,22 @@
package org.amnezia.vpn
import android.app.Application
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.MultiProcessDataStoreFactory
import androidx.datastore.core.Serializer
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import androidx.datastore.dataStoreFile
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
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 kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationException
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.protobuf.ProtoBuf
import org.amnezia.vpn.protocol.ProtocolState
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
import org.amnezia.vpn.util.Log
@@ -21,13 +24,14 @@ import org.amnezia.vpn.util.Log
private const val TAG = "VpnState"
private const val STORE_FILE_NAME = "vpnState"
@Serializable
data class VpnState(
val protocolState: ProtocolState,
val serverName: String? = null,
val serverIndex: Int = -1
) : Serializable {
val serverIndex: Int = -1,
val vpnProto: VpnProto? = null
) {
companion object {
private const val serialVersionUID: Long = -1760654961004181606
val defaultState: VpnState = VpnState(DISCONNECTED)
}
}
@@ -37,7 +41,11 @@ object VpnStateStore {
private val dataStore = MultiProcessDataStoreFactory.create(
serializer = VpnStateSerializer(),
produceFile = { app.dataStoreFile(STORE_FILE_NAME) }
produceFile = { app.dataStoreFile(STORE_FILE_NAME) },
corruptionHandler = ReplaceFileCorruptionHandler { e ->
Log.e(TAG, "VpnState DataStore corrupted: $e")
VpnState.defaultState
}
)
fun init(app: Application) {
@@ -45,36 +53,36 @@ object VpnStateStore {
this.app = app
}
fun dataFlow(): Flow<VpnState> = dataStore.data
fun dataFlow(): Flow<VpnState> = dataStore.data.catch { e ->
Log.e(TAG, "Failed to read VpnState from store: ${e.message}")
emit(VpnState.defaultState)
}
suspend fun getVpnState(): VpnState = dataFlow().firstOrNull() ?: VpnState.defaultState
suspend fun store(f: (vpnState: VpnState) -> VpnState) {
try {
dataStore.updateData(f)
} catch (e : Exception) {
} catch (e: Exception) {
Log.e(TAG, "Failed to store VpnState: $e")
Log.w(TAG, "Remove DataStore file")
app.dataStoreFile(STORE_FILE_NAME).delete()
}
}
}
@OptIn(ExperimentalSerializationApi::class)
private class VpnStateSerializer : Serializer<VpnState> {
override val defaultValue: VpnState = VpnState.defaultState
override suspend fun readFrom(input: InputStream): VpnState {
return withContext(Dispatchers.IO) {
val bios = ByteArrayInputStream(input.readBytes())
ObjectInputStream(bios).use {
it.readObject() as VpnState
}
}
override suspend fun readFrom(input: InputStream): VpnState = try {
ProtoBuf.decodeFromByteArray<VpnState>(input.readBytes())
} catch (e: SerializationException) {
Log.e(TAG, "Failed to deserialize data: $e")
throw CorruptionException("Failed to deserialize data", e)
}
override suspend fun writeTo(t: VpnState, output: OutputStream) {
withContext(Dispatchers.IO) {
val baos = ByteArrayOutputStream()
ObjectOutputStream(baos).use {
it.writeObject(t)
}
output.write(baos.toByteArray())
}
}
@Suppress("BlockingMethodInNonBlockingContext")
override suspend fun writeTo(t: VpnState, output: OutputStream) =
output.write(ProtoBuf.encodeToByteArray(t))
}

View File

@@ -0,0 +1,3 @@
package org.amnezia.vpn
class XrayService : AmneziaVpnService()

View File

@@ -1,12 +1,9 @@
package org.amnezia.vpn.protocol.wireguard
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
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
import org.amnezia.vpn.protocol.Statistics
@@ -78,9 +75,8 @@ open class Wireguard : Protocol() {
}
}
override fun initialize(context: Context, state: MutableStateFlow<ProtocolState>, onError: (String) -> Unit) {
super.initialize(context, state, onError)
loadSharedLibrary(context, "wg-go")
override fun internalInit() {
if (!isInitialized) loadSharedLibrary(context, "wg-go")
}
override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {

View File

@@ -0,0 +1,19 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
}
kotlin {
jvmToolchain(17)
}
android {
namespace = "org.amnezia.vpn.protocol.xray"
}
dependencies {
compileOnly(project(":utils"))
compileOnly(project(":protocolApi"))
implementation(project(":xray:libXray"))
implementation(libs.kotlinx.coroutines)
}

View File

@@ -0,0 +1,6 @@
@file:Suppress("UnstableApiUsage")
configurations {
maybeCreate("default")
}
artifacts.add("default", file("libxray.aar"))

View File

@@ -0,0 +1,239 @@
package org.amnezia.vpn.protocol.xray
import android.content.Context
import android.net.VpnService.Builder
import java.io.File
import java.io.IOException
import go.Seq
import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.Protocol
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
import org.amnezia.vpn.protocol.Statistics
import org.amnezia.vpn.protocol.VpnStartException
import org.amnezia.vpn.protocol.xray.libXray.DialerController
import org.amnezia.vpn.protocol.xray.libXray.LibXray
import org.amnezia.vpn.protocol.xray.libXray.Logger
import org.amnezia.vpn.protocol.xray.libXray.Tun2SocksConfig
import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.net.InetNetwork
import org.amnezia.vpn.util.net.parseInetAddress
import org.json.JSONObject
/**
* Config example:
* {
* "appSplitTunnelType": 0,
* "config_version": 0,
* "description": "Server 1",
* "dns1": "1.1.1.1",
* "dns2": "1.0.0.1",
* "hostName": "100.100.100.0",
* "protocol": "xray",
* "splitTunnelApps": [],
* "splitTunnelSites": [],
* "splitTunnelType": 0,
* "xray_config_data": {
* "inbounds": [
* {
* "listen": "127.0.0.1",
* "port": 8080,
* "protocol": "socks",
* "settings": {
* "udp": true
* }
* }
* ],
* "log": {
* "loglevel": "error"
* },
* "outbounds": [
* {
* "protocol": "vless",
* "settings": {
* "vnext": [
* {
* "address": "100.100.100.0",
* "port": 443,
* "users": [
* {
* "encryption": "none",
* "flow": "xtls-rprx-vision",
* "id": "id"
* }
* ]
* }
* ]
* },
* "streamSettings": {
* "network": "tcp",
* "realitySettings": {
* "fingerprint": "chrome",
* "publicKey": "publicKey",
* "serverName": "google.com",
* "shortId": "id",
* "spiderX": ""
* },
* "security": "reality"
* }
* }
* ]
* }
* }
*
*/
private const val TAG = "Xray"
private const val LIBXRAY_TAG = "libXray"
class Xray : Protocol() {
private var isRunning: Boolean = false
override val statistics: Statistics = Statistics.EMPTY_STATISTICS
override fun internalInit() {
Seq.setContext(context)
if (!isInitialized) {
LibXray.initLogger(object : Logger {
override fun warning(s: String) = Log.w(LIBXRAY_TAG, s)
override fun error(s: String) = Log.e(LIBXRAY_TAG, s)
override fun write(msg: ByteArray): Long {
Log.w(LIBXRAY_TAG, String(msg))
return msg.size.toLong()
}
}).isNotNullOrBlank { err ->
Log.w(TAG, "Failed to initialize logger: $err")
}
}
}
override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
if (isRunning) {
Log.w(TAG, "XRay already running")
return
}
val xrayJsonConfig = config.optJSONObject("xray_config_data")
?: config.optJSONObject("ssxray_config_data")
?: throw BadConfigException("config_data not found")
val xrayConfig = parseConfig(config, xrayJsonConfig)
(xrayJsonConfig.optJSONObject("log") ?: JSONObject().also { xrayJsonConfig.put("log", it) })
.put("loglevel", "warning")
.put("access", "none") // disable access log
start(xrayConfig, xrayJsonConfig.toString(), vpnBuilder, protect)
state.value = CONNECTED
isRunning = true
}
private fun parseConfig(config: JSONObject, xrayJsonConfig: JSONObject): XrayConfig {
return XrayConfig.build {
addAddress(XrayConfig.DEFAULT_IPV4_ADDRESS)
config.optString("dns1").let {
if (it.isNotBlank()) addDnsServer(parseInetAddress(it))
}
config.optString("dns2").let {
if (it.isNotBlank()) addDnsServer(parseInetAddress(it))
}
addRoute(InetNetwork("0.0.0.0", 0))
addRoute(InetNetwork("2000::0", 3))
config.getString("hostName").let {
excludeRoute(InetNetwork(it, 32))
}
config.optString("mtu").let {
if (it.isNotBlank()) setMtu(it.toInt())
}
val socksConfig = xrayJsonConfig.getJSONArray("inbounds")[0] as JSONObject
socksConfig.getInt("port").let { setSocksPort(it) }
configSplitTunneling(config)
configAppSplitTunneling(config)
}
}
private fun start(config: XrayConfig, configJson: String, vpnBuilder: Builder, protect: (Int) -> Boolean) {
buildVpnInterface(config, vpnBuilder)
DialerController { protect(it.toInt()) }.also {
LibXray.registerDialerController(it).isNotNullOrBlank { err ->
throw VpnStartException("Failed to register dialer controller: $err")
}
LibXray.registerListenerController(it).isNotNullOrBlank { err ->
throw VpnStartException("Failed to register listener controller: $err")
}
}
vpnBuilder.establish().use { tunFd ->
if (tunFd == null) {
throw VpnStartException("Create VPN interface: permission not granted or revoked")
}
Log.d(TAG, "Run tun2Socks")
runTun2Socks(config, tunFd.detachFd())
Log.d(TAG, "Run XRay")
Log.i(TAG, "xray ${LibXray.xrayVersion()}")
val assetsPath = context.getDir("assets", Context.MODE_PRIVATE).absolutePath
LibXray.initXray(assetsPath)
val geoDir = File(assetsPath, "geo").absolutePath
val configPath = File(context.cacheDir, "config.json")
Log.d(TAG, "xray.location.asset: $geoDir")
Log.d(TAG, "config: $configPath")
try {
configPath.writeText(configJson)
} catch (e: IOException) {
LibXray.stopTun2Socks()
throw VpnStartException("Failed to write xray config: ${e.message}")
}
LibXray.runXray(geoDir, configPath.absolutePath, config.maxMemory).isNotNullOrBlank { err ->
LibXray.stopTun2Socks()
throw VpnStartException("Failed to start xray: $err")
}
}
}
override fun stopVpn() {
LibXray.stopXray().isNotNullOrBlank { err ->
Log.e(TAG, "Failed to stop XRay: $err")
}
LibXray.stopTun2Socks().isNotNullOrBlank { err ->
Log.e(TAG, "Failed to stop tun2Socks: $err")
}
isRunning = false
state.value = DISCONNECTED
}
override fun reconnectVpn(vpnBuilder: Builder) {
state.value = CONNECTED
}
private fun runTun2Socks(config: XrayConfig, fd: Int) {
val tun2SocksConfig = Tun2SocksConfig().apply {
mtu = config.mtu.toLong()
proxy = "socks5://127.0.0.1:${config.socksPort}"
device = "fd://$fd"
logLevel = "warning"
}
LibXray.startTun2Socks(tun2SocksConfig, fd.toLong()).isNotNullOrBlank { err ->
throw VpnStartException("Failed to start tun2socks: $err")
}
}
companion object {
val instance: Xray by lazy { Xray() }
}
}
private fun String?.isNotNullOrBlank(block: (String) -> Unit) {
if (!this.isNullOrBlank()) {
block(this)
}
}

View File

@@ -0,0 +1,42 @@
package org.amnezia.vpn.protocol.xray
import org.amnezia.vpn.protocol.ProtocolConfig
import org.amnezia.vpn.util.net.InetNetwork
private const val XRAY_DEFAULT_MTU = 1500
private const val XRAY_DEFAULT_MAX_MEMORY: Long = 50 shl 20 // 50 MB
class XrayConfig protected constructor(
protocolConfigBuilder: ProtocolConfig.Builder,
val socksPort: Int,
val maxMemory: Long,
) : ProtocolConfig(protocolConfigBuilder) {
protected constructor(builder: Builder) : this(
builder,
builder.socksPort,
builder.maxMemory
)
class Builder : ProtocolConfig.Builder(false) {
internal var socksPort: Int = 0
private set
internal var maxMemory: Long = XRAY_DEFAULT_MAX_MEMORY
private set
override var mtu: Int = XRAY_DEFAULT_MTU
fun setSocksPort(port: Int) = apply { socksPort = port }
fun setMaxMemory(maxMemory: Long) = apply { this.maxMemory = maxMemory }
override fun build(): XrayConfig = configBuild().run { XrayConfig(this@Builder) }
}
companion object {
internal val DEFAULT_IPV4_ADDRESS: InetNetwork = InetNetwork("10.0.42.2", 30)
inline fun build(block: Builder.() -> Unit): XrayConfig = Builder().apply(block).build()
}
}

View File

@@ -52,3 +52,6 @@ foreach(abi IN ITEMS ${QT_ANDROID_ABIS})
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/libssh/android/${abi}/libssh.so
)
endforeach()
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/xray/android/libxray.aar
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/android/xray/libXray)

View File

@@ -69,6 +69,8 @@ QVector<amnezia::Proto> ContainerProps::protocolsForContainer(amnezia::DockerCon
case DockerContainer::Sftp: return { Proto::Sftp };
case DockerContainer::Socks5Proxy: return { Proto::Socks5Proxy };
default: return { defaultProtocol(container) };
}
}
@@ -88,7 +90,7 @@ QMap<DockerContainer, QString> ContainerProps::containerHumanNames()
{
return { { DockerContainer::None, "Not installed" },
{ DockerContainer::OpenVpn, "OpenVPN" },
{ DockerContainer::ShadowSocks, "ShadowSocks" },
{ DockerContainer::ShadowSocks, "OpenVPN over SS" },
{ DockerContainer::Cloak, "OpenVPN over Cloak" },
{ DockerContainer::WireGuard, "WireGuard" },
{ DockerContainer::Awg, "AmneziaWG" },
@@ -97,8 +99,9 @@ QMap<DockerContainer, QString> ContainerProps::containerHumanNames()
{ DockerContainer::SSXray, "ShadowSocks"},
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
{ DockerContainer::Dns, QObject::tr("Amnezia DNS") },
{ DockerContainer::Sftp, QObject::tr("Sftp file sharing service") } };
{ DockerContainer::Dns, QObject::tr("AmneziaDNS") },
{ DockerContainer::Sftp, QObject::tr("SFTP file sharing service") },
{ DockerContainer::Socks5Proxy, QObject::tr("SOCKS5 proxy server") } };
}
QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
@@ -107,7 +110,7 @@ QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
QObject::tr("OpenVPN is the most popular VPN protocol, with flexible configuration options. It uses its "
"own security protocol with SSL/TLS for key exchange.") },
{ DockerContainer::ShadowSocks,
QObject::tr("ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but it "
QObject::tr("Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it "
"may be recognized by analysis systems in some highly censored regions.") },
{ DockerContainer::Cloak,
QObject::tr("OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against "
@@ -124,14 +127,16 @@ QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
QObject::tr("XRay with REALITY - Suitable for countries with the highest level of internet censorship. "
"Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods.") },
{ DockerContainer::Ipsec,
QObject::tr("IKEv2 - Modern stable protocol, a bit faster than others, restores connection after "
QObject::tr("IKEv2/IPsec - 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.") },
{ DockerContainer::TorWebSite, QObject::tr("Deploy a WordPress site on the Tor network in two clicks.") },
{ DockerContainer::Dns,
QObject::tr("Replace the current DNS server with your own. This will increase your privacy level.") },
{ DockerContainer::Sftp,
QObject::tr("Create a file vault on your server to securely store and transfer files.") } };
QObject::tr("Create a file vault on your server to securely store and transfer files.") },
{ DockerContainer::Socks5Proxy,
QObject::tr("") } };
}
QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
@@ -159,7 +164,6 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
"However, certain traffic analysis systems might still detect a Shadowsocks connection. "
"Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.\n\n"
"* Available in the AmneziaVPN only on desktop platforms\n"
"* Normal power consumption on mobile devices\n\n"
"* Configurable encryption protocol\n"
"* Detectable by some DPI systems\n"
"* Works over TCP network protocol.") },
@@ -240,7 +244,8 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
QObject::tr("After installation, Amnezia will create a\n\n file storage on your server. "
"You will be able to access it using\n FileZilla or other SFTP clients, "
"as well as mount the disk on your device to access\n it directly from your device.\n\n"
"For more detailed information, you can\n find it in the support section under \"Create SFTP file storage.\" ") }
"For more detailed information, you can\n find it in the support section under \"Create SFTP file storage.\" ") },
{ DockerContainer::Socks5Proxy, QObject::tr("SOCKS5 proxy server") }
};
}
@@ -265,6 +270,7 @@ Proto ContainerProps::defaultProtocol(DockerContainer c)
case DockerContainer::TorWebSite: return Proto::TorWebSite;
case DockerContainer::Dns: return Proto::Dns;
case DockerContainer::Sftp: return Proto::Sftp;
case DockerContainer::Socks5Proxy: return Proto::Socks5Proxy;
default: return Proto::Any;
}
}
@@ -279,6 +285,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
case DockerContainer::WireGuard: return true;
case DockerContainer::OpenVpn: return true;
case DockerContainer::Awg: return true;
case DockerContainer::Xray: return true;
case DockerContainer::Cloak:
return true;
// case DockerContainer::ShadowSocks: return true;
@@ -298,6 +305,8 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
case DockerContainer::ShadowSocks: return false;
case DockerContainer::Awg: return true;
case DockerContainer::Cloak: return true;
case DockerContainer::Xray: return true;
case DockerContainer::SSXray: return true;
default: return false;
}
@@ -367,6 +376,7 @@ bool ContainerProps::isShareable(DockerContainer container)
case DockerContainer::TorWebSite: return false;
case DockerContainer::Dns: return false;
case DockerContainer::Sftp: return false;
case DockerContainer::Socks5Proxy: return false;
default: return true;
}
}
@@ -380,3 +390,18 @@ QJsonObject ContainerProps::getProtocolConfigFromContainer(const Proto protocol,
return QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
}
int ContainerProps::installPageOrder(DockerContainer container)
{
switch (container) {
case DockerContainer::OpenVpn: return 4;
case DockerContainer::Cloak: return 5;
case DockerContainer::ShadowSocks: return 6;
case DockerContainer::WireGuard: return 2;
case DockerContainer::Awg: return 1;
case DockerContainer::Xray: return 3;
case DockerContainer::Ipsec: return 7;
case DockerContainer::SSXray: return 8;
default: return 0;
}
}

View File

@@ -28,7 +28,8 @@ namespace amnezia
// non-vpn
TorWebSite,
Dns,
Sftp
Sftp,
Socks5Proxy
};
Q_ENUM_NS(DockerContainer)
} // namespace ContainerEnumNS
@@ -71,6 +72,8 @@ namespace amnezia
static bool isShareable(amnezia::DockerContainer container);
static QJsonObject getProtocolConfigFromContainer(const amnezia::Proto protocol, const QJsonObject &containerConfig);
static int installPageOrder(amnezia::DockerContainer container);
};
static void declareQmlContainerEnum()

View File

@@ -40,6 +40,28 @@ void ApiController::processApiConfig(const QString &protocol, const ApiControlle
return;
} else if (protocol == configKey::awg) {
config.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
auto serverConfig = QJsonDocument::fromJson(config.toUtf8()).object();
auto containers = serverConfig.value(config_key::containers).toArray();
if (containers.isEmpty()) {
return;
}
auto container = containers.at(0).toObject();
QString containerName = ContainerProps::containerTypeToString(DockerContainer::Awg);
auto containerConfig = container.value(containerName).toObject();
auto protocolConfig = QJsonDocument::fromJson(containerConfig.value(config_key::last_config).toString().toUtf8()).object();
containerConfig[config_key::junkPacketCount] = protocolConfig.value(config_key::junkPacketCount);
containerConfig[config_key::junkPacketMinSize] = protocolConfig.value(config_key::junkPacketMinSize);
containerConfig[config_key::junkPacketMaxSize] = protocolConfig.value(config_key::junkPacketMaxSize);
containerConfig[config_key::initPacketJunkSize] = protocolConfig.value(config_key::initPacketJunkSize);
containerConfig[config_key::responsePacketJunkSize] = protocolConfig.value(config_key::responsePacketJunkSize);
containerConfig[config_key::initPacketMagicHeader] = protocolConfig.value(config_key::initPacketMagicHeader);
containerConfig[config_key::responsePacketMagicHeader] = protocolConfig.value(config_key::responsePacketMagicHeader);
containerConfig[config_key::underloadPacketMagicHeader] = protocolConfig.value(config_key::underloadPacketMagicHeader);
containerConfig[config_key::transportPacketMagicHeader] = protocolConfig.value(config_key::transportPacketMagicHeader);
container[containerName] = containerConfig;
containers.replace(0, container);
serverConfig[config_key::containers] = containers;
config = QString(QJsonDocument(serverConfig).toJson());
}
return;
}

View File

@@ -106,7 +106,7 @@ ErrorCode ServerController::runContainerScript(const ServerCredentials &credenti
if (e)
return e;
QString runner = QString("sudo docker exec -i $CONTAINER_NAME bash %1 ").arg(fileName);
QString runner = QString("sudo docker exec -i $CONTAINER_NAME %2 %1 ").arg(fileName, (container == DockerContainer::Socks5Proxy ? "sh" : "bash"));
e = runScript(credentials, replaceVars(runner, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
QString remover = QString("sudo docker exec -i $CONTAINER_NAME rm %1 ").arg(fileName);
@@ -376,6 +376,10 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
return true;
}
if (container == DockerContainer::Socks5Proxy) {
return true;
}
return false;
}
@@ -516,6 +520,7 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
const QJsonObject &amneziaWireguarConfig = config.value(ProtocolProps::protoToString(Proto::Awg)).toObject();
const QJsonObject &xrayConfig = config.value(ProtocolProps::protoToString(Proto::Xray)).toObject();
const QJsonObject &sftpConfig = config.value(ProtocolProps::protoToString(Proto::Sftp)).toObject();
const QJsonObject &socks5ProxyConfig = config.value(ProtocolProps::protoToString(Proto::Socks5Proxy)).toObject();
Vars vars;
@@ -613,6 +618,14 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } });
vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } });
// Socks5 proxy vars
vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } });
auto username = socks5ProxyConfig.value(config_key:: userName).toString();
auto password = socks5ProxyConfig.value(config_key::password).toString();
QString socks5user = (!username.isEmpty() && !password.isEmpty()) ? QString("users %1:CL:%2").arg(username, password) : "";
vars.append({ { "$SOCKS5_USER", socks5user } });
vars.append({ { "$SOCKS5_AUTH_TYPE", socks5user.isEmpty() ? "none" : "strong" } });
QString serverIp = NetworkUtilities::getIPAddress(credentials.hostName);
if (!serverIp.isEmpty()) {
vars.append({ { "$SERVER_IP_ADDRESS", serverIp } });

View File

@@ -9,7 +9,7 @@ QString errorString(ErrorCode code) {
// General error codes
case(ErrorCode::NoError): errorMessage = QObject::tr("No error"); break;
case(ErrorCode::UnknownError): errorMessage = QObject::tr("Unknown Error"); break;
case(ErrorCode::UnknownError): errorMessage = QObject::tr("Unknown error"); break;
case(ErrorCode::NotImplementedError): errorMessage = QObject::tr("Function not implemented"); break;
case(ErrorCode::AmneziaServiceNotRunning): errorMessage = QObject::tr("Background service is not running"); break;
@@ -23,15 +23,15 @@ QString errorString(ErrorCode code) {
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break;
// Libssh errors
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("Ssh request was denied"); break;
case(ErrorCode::SshInterruptedError): errorMessage = QObject::tr("Ssh request was interrupted"); break;
case(ErrorCode::SshInternalError): errorMessage = QObject::tr("Ssh internal error"); break;
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
case(ErrorCode::SshInterruptedError): errorMessage = QObject::tr("SSH request was interrupted"); break;
case(ErrorCode::SshInternalError): errorMessage = QObject::tr("SSH internal error"); break;
case(ErrorCode::SshPrivateKeyError): errorMessage = QObject::tr("Invalid private key or invalid passphrase entered"); break;
case(ErrorCode::SshPrivateKeyFormatError): errorMessage = QObject::tr("The selected private key format is not supported, use openssh ED25519 key types or PEM key types"); break;
case(ErrorCode::SshTimeoutError): errorMessage = QObject::tr("Timeout connecting to server"); break;
// Ssh scp errors
case(ErrorCode::SshScpFailureError): errorMessage = QObject::tr("Scp error: Generic failure"); break;
case(ErrorCode::SshScpFailureError): errorMessage = QObject::tr("SCP error: Generic failure"); break;
// Local errors
case (ErrorCode::OpenVpnConfigMissing): errorMessage = QObject::tr("OpenVPN config missing"); break;
@@ -39,7 +39,7 @@ QString errorString(ErrorCode code) {
// Distro errors
case (ErrorCode::OpenVpnExecutableMissing): errorMessage = QObject::tr("OpenVPN executable missing"); break;
case (ErrorCode::ShadowSocksExecutableMissing): errorMessage = QObject::tr("ShadowSocks (ss-local) executable missing"); break;
case (ErrorCode::ShadowSocksExecutableMissing): errorMessage = QObject::tr("Shadowsocks (ss-local) executable missing"); break;
case (ErrorCode::CloakExecutableMissing): errorMessage = QObject::tr("Cloak (ck-client) executable missing"); break;
case (ErrorCode::AmneziaServiceConnectionFailed): errorMessage = QObject::tr("Amnezia helper service error"); break;
case (ErrorCode::OpenSslFailed): errorMessage = QObject::tr("OpenSSL failed"); break;

View File

@@ -18,6 +18,7 @@ QString amnezia::scriptFolder(amnezia::DockerContainer container)
case DockerContainer::TorWebSite: return QLatin1String("website_tor");
case DockerContainer::Dns: return QLatin1String("dns");
case DockerContainer::Sftp: return QLatin1String("sftp");
case DockerContainer::Socks5Proxy: return QLatin1String("socks5_proxy");
default: return QString();
}
}

View File

@@ -50,10 +50,12 @@ set_target_properties("networkextension" PROPERTIES
find_library(FW_ASSETS_LIBRARY AssetsLibrary)
find_library(FW_MOBILE_CORE MobileCoreServices)
find_library(FW_UI_KIT UIKit)
find_library(FW_LIBRESOLV libresolv.9.tbd)
target_link_libraries(networkextension PRIVATE ${FW_ASSETS_LIBRARY})
target_link_libraries(networkextension PRIVATE ${FW_MOBILE_CORE})
target_link_libraries(networkextension PRIVATE ${FW_UI_KIT})
target_link_libraries(networkextension PRIVATE ${FW_LIBRESOLV})
target_compile_options(networkextension PRIVATE -DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\")
target_compile_options(networkextension PRIVATE -DNETWORK_EXTENSION=1)
@@ -80,12 +82,14 @@ target_sources(networkextension PRIVATE
${WG_APPLE_SOURCE_DIR}/WireGuardKit/Array+ConcurrentMap.swift
${WG_APPLE_SOURCE_DIR}/WireGuardKit/IPAddress+AddrInfo.swift
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PrivateKey.swift
${CLIENT_ROOT_DIR}/platforms/ios/HevSocksTunnel.swift
${CLIENT_ROOT_DIR}/platforms/ios/NELogController.swift
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider.swift
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+WireGuard.swift
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+OpenVPN.swift
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+Xray.swift
${CLIENT_ROOT_DIR}/platforms/ios/WGConfig.swift
${CLIENT_ROOT_DIR}/platforms/ios/iosglue.mm
)
@@ -114,3 +118,5 @@ target_include_directories(networkextension PRIVATE ${CLIENT_ROOT_DIR})
target_include_directories(networkextension PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(networkextension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/wireguard/ios/arm64/libwg-go.a)
target_link_libraries(networkextension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework)

View File

@@ -0,0 +1,73 @@
import HevSocks5Tunnel
public enum Socks5Tunnel {
private static var tunnelFileDescriptor: Int32? {
var ctlInfo = ctl_info()
withUnsafeMutablePointer(to: &ctlInfo.ctl_name) {
$0.withMemoryRebound(to: CChar.self, capacity: MemoryLayout.size(ofValue: $0.pointee)) {
_ = strcpy($0, "com.apple.net.utun_control")
}
}
for fd: Int32 in 0...1024 {
var addr = sockaddr_ctl()
var ret: Int32 = -1
var len = socklen_t(MemoryLayout.size(ofValue: addr))
withUnsafeMutablePointer(to: &addr) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
ret = getpeername(fd, $0, &len)
}
}
if ret != 0 || addr.sc_family != AF_SYSTEM {
continue
}
if ctlInfo.ctl_id == 0 {
ret = ioctl(fd, CTLIOCGINFO, &ctlInfo)
if ret != 0 {
continue
}
}
if addr.sc_id == ctlInfo.ctl_id {
return fd
}
}
return nil
}
private static var interfaceName: String? {
guard let tunnelFileDescriptor = self.tunnelFileDescriptor else {
return nil
}
var buffer = [UInt8](repeating: 0, count: Int(IFNAMSIZ))
return buffer.withUnsafeMutableBufferPointer { mutableBufferPointer in
guard let baseAddress = mutableBufferPointer.baseAddress else {
return nil
}
var ifnameSize = socklen_t(IFNAMSIZ)
let result = getsockopt(
tunnelFileDescriptor,
2 /* SYSPROTO_CONTROL */,
2 /* UTUN_OPT_IFNAME */,
baseAddress,
&ifnameSize
)
if result == 0 {
return String(cString: baseAddress)
} else {
return nil
}
}
}
@discardableResult
public static func run(withConfig filePath: String) -> Int32 {
guard let fileDescriptor = self.tunnelFileDescriptor else {
fatalError("Get tunnel file descriptor failed.")
}
return hev_socks5_tunnel_main(filePath.cString(using: .utf8), fileDescriptor)
}
public static func quit() {
hev_socks5_tunnel_quit()
}
}

View File

@@ -13,6 +13,10 @@ public func ovpnLog(_ type: OSLogType, title: String = "", message: String) {
neLog(type, title: "OVPN: \(title)", message: message)
}
public func xrayLog(_ type: OSLogType, title: String = "", message: String) {
neLog(type, title: "XRAY: \(title)", message: message)
}
public func neLog(_ type: OSLogType, title: String = "", message: String) {
Log.log(type, title: "NE: \(title)", message: message)
}

View File

@@ -0,0 +1,166 @@
import Foundation
import NetworkExtension
import WireGuardKitGo
enum XrayErrors: Error {
case noXrayConfig
case cantSaveXrayConfig
case cantParseListenAndPort
case cantSaveHevSocksConfig
}
extension Constants {
static let cachesDirectory: URL = {
if let cachesDirectoryURL = FileManager.default.urls(for: .cachesDirectory,
in: .userDomainMask).first {
return cachesDirectoryURL
} else {
fatalError("Unable to retrieve caches directory.")
}
}()
}
extension PacketTunnelProvider {
func startXray(completionHandler: @escaping (Error?) -> Void) {
// Xray configuration
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration,
let xrayConfigData = providerConfiguration[Constants.xrayConfigKey] as? Data else {
xrayLog(.error, message: "Can't get xray configuration")
completionHandler(XrayErrors.noXrayConfig)
return
}
// Tunnel settings
let ipv6Enabled = true
let hideVPNIcon = false
let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "254.1.1.1")
settings.mtu = 9000
settings.ipv4Settings = {
let settings = NEIPv4Settings(addresses: ["198.18.0.1"], subnetMasks: ["255.255.0.0"])
settings.includedRoutes = [NEIPv4Route.default()]
return settings
}()
settings.ipv6Settings = {
guard ipv6Enabled else {
return nil
}
let settings = NEIPv6Settings(addresses: ["fd6e:a81b:704f:1211::1"], networkPrefixLengths: [64])
settings.includedRoutes = [NEIPv6Route.default()]
if hideVPNIcon {
settings.excludedRoutes = [NEIPv6Route(destinationAddress: "::", networkPrefixLength: 128)]
}
return settings
}()
let dns = ["8.8.4.4","1.1.1.1"]
settings.dnsSettings = NEDNSSettings(servers: dns)
do {
let port = 10808
let address = "::1"
let jsonDict = try JSONSerialization.jsonObject(with: xrayConfigData,
options: []) as? [String: Any]
guard var jsonDict else {
xrayLog(.error, message: "Can't parse address and port for hevSocks")
completionHandler(XrayErrors.cantParseListenAndPort)
return
}
if var inboundsArray = jsonDict["inbounds"] as? [[String: Any]], !inboundsArray.isEmpty {
inboundsArray[0]["port"] = port
inboundsArray[0]["listen"] = address
jsonDict["inbounds"] = inboundsArray
}
let updatedData = try JSONSerialization.data(withJSONObject: jsonDict, options: [])
setTunnelNetworkSettings(settings) { [weak self] error in
if let error {
completionHandler(error)
return
}
// Launch xray
self?.setupAndStartXray(configData: updatedData) { xrayError in
if let xrayError {
completionHandler(xrayError)
return
}
// Launch hevSocks
self?.setupAndRunTun2socks(configData: updatedData,
address: address,
port: port,
completionHandler: completionHandler)
}
}
} catch {
completionHandler(error)
return
}
}
func stopXray(completionHandler: () -> Void) {
Socks5Tunnel.quit()
LibXrayStopXray()
completionHandler()
}
private func setupAndStartXray(configData: Data,
completionHandler: @escaping (Error?) -> Void) {
let path = Constants.cachesDirectory.appendingPathComponent("config.json", isDirectory: false).path
guard FileManager.default.createFile(atPath: path, contents: configData) else {
xrayLog(.error, message: "Can't save xray configuration")
completionHandler(XrayErrors.cantSaveXrayConfig)
return
}
LibXrayRunXray(nil,
path,
Int64.max)
completionHandler(nil)
xrayLog(.info, message: "Xray started")
}
private func setupAndRunTun2socks(configData: Data,
address: String,
port: Int,
completionHandler: @escaping (Error?) -> Void) {
let config = """
tunnel:
mtu: 9000
socks5:
port: \(port)
address: \(address)
udp: 'udp'
misc:
task-stack-size: 20480
connect-timeout: 5000
read-write-timeout: 60000
log-file: stderr
log-level: error
limit-nofile: 65535
"""
let configurationFilePath = Constants.cachesDirectory.appendingPathComponent("config.yml", isDirectory: false).path
guard FileManager.default.createFile(atPath: configurationFilePath, contents: config.data(using: .utf8)!) else {
xrayLog(.info, message: "Cant save hevSocks configuration")
completionHandler(XrayErrors.cantSaveHevSocksConfig)
return
}
DispatchQueue.global().async {
xrayLog(.info, message: "Hev socks started")
completionHandler(nil)
Socks5Tunnel.run(withConfig: configurationFilePath)
}
}
}

View File

@@ -5,7 +5,8 @@ import Darwin
import OpenVPNAdapter
enum TunnelProtoType: String {
case wireguard, openvpn
case wireguard, openvpn, xray
}
struct Constants {
@@ -13,6 +14,7 @@ struct Constants {
static let processQueueName = "org.amnezia.process-packets"
static let kActivationAttemptId = "activationAttemptId"
static let ovpnConfigKey = "ovpn"
static let xrayConfigKey = "xray"
static let wireGuardConfigKey = "wireguard"
static let loggerTag = "NET"
@@ -91,6 +93,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
protoType = .openvpn
} else if (providerConfiguration?[Constants.wireGuardConfigKey] as? Data) != nil {
protoType = .wireguard
} else if (providerConfiguration?[Constants.xrayConfigKey] as? Data) != nil {
protoType = .xray
}
}
@@ -107,6 +111,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
completionHandler: completionHandler)
case .openvpn:
startOpenVPN(completionHandler: completionHandler)
case .xray:
startXray(completionHandler: completionHandler)
}
}
@@ -124,6 +131,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
case .openvpn:
stopOpenVPN(with: reason,
completionHandler: completionHandler)
case .xray:
stopXray(completionHandler: completionHandler)
}
}
@@ -138,6 +147,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
handleWireguardStatusMessage(messageData, completionHandler: completionHandler)
case .openvpn:
handleOpenVPNStatusMessage(messageData, completionHandler: completionHandler)
case .xray:
break;
}
}

View File

@@ -13,7 +13,7 @@ struct WGConfig: Decodable {
let clientIP: String
let clientPrivateKey: String
let serverPublicKey: String
let presharedKey: String
let presharedKey: String?
var allowedIPs: [String]
var persistentKeepAlive: String
let splitTunnelType: Int
@@ -65,7 +65,7 @@ struct WGConfig: Decodable {
\(settings)
[Peer]
PublicKey = \(serverPublicKey)
PresharedKey = \(presharedKey)
\(presharedKey == nil ? "" : "PresharedKey = \(presharedKey!)")
AllowedIPs = \(allowedIPs.joined(separator: ", "))
Endpoint = \(hostName):\(port)
PersistentKeepalive = \(persistentKeepAlive)

View File

@@ -72,9 +72,11 @@ private:
bool setupCloak();
bool setupWireGuard();
bool setupAwg();
bool setupXray();
bool startOpenVPN(const QString &config);
bool startWireGuard(const QString &jsonConfig);
bool startXray(const QString &jsonConfig);
void startTunnel();

View File

@@ -216,6 +216,9 @@ bool IosController::connectVpn(amnezia::Proto proto, const QJsonObject& configur
if (proto == amnezia::Proto::Awg) {
return setupAwg();
}
if (proto == amnezia::Proto::Xray) {
return setupXray();
}
return false;
}
@@ -501,6 +504,15 @@ bool IosController::setupWireGuard()
return startWireGuard(wgConfigDocStr);
}
bool IosController::setupXray()
{
QJsonObject config = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::Xray)].toObject();
QJsonDocument xrayConfigDoc(config);
QString xrayConfigStr(xrayConfigDoc.toJson(QJsonDocument::Compact));
return startXray(xrayConfigStr);
}
bool IosController::setupAwg()
{
QJsonObject config = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::Awg)].toObject();
@@ -590,6 +602,20 @@ bool IosController::startWireGuard(const QString &config)
startTunnel();
}
bool IosController::startXray(const QString &config)
{
qDebug() << "IosController::startXray";
NETunnelProviderProtocol *tunnelProtocol = [[NETunnelProviderProtocol alloc] init];
tunnelProtocol.providerBundleIdentifier = [NSString stringWithUTF8String:VPN_NE_BUNDLEID];
tunnelProtocol.providerConfiguration = @{@"xray": [[NSString stringWithUTF8String:config.toStdString().c_str()] dataUsingEncoding:NSUTF8StringEncoding]};
tunnelProtocol.serverAddress = m_serverAddress;
m_currentTunnel.protocolConfiguration = tunnelProtocol;
startTunnel();
}
void IosController::startTunnel()
{
NSString *protocolName = @"Unknown";

View File

@@ -139,7 +139,7 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
if (config.m_killSwitchEnabled) {
FirewallParams params { };
params.dnsServers.append(config.m_dnsServer);
if (config.m_allowedIPAddressRanges.at(0).toString() == "0.0.0.0/0"){
if (config.m_allowedIPAddressRanges.contains(IPAddress("0.0.0.0/0"))) {
params.blockAll = true;
if (config.m_excludedAddresses.size()) {
params.allowNets = true;

View File

@@ -137,7 +137,8 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) {
if (config.m_killSwitchEnabled) {
FirewallParams params { };
params.dnsServers.append(config.m_dnsServer);
if (config.m_allowedIPAddressRanges.at(0).toString() == "0.0.0.0/0"){
if (config.m_allowedIPAddressRanges.contains(IPAddress("0.0.0.0/0"))) {
params.blockAll = true;
if (config.m_excludedAddresses.size()) {
params.allowNets = true;

View File

@@ -77,7 +77,8 @@ QMap<amnezia::Proto, QString> ProtocolProps::protocolHumanNames()
{ Proto::TorWebSite, "Website in Tor network" },
{ Proto::Dns, "DNS Service" },
{ Proto::Sftp, QObject::tr("Sftp service") } };
{ Proto::Sftp, QObject::tr("SFTP service") },
{ Proto::Socks5Proxy, QObject::tr("SOCKS5 proxy server") } };
}
QMap<amnezia::Proto, QString> ProtocolProps::protocolDescriptions()
@@ -102,6 +103,7 @@ amnezia::ServiceType ProtocolProps::protocolService(Proto p)
case Proto::TorWebSite: return ServiceType::Other;
case Proto::Dns: return ServiceType::Other;
case Proto::Sftp: return ServiceType::Other;
case Proto::Socks5Proxy: return ServiceType::Other;
default: return ServiceType::Other;
}
}
@@ -113,6 +115,7 @@ int ProtocolProps::getPortForInstall(Proto p)
case WireGuard:
case ShadowSocks:
case OpenVpn:
case Socks5Proxy:
return QRandomGenerator::global()->bounded(30000, 50000);
default:
return defaultPort(p);
@@ -135,6 +138,7 @@ int ProtocolProps::defaultPort(Proto p)
case Proto::TorWebSite: return -1;
case Proto::Dns: return 53;
case Proto::Sftp: return 222;
case Proto::Socks5Proxy: return 38080;
default: return -1;
}
}
@@ -154,6 +158,7 @@ bool ProtocolProps::defaultPortChangeable(Proto p)
case Proto::TorWebSite: return false;
case Proto::Dns: return false;
case Proto::Sftp: return true;
case Proto::Socks5Proxy: return true;
default: return false;
}
}
@@ -175,6 +180,7 @@ TransportProto ProtocolProps::defaultTransportProto(Proto p)
case Proto::TorWebSite: return TransportProto::Tcp;
case Proto::Dns: return TransportProto::Udp;
case Proto::Sftp: return TransportProto::Tcp;
case Proto::Socks5Proxy: return TransportProto::Tcp;
}
}
@@ -195,6 +201,7 @@ bool ProtocolProps::defaultTransportProtoChangeable(Proto p)
case Proto::TorWebSite: return false;
case Proto::Dns: return false;
case Proto::Sftp: return false;
case Proto::Socks5Proxy: return false;
default: return false;
}
return false;

View File

@@ -84,6 +84,7 @@ namespace amnezia
constexpr char awg[] = "awg";
constexpr char xray[] = "xray";
constexpr char ssxray[] = "ssxray";
constexpr char socks5proxy[] = "socks5proxy";
constexpr char configVersion[] = "config_version";
@@ -216,6 +217,14 @@ namespace amnezia
constexpr char defaultUnderloadPacketMagicHeader[] = "1766607858";
}
namespace socks5Proxy
{
constexpr char defaultUserName[] = "proxy_user";
constexpr char defaultPort[] = "38080";
constexpr char proxyConfigPath[] = "/usr/local/3proxy/conf/3proxy.cfg";
}
} // namespace protocols
namespace ProtocolEnumNS
@@ -244,7 +253,8 @@ namespace amnezia
// non-vpn
TorWebSite,
Dns,
Sftp
Sftp,
Socks5Proxy
};
Q_ENUM_NS(Proto)

View File

@@ -198,7 +198,7 @@
<file>ui/qml/Pages2/PageProtocolOpenVpnSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolShadowSocksSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolCloakSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolXraySettings.qml</file>
<file>ui/qml/Pages2/PageProtocolXraySettings.qml</file>
<file>ui/qml/Pages2/PageProtocolRaw.qml</file>
<file>ui/qml/Pages2/PageSettingsLogging.qml</file>
<file>ui/qml/Pages2/PageServiceSftpSettings.qml</file>
@@ -239,5 +239,12 @@
<file>images/controls/alert-circle.svg</file>
<file>images/controls/file-check-2.svg</file>
<file>ui/qml/Controls2/WarningType.qml</file>
<file>ui/qml/Modules/Style/qmldir</file>
<file>ui/qml/Modules/Style/AmneziaStyle.qml</file>
<file>ui/qml/Pages2/PageServiceSocksProxySettings.qml</file>
<file>server_scripts/socks5_proxy/run_container.sh</file>
<file>server_scripts/socks5_proxy/Dockerfile</file>
<file>server_scripts/socks5_proxy/configure_container.sh</file>
<file>server_scripts/socks5_proxy/start.sh</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,10 @@
FROM 3proxy/3proxy:latest
LABEL maintainer="AmneziaVPN"
RUN mkdir -p /opt/amnezia
RUN echo -e "#!/bin/bash\ntail -f /dev/null" > /opt/amnezia/start.sh
RUN chmod a+x /opt/amnezia/start.sh
ENTRYPOINT [ "/bin/sh", "/opt/amnezia/start.sh" ]
CMD [ "" ]

View File

@@ -0,0 +1,12 @@
#!/bin/sh
echo -e "#!/bin/3proxy" > /usr/local/3proxy/conf/3proxy.cfg
echo -e "config /usr/local/3proxy/conf/3proxy.cfg" >> /usr/local/3proxy/conf/3proxy.cfg
echo -e "timeouts 1 5 30 60 180 1800 15 60" >> /usr/local/3proxy/conf/3proxy.cfg
echo -e "$SOCKS5_USER" >> /usr/local/3proxy/conf/3proxy.cfg
echo -e "log /usr/local/3proxy/logs/3proxy.log" >> /usr/local/3proxy/conf/3proxy.cfg
echo -e "logformat \"-\\\"\"+_G{\"\"time_unix\"\":%t, \"\"proxy\"\":{\"\"type:\"\":\"\"%N\"\", \"\"port\"\":%p}, \"\"error\"\":{\"\"code\"\":\"\"%E\"\"}, \"\"auth\"\":{\"\"user\"\":\"\"%U\"\"}, \"\"client\"\":{\"\"ip\"\":\"\"%C\"\", \"\"port\"\":%c}, \"\"server\"\":{\"\"ip\"\":\"\"%R\"\", \"\"port\"\":%r}, \"\"bytes\"\":{\"\"sent\"\":%O, \"\"received\"\":%I}, \"\"request\"\":{\"\"hostname\"\":\"\"%n\"\"}, \"\"message\"\":\"\"%T\"\"}\"" >> /usr/local/3proxy/conf/3proxy.cfg
echo -e "auth $SOCKS5_AUTH_TYPE" >> /usr/local/3proxy/conf/3proxy.cfg
echo -e "socks -p$SOCKS5_PROXY_PORT" >> /usr/local/3proxy/conf/3proxy.cfg

View File

@@ -0,0 +1,5 @@
sudo docker run -d \
--restart always \
-p $SOCKS5_PROXY_PORT:$SOCKS5_PROXY_PORT/tcp \
--name $CONTAINER_NAME \
$CONTAINER_NAME

View File

@@ -0,0 +1,7 @@
#!/bin/sh
# This scripts copied from Amnezia client to Docker container to /opt/amnezia and launched every time container starts
echo "Container startup"
/bin/3proxy /usr/local/3proxy/conf/3proxy.cfg

View File

@@ -39,7 +39,7 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="115"/>
<source>Settings updated successfully, Reconnnection...</source>
<source>Settings updated successfully, reconnnection...</source>
<translation>تم تحديث الاعدادات بنجاح, جاري إعادة الاتصال...</translation>
</message>
<message>
@@ -700,8 +700,8 @@ Already installed containers were found on the server. All installed containers
<name>PageProtocolShadowSocksSettings</name>
<message>
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="80"/>
<source>ShadowSocks settings</source>
<translation>ShadowSocks إعدادات</translation>
<source>Shadowsocks settings</source>
<translation>Shadowsocks إعدادات</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="89"/>
@@ -1041,8 +1041,8 @@ And if you don&apos;t like the app, all the more support it - the donation will
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="115"/>
<source>Github</source>
<translation></translation>
<source>GitHub</source>
<translation>GitHub</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="119"/>
@@ -1317,7 +1317,7 @@ And if you don&apos;t like the app, all the more support it - the donation will
<name>PageSettingsDns</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="39"/>
<source>Default server does not support custom dns</source>
<source>Default server does not support custom DNS</source>
<translation>الخادم الافتراضي لا يدعم DNS مخصص</translation>
</message>
<message>
@@ -1390,7 +1390,7 @@ And if you don&apos;t like the app, all the more support it - the donation will
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="64"/>
<source>Enabling this function will save application&apos;s logs automatically, By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
<source>Enabling this function will save application&apos;s logs automatically. By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
<translation>سيتم حفظ سجلات البرنامج بشكل تلقائي عند تفعيل هذه الميزة, بشكل افتراضي, هذه الميزة مٌعطلة. قم بتفعيل هذه الميزة في حالة هناك خلل في التطبيق.</translation>
</message>
<message>
@@ -1797,7 +1797,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="89"/>
<source>QR-code</source>
<source>QR code</source>
<translation>رمز QR</translation>
</message>
<message>
@@ -2089,8 +2089,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="79"/>
<source>Save ShadowSocks config</source>
<translation>احفظ تكوين ShadowSocks</translation>
<source>Save Shadowsocks config</source>
<translation>احفظ تكوين Shadowsocks</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="86"/>
@@ -2119,8 +2119,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="131"/>
<source>ShadowSocks native format</source>
<translation>تنسيق ShadowSocks الاصلي</translation>
<source>Shadowsocks native format</source>
<translation>تنسيق Shadowsocks الاصلي</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="136"/>
@@ -2571,8 +2571,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<name>QObject</name>
<message>
<location filename="../protocols/protocols_defs.cpp" line="77"/>
<source>Sftp service</source>
<translation>خدمة Sftp</translation>
<source>SFTP service</source>
<translation>خدمة SFTP</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="11"/>
@@ -2581,7 +2581,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="12"/>
<source>Unknown Error</source>
<source>Unknown error</source>
<translation>خطأ غير معروف</translation>
</message>
<message>
@@ -2621,18 +2621,18 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="24"/>
<source>Ssh request was denied</source>
<translation>طلب Ssh محظو</translation>
<source>SSH request was denied</source>
<translation>طلب SSH محظو</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="25"/>
<source>Ssh request was interrupted</source>
<translation>إنقطع طلب Ssh</translation>
<source>SSH request was interrupted</source>
<translation>إنقطع طلب SSH</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="26"/>
<source>Ssh internal error</source>
<translation>مشكلة داخلية Ssh</translation>
<source>SSH internal error</source>
<translation>مشكلة داخلية SSH</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="27"/>
@@ -2720,7 +2720,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="32"/>
<source>Scp error: Generic failure</source>
<source>SCP error: Generic failure</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -2735,8 +2735,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="40"/>
<source>ShadowSocks (ss-local) executable missing</source>
<translation>ShadowSocks (ss-local) executable مفقود</translation>
<source>Shadowsocks (ss-local) executable missing</source>
<translation>Shadowsocks (ss-local) executable مفقود</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="41"/>
@@ -2821,13 +2821,13 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="91"/>
<source>Amnezia DNS</source>
<translation></translation>
<source>AmneziaDNS</source>
<translation>AmneziaDNS</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="92"/>
<source>Sftp file sharing service</source>
<translation>ملف Sftp: خدمة المشاركة</translation>
<source>SFTP file sharing service</source>
<translation>ملف SFTP: خدمة المشاركة</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="98"/>
@@ -2836,8 +2836,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="101"/>
<source>ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
<translation>بروتوكول ShadowSocks- يتنكر في حركة مرور VPN, يبدو ك حركة مرور الويب العادية
<source>Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
<translation>بروتوكول Shadowsocks- يتنكر في حركة مرور VPN, يبدو ك حركة مرور الويب العادية
ولكن قد يتم التعرف عليه من خلال أنظمة التحليل في بعض المناطق شديدة الرقابة.</translation>
</message>
<message>
@@ -2944,8 +2944,8 @@ For more detailed information, you can
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="115"/>
<source>IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
<translation>بروتوكول IKEv2 - بروتوكول مستقر حديث, اسرع بقليل من الباقي, يسترجع الاتصال بعد خسارة الاشارة.</translation>
<source>IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
<translation>بروتوكول IKEv2/IPsec - بروتوكول مستقر حديث, اسرع بقليل من الباقي, يسترجع الاتصال بعد خسارة الاشارة.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="118"/>
@@ -2981,16 +2981,12 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
<source>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&apos;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&apos;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.</source>
<translation>Shadowsocks, مستوحي من بروتوكول SOCKS5, يحمي الاتصال بأستعمال شفرة AEAD. كذلك Shadowsocks صٌمم كي يكون متحفظاً ويصعب تحديدة, إنه ليس مطابقًا لاتصال HTTPS القياسي. عمتاُ. بعض انظمة تحليل حركات المرور قد تتعرف علي اتصال Shadowsocks. بسبب الدعم المحدود في Amnezia, يٌنصح بأستخدام بروتوكول AmneziaWG.
* مٌتاح في AmneziaVPN عبر جميع المنصات
* استهلاك طاقة عادي علي اجهزة المحمول
* بروتوكول تشفير قابل للتكوين
* قابل للكشف بواسطة بعض انظمة DPI
* يعمل عبر بروتوكول شبكة TCP.</translation>

View File

@@ -22,7 +22,7 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="115"/>
<source>Settings updated successfully, Reconnnection...</source>
<source>Settings updated successfully, reconnnection...</source>
<translation>تنظیمات به روز رسانی شد
در حال اتصال دوباره...</translation>
</message>
@@ -664,8 +664,8 @@ Already installed containers were found on the server. All installed containers
<name>PageProtocolShadowSocksSettings</name>
<message>
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="80"/>
<source>ShadowSocks settings</source>
<translation>تنظیمات ShadowSocks</translation>
<source>Shadowsocks settings</source>
<translation>تنظیمات Shadowsocks</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="89"/>
@@ -1002,8 +1002,8 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="115"/>
<source>Github</source>
<translation>Github</translation>
<source>GitHub</source>
<translation>GitHub</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="119"/>
@@ -1250,7 +1250,7 @@ Already installed containers were found on the server. All installed containers
<name>PageSettingsDns</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="39"/>
<source>Default server does not support custom dns</source>
<source>Default server does not support custom DNS</source>
<translation>سرور پیشفرض از دیاناس سفارشی پشتیبانی نمیکند</translation>
</message>
<message>
@@ -1323,7 +1323,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="64"/>
<source>Enabling this function will save application&apos;s logs automatically, By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
<source>Enabling this function will save application&apos;s logs automatically. By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
<translation>فعال کردن این عملکرد باعث ذخیره خودکار لاگهای برنامه میشود. به طور پیشفرض، قابلیت ثبت لاگ غیرفعال است. در صورت بروز خطا در برنامه، ذخیره لاگ را فعال کنید.</translation>
</message>
<message>
@@ -1710,7 +1710,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="89"/>
<source>QR-code</source>
<source>QR code</source>
<translation>QR-Code</translation>
</message>
<message>
@@ -2037,8 +2037,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="79"/>
<source>Save ShadowSocks config</source>
<translation>ذخیره تنظیمات ShadowSocks</translation>
<source>Save Shadowsocks config</source>
<translation>ذخیره تنظیمات Shadowsocks</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="86"/>
@@ -2057,8 +2057,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="131"/>
<source>ShadowSocks native format</source>
<translation>فرمت ShadowSocks</translation>
<source>Shadowsocks native format</source>
<translation>فرمت Shadowsocks</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="136"/>
@@ -2476,8 +2476,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="12"/>
<source>Unknown Error</source>
<translation>Unknown Error</translation>
<source>Unknown error</source>
<translation>Unknown error</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="13"/>
@@ -2516,18 +2516,18 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="24"/>
<source>Ssh request was denied</source>
<translation>Ssh request was denied</translation>
<source>SSH request was denied</source>
<translation>SSH request was denied</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="25"/>
<source>Ssh request was interrupted</source>
<translation>Ssh request was interrupted</translation>
<source>SSH request was interrupted</source>
<translation>SSH request was interrupted</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="26"/>
<source>Ssh internal error</source>
<translation>Ssh internal error</translation>
<source>SSH internal error</source>
<translation>SSH internal error</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="27"/>
@@ -2628,7 +2628,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="32"/>
<source>Scp error: Generic failure</source>
<source>SCP error: Generic failure</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -2643,8 +2643,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="40"/>
<source>ShadowSocks (ss-local) executable missing</source>
<translation>ShadowSocks (ss-local) executable missing</translation>
<source>Shadowsocks (ss-local) executable missing</source>
<translation>Shadowsocks (ss-local) executable missing</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="41"/>
@@ -2718,7 +2718,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="101"/>
<source>ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
<source>Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
<translation>شدوساکس - ترافیک VPN را پنهان می کند، به طوری که مشابه ترافیک وب عادی می شود، اما ممکن است توسط سیستم های تجزیه و تحلیل در برخی از مناطق با سانسور شدید شناسایی شود.</translation>
</message>
<message>
@@ -2819,8 +2819,8 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="92"/>
<source>Sftp file sharing service</source>
<translation>سرویس فایل اشتراک Sftp</translation>
<source>SFTP file sharing service</source>
<translation>سرویس فایل اشتراک SFTP</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="90"/>
@@ -2830,8 +2830,8 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="91"/>
<source>Amnezia DNS</source>
<translation>Amnezia DNS</translation>
<source>AmneziaDNS</source>
<translation>AmneziaDNS</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="98"/>
@@ -2850,8 +2850,8 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="115"/>
<source>IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
<translation>پروتکل IKEv2 پروتکلی پایدار و مدرن که مقداری سریعتر از سایر پروتکلهاست. بعد از قطع سیگنال دوباره اتصال را بازیابی میکند.</translation>
<source>IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
<translation>پروتکل IKEv2/IPsec پروتکلی پایدار و مدرن که مقداری سریعتر از سایر پروتکلهاست. بعد از قطع سیگنال دوباره اتصال را بازیابی میکند.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="118"/>
@@ -2891,15 +2891,12 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
<source>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&apos;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&apos;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.</source>
<translation>پروتکل Shadowsocks، الهام گرفته از پروتکل Socks5، اتصال را با استفاده از رمزگذاری AEAD امن میکند. اگرچه Shadowsocks طوری طراحی شده که برای شناسایی در شبکه چالشبرانگیز باشد و محتاط عمل کند اما این پروتکل مانند یک اتصال استاندارد HTTPS نیست و برخی از سیستمهای تحلیل ترافیک مشخص ممکن است بتوانند اتصال Shadowsocks را شناسایی کنند. به دلیل محدودیت پشتیبانی در Amnezia پیشنهاد میشود که از َAmneziaWG استفاده شود.
* فقط بر روی پلتفرم دسکتاپ بر روی Amnezia قابل دسترس است
* مصرف انرژی عادی در دستگاههای موبایل
* پروتکل رمزنگاری قابل پیکربندی
* قابل شناسایی توسط برخی سیستمهای تحلیل عمیق DPI
* عملکرد بر روی پروتکل شبکه TCP</translation>
@@ -2946,8 +2943,8 @@ For more detailed information, you can
</message>
<message>
<location filename="../protocols/protocols_defs.cpp" line="77"/>
<source>Sftp service</source>
<translation>سرویس Sftp</translation>
<source>SFTP service</source>
<translation>سرویس SFTP</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/qtkeychain/libsecret.cpp" line="119"/>

View File

@@ -86,7 +86,7 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="162"/>
<source>Settings updated successfully, Reconnnection...</source>
<source>Settings updated successfully, reconnnection...</source>
<translation>ि ...</translation>
</message>
<message>
@@ -711,7 +711,7 @@ Already installed containers were found on the server. All installed containers
<name>PageProtocolShadowSocksSettings</name>
<message>
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="95"/>
<source>ShadowSocks settings</source>
<source>Shadowsocks settings</source>
<translation> ि</translation>
</message>
<message>
@@ -1055,13 +1055,13 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="140"/>
<source>Github</source>
<translation>Github</translation>
<source>GitHub</source>
<translation>GitHub</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="147"/>
<source>https://github.com/amnezia-vpn/amnezia-client</source>
<translation></translation>
<translation>https://github.com/amnezia-vpn/amnezia-client</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="158"/>
@@ -1144,7 +1144,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="278"/>
<source>Executable file (*.*)</source>
<translation>ि (**)</translation>
<translation>ि (*.*)</translation>
</message>
</context>
<context>
@@ -1276,7 +1276,7 @@ Already installed containers were found on the server. All installed containers
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="102"/>
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="134"/>
<source>Backup files (*.backup)</source>
<translation> (*.)</translation>
<translation> (*.backup)</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="111"/>
@@ -1386,7 +1386,7 @@ Already installed containers were found on the server. All installed containers
<name>PageSettingsDns</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="46"/>
<source>Default server does not support custom dns</source>
<source>Default server does not support custom DNS</source>
<translation>ि </translation>
</message>
<message>
@@ -1459,7 +1459,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="69"/>
<source>Enabling this function will save application&apos;s logs automatically, By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
<source>Enabling this function will save application&apos;s logs automatically. By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
<translation> ि ि , ि , ि ि िि ि .</translation>
</message>
<message>
@@ -1480,7 +1480,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="143"/>
<source>Logs files (*.log)</source>
<translation> (*.)</translation>
<translation> (*.log)</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="152"/>
@@ -1868,7 +1868,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="102"/>
<source>QR-code</source>
<source>QR code</source>
<translation> ि</translation>
</message>
<message>
@@ -2164,7 +2164,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="81"/>
<source>Save ShadowSocks config</source>
<source>Save Shadowsocks config</source>
<translation> ि </translation>
</message>
<message>
@@ -2199,7 +2199,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="140"/>
<source>ShadowSocks native format</source>
<source>Shadowsocks native format</source>
<translation> </translation>
</message>
<message>
@@ -2663,7 +2663,7 @@ Already installed containers were found on the server. All installed containers
<name>QObject</name>
<message>
<location filename="../protocols/protocols_defs.cpp" line="78"/>
<source>Sftp service</source>
<source>SFTP service</source>
<translation> </translation>
</message>
<message>
@@ -2673,7 +2673,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../core/errorstrings.cpp" line="12"/>
<source>Unknown Error</source>
<source>Unknown error</source>
<translation> ि</translation>
</message>
<message>
@@ -2718,18 +2718,18 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../core/errorstrings.cpp" line="25"/>
<source>Ssh request was denied</source>
<translation>Ssh ि </translation>
<source>SSH request was denied</source>
<translation>SSH ि </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="26"/>
<source>Ssh request was interrupted</source>
<translation>Ssh ि </translation>
<source>SSH request was interrupted</source>
<translation>SSH ि </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="27"/>
<source>Ssh internal error</source>
<translation>Ssh ि ि</translation>
<source>SSH internal error</source>
<translation>SSH ि ि</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="28"/>
@@ -2773,7 +2773,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../core/errorstrings.cpp" line="33"/>
<source>Scp error: Generic failure</source>
<source>SCP error: Generic failure</source>
<translation> ि: ि</translation>
</message>
<message>
@@ -2788,7 +2788,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../core/errorstrings.cpp" line="41"/>
<source>ShadowSocks (ss-local) executable missing</source>
<source>Shadowsocks (ss-local) executable missing</source>
<translation> (-) ि </translation>
</message>
<message>
@@ -2874,13 +2874,13 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="97"/>
<source>Amnezia DNS</source>
<translation></translation>
<source>AmneziaDNS</source>
<translation>AmneziaDNS</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="98"/>
<source>Sftp file sharing service</source>
<translation>Sftp </translation>
<source>SFTP file sharing service</source>
<translation>SFTP </translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="104"/>
@@ -2889,7 +2889,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="107"/>
<source>ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
<source>Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
<translation> - ि , ि ि , ि ि ि ि ि .</translation>
</message>
<message>
@@ -2998,8 +2998,8 @@ For more detailed information, you can
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="124"/>
<source>IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
<translation>IKEv2 - ि ि , , ि ि ि </translation>
<source>IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
<translation>IKEv2/IPsec - ि ि , , ि ि ि </translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="127"/>
@@ -3035,16 +3035,12 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
<source>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&apos;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&apos;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.</source>
<translation>, SOCKS5 ि , AEAD ि ि ि , HTTPS ि, ि ि ि Amnezia ि , AmneziaWG
* AmneziaVPN
* ि
* ि ि
* ि
* .</translation>

View File

@@ -22,7 +22,7 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="115"/>
<source>Settings updated successfully, Reconnnection...</source>
<source>Settings updated successfully, reconnnection...</source>
<translation>ကက က...</translation>
</message>
<message>
@@ -664,8 +664,8 @@ Already installed containers were found on the server. All installed containers
<name>PageProtocolShadowSocksSettings</name>
<message>
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="80"/>
<source>ShadowSocks settings</source>
<translation>ShadowSocks က</translation>
<source>Shadowsocks settings</source>
<translation>Shadowsocks က</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="89"/>
@@ -1002,8 +1002,8 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="115"/>
<source>Github</source>
<translation>Github</translation>
<source>GitHub</source>
<translation>GitHub</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="119"/>
@@ -1250,8 +1250,8 @@ Already installed containers were found on the server. All installed containers
<name>PageSettingsDns</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="39"/>
<source>Default server does not support custom dns</source>
<translation> ကက dns က က</translation>
<source>Default server does not support custom DNS</source>
<translation> ကက DNS က က</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="57"/>
@@ -1323,7 +1323,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="64"/>
<source>Enabling this function will save application&apos;s logs automatically, By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
<source>Enabling this function will save application&apos;s logs automatically. By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
<translation>ကက က က က ကက ကက က </translation>
</message>
<message>
@@ -1711,7 +1711,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="89"/>
<source>QR-code</source>
<source>QR code</source>
<translation>QR-က</translation>
</message>
<message>
@@ -2038,8 +2038,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="79"/>
<source>Save ShadowSocks config</source>
<translation>ShadowSocks config က</translation>
<source>Save Shadowsocks config</source>
<translation>Shadowsocks config က</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="86"/>
@@ -2058,8 +2058,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="131"/>
<source>ShadowSocks native format</source>
<translation>ShadowSocks </translation>
<source>Shadowsocks native format</source>
<translation>Shadowsocks </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="136"/>
@@ -2477,7 +2477,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="12"/>
<source>Unknown Error</source>
<source>Unknown error</source>
<translation> </translation>
</message>
<message>
@@ -2517,18 +2517,18 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="24"/>
<source>Ssh request was denied</source>
<translation>Ssh က</translation>
<source>SSH request was denied</source>
<translation>SSH က</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="25"/>
<source>Ssh request was interrupted</source>
<translation>Ssh ကကက</translation>
<source>SSH request was interrupted</source>
<translation>SSH ကကက</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="26"/>
<source>Ssh internal error</source>
<translation>က Ssh </translation>
<source>SSH internal error</source>
<translation>က SSH </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="27"/>
@@ -2624,7 +2624,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="32"/>
<source>Scp error: Generic failure</source>
<source>SCP error: Generic failure</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -2639,8 +2639,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="40"/>
<source>ShadowSocks (ss-local) executable missing</source>
<translation>ShadowSocks (ss-local) executable က</translation>
<source>Shadowsocks (ss-local) executable missing</source>
<translation>Shadowsocks (ss-local) executable က</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="41"/>
@@ -2719,8 +2719,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="101"/>
<source>ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
<translation>ShadowSocks - က VPN က က က က က.</translation>
<source>Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
<translation>Shadowsocks - က VPN က က က က က.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="104"/>
@@ -2820,8 +2820,8 @@ IKEv2 သည် လုံခြုံရေး၊ တည်ငြိမ်မှ
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="92"/>
<source>Sftp file sharing service</source>
<translation>Sftp </translation>
<source>SFTP file sharing service</source>
<translation>SFTP </translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="90"/>
@@ -2831,8 +2831,8 @@ IKEv2 သည် လုံခြုံရေး၊ တည်ငြိမ်မှ
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="91"/>
<source>Amnezia DNS</source>
<translation>Amnezia DNS</translation>
<source>AmneziaDNS</source>
<translation>AmneziaDNS</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="98"/>
@@ -2851,8 +2851,8 @@ IKEv2 သည် လုံခြုံရေး၊ တည်ငြိမ်မှ
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="115"/>
<source>IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
<translation>IKEv2 - က က signal ကက ကက .</translation>
<source>IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
<translation>IKEv2/IPsec - က က signal ကက ကက .</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="118"/>
@@ -2888,16 +2888,12 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
<source>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&apos;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&apos;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.</source>
<translation>SOCKS5 ကက က Shadowsocks AEAD cipher က ကကကက Shadowsocks က ကက HTTPS က က က Shadowsocks ကက Amnezia ကကက AmneziaWG ကက က
* Desktop AmneziaVPN
* က
* က က
* DPI က
* TCP ကက က .</translation>
@@ -2944,8 +2940,8 @@ For more detailed information, you can
</message>
<message>
<location filename="../protocols/protocols_defs.cpp" line="77"/>
<source>Sftp service</source>
<translation>Sftp </translation>
<source>SFTP service</source>
<translation>SFTP </translation>
</message>
<message>
<location filename="../3rd/qtkeychain/qtkeychain/libsecret.cpp" line="119"/>

File diff suppressed because it is too large Load Diff

View File

@@ -86,8 +86,8 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="161"/>
<source>Settings updated successfully, Reconnnection...</source>
<translation>Налаштування оновлено, Підключення...</translation>
<source>Settings updated successfully, reconnnection...</source>
<translation>Налаштування оновлено, підключення...</translation>
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="164"/>
@@ -771,8 +771,8 @@ Already installed containers were found on the server. All installed containers
<name>PageProtocolShadowSocksSettings</name>
<message>
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="80"/>
<source>ShadowSocks settings</source>
<translation>Налаштування ShadowSocks</translation>
<source>Shadowsocks settings</source>
<translation>Налаштування Shadowsocks</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="91"/>
@@ -1138,8 +1138,8 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="115"/>
<source>Github</source>
<translation>Github</translation>
<source>GitHub</source>
<translation>GitHub</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="119"/>
@@ -1413,7 +1413,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="50"/>
<source>Use AmneziaDNS</source>
<translation>Використовувати Amnezia DNS</translation>
<translation>Використовувати AmneziaDNS</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="51"/>
@@ -1459,7 +1459,7 @@ Already installed containers were found on the server. All installed containers
<name>PageSettingsDns</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="39"/>
<source>Default server does not support custom dns</source>
<source>Default server does not support custom DNS</source>
<translation type="unfinished">Сервер за замовчуванням не підтримує користувацький DNS</translation>
</message>
<message>
@@ -1536,7 +1536,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="60"/>
<source>Enabling this function will save application&apos;s logs automatically, By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
<source>Enabling this function will save application&apos;s logs automatically. By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -1975,7 +1975,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="89"/>
<source>QR-code</source>
<source>QR code</source>
<translation>QR-код</translation>
</message>
<message>
@@ -2368,8 +2368,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="81"/>
<source>Save ShadowSocks config</source>
<translation type="unfinished">Зберегти конфігурацію ShadowSocks</translation>
<source>Save Shadowsocks config</source>
<translation type="unfinished">Зберегти конфігурацію Shadowsocks</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="88"/>
@@ -2393,8 +2393,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="140"/>
<source>ShadowSocks native format</source>
<translation type="unfinished">ShadowSocks нативний формат</translation>
<source>Shadowsocks native format</source>
<translation type="unfinished">Shadowsocks нативний формат</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="145"/>
@@ -2820,8 +2820,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="12"/>
<source>Unknown Error</source>
<translation>Unknown Error</translation>
<source>Unknown error</source>
<translation>Unknown error</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="13"/>
@@ -2865,18 +2865,18 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="25"/>
<source>Ssh request was denied</source>
<translation>Ssh request was denied</translation>
<source>SSH request was denied</source>
<translation>SSH request was denied</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="26"/>
<source>Ssh request was interrupted</source>
<translation>Ssh request was interrupted</translation>
<source>SSH request was interrupted</source>
<translation>SSH request was interrupted</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="27"/>
<source>Ssh internal error</source>
<translation>Ssh internal error</translation>
<source>SSH internal error</source>
<translation>SSH internal error</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="28"/>
@@ -2895,7 +2895,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="33"/>
<source>Scp error: Generic failure</source>
<source>SCP error: Generic failure</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -2991,8 +2991,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="41"/>
<source>ShadowSocks (ss-local) executable missing</source>
<translation>ShadowSocks (ss-local) executable missing</translation>
<source>Shadowsocks (ss-local) executable missing</source>
<translation>Shadowsocks (ss-local) executable missing</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="42"/>
@@ -3071,13 +3071,13 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="107"/>
<source>ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
<translation type="unfinished">ShadowSocks - маскує VPN-трафік під звичайний веб-трафік, але розпізнається системами аналізу трафіка в деяких регіонах з високим рівнем цензури.</translation>
<source>Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
<translation type="unfinished">Shadowsocks - маскує VPN-трафік під звичайний веб-трафік, але розпізнається системами аналізу трафіка в деяких регіонах з високим рівнем цензури.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="110"/>
<source>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.</source>
<translation type="unfinished">OpenVPN over Cloak - OpenVPN з маскуванням VPN під HTTPS трафік і захистом від active-probbing. Підходить для регіонів з самим високим рівнем цензури.</translation>
<translation type="unfinished">OpenVPN over Cloak - OpenVPN з маскуванням VPN під HTTPS трафік і захистом від active-probing. Підходить для регіонів з самим високим рівнем цензури.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="131"/>
@@ -3142,7 +3142,7 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
Він може швидко переключись між мережами та пристроями, що робить його осболиво адаптованим під динамічні мережеві середовища.
Потрібно зазначити, що незважаючи на стабільність та швидкість, IKEv2 легко розпізнається та вразливий до блокувань.
* IKEv2 в AmneziaVPN тільки для Windows.
* IKEv2/IPsec в AmneziaVPN тільки для Windows.
* Низьке енергоспоживання, на мобільних пристроях
* Мінімальна конфігурація
* Розпізнається системами DPI-анализу
@@ -3155,8 +3155,8 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="98"/>
<source>Sftp file sharing service</source>
<translation>Сервіс обміну файлами Sftp</translation>
<source>SFTP file sharing service</source>
<translation>Сервіс обміну файлами SFTP</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="96"/>
@@ -3166,8 +3166,8 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="97"/>
<source>Amnezia DNS</source>
<translation>Amnezia DNS</translation>
<source>AmneziaDNS</source>
<translation>AmneziaDNS</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="104"/>
@@ -3191,8 +3191,8 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="124"/>
<source>IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
<translation>IKEv2 сучасний стабільний протокол, трішки швидше за інших відновлює підключення.</translation>
<source>IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
<translation>IKEv2/IPsec сучасний стабільний протокол, трішки швидше за інших відновлює підключення.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="127"/>
@@ -3202,7 +3202,7 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
<message>
<location filename="../containers/containers_defs.cpp" line="129"/>
<source>Replace the current DNS server with your own. This will increase your privacy level.</source>
<translation>Замініть DNS-сервер на Amnezia DNS. Це підвищить вашу рівень захищеності в інтернеті.</translation>
<translation>Замініть DNS-сервер на AmneziaDNS. Це підвищить вашу рівень захищеності в інтернеті.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="138"/>
@@ -3228,8 +3228,6 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
<source>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&apos;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&apos;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.</source>
@@ -3340,7 +3338,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin
</message>
<message>
<location filename="../protocols/protocols_defs.cpp" line="78"/>
<source>Sftp service</source>
<source>SFTP service</source>
<translation>Сервіс SFTP</translation>
</message>
<message>

View File

@@ -85,7 +85,7 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="162"/>
<source>Settings updated successfully, Reconnnection...</source>
<source>Settings updated successfully, reconnnection...</source>
<translation>ترتیب ک ھوگی،دوبارہ جوڑنےکی کوشش...</translation>
</message>
<message>
@@ -711,7 +711,7 @@ Already installed containers were found on the server. All installed containers
<name>PageProtocolShadowSocksSettings</name>
<message>
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="95"/>
<source>ShadowSocks settings</source>
<source>Shadowsocks settings</source>
<translation>شیڈو ساکس ترتیبات</translation>
</message>
<message>
@@ -1057,13 +1057,13 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="140"/>
<source>Github</source>
<source>GitHub</source>
<translation>گِٹ ہَب</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="147"/>
<source>https://github.com/amnezia-vpn/amnezia-client</source>
<translation></translation>
<translation>https://github.com/amnezia-vpn/amnezia-client</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="158"/>
@@ -1373,7 +1373,7 @@ Already installed containers were found on the server. All installed containers
<name>PageSettingsDns</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="46"/>
<source>Default server does not support custom dns</source>
<source>Default server does not support custom DNS</source>
<translation>افتراضی سرور کا مخصوص DNS کو سپورٹ نہیں کرتا ہے</translation>
</message>
<message>
@@ -1446,7 +1446,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="69"/>
<source>Enabling this function will save application&apos;s logs automatically, By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
<source>Enabling this function will save application&apos;s logs automatically. By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
<translation>اس فعل کو فعال کرنے سے، ایپلیکیشن کے لاگ خود بخود محفوظ ہوجائیں گے۔ پہلے سے، لاگنگ کی فعالیت غیر فعال ہوتی ہے۔ اگر ایپلیکیشن میں کوئی خرابی ہو، تو لاگ کو بچانا فعال کریں.</translation>
</message>
<message>
@@ -1855,7 +1855,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="102"/>
<source>QR-code</source>
<source>QR code</source>
<translation>QR کوڈ</translation>
</message>
<message>
@@ -2151,7 +2151,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="81"/>
<source>Save ShadowSocks config</source>
<source>Save Shadowsocks config</source>
<translation>شیڈو ساکس کی ترتیبات کو محفوظ کریں</translation>
</message>
<message>
@@ -2186,7 +2186,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="140"/>
<source>ShadowSocks native format</source>
<source>Shadowsocks native format</source>
<translation>شیڈو ساکس کا اصل فارمیٹ</translation>
</message>
<message>
@@ -2631,7 +2631,7 @@ Already installed containers were found on the server. All installed containers
<name>QObject</name>
<message>
<location filename="../protocols/protocols_defs.cpp" line="78"/>
<source>Sftp service</source>
<source>SFTP service</source>
<translation>ایس ایف ٹی پی سروس</translation>
</message>
<message>
@@ -2641,7 +2641,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../core/errorstrings.cpp" line="12"/>
<source>Unknown Error</source>
<source>Unknown error</source>
<translation>نامعلوم خامی</translation>
</message>
<message>
@@ -2681,18 +2681,18 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../core/errorstrings.cpp" line="25"/>
<source>Ssh request was denied</source>
<translation>Ssh درخواست مسترد کر دی گئی</translation>
<source>SSH request was denied</source>
<translation>SSH درخواست مسترد کر دی گئی</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="26"/>
<source>Ssh request was interrupted</source>
<translation>Ssh درخواست میں خلل پڑ</translation>
<source>SSH request was interrupted</source>
<translation>SSH درخواست میں خلل پڑ</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="27"/>
<source>Ssh internal error</source>
<translation>Ssh اندرونی خرابی</translation>
<source>SSH internal error</source>
<translation>SSH اندرونی خرابی</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="28"/>
@@ -2741,7 +2741,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../core/errorstrings.cpp" line="33"/>
<source>Scp error: Generic failure</source>
<source>SCP error: Generic failure</source>
<translation>ایس سی پی کی خرابی: عام ناکامی</translation>
</message>
<message>
@@ -2756,7 +2756,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../core/errorstrings.cpp" line="41"/>
<source>ShadowSocks (ss-local) executable missing</source>
<source>Shadowsocks (ss-local) executable missing</source>
<translation>شیڈو ساکس (ss-local) قابل عمل غائب</translation>
</message>
<message>
@@ -2842,13 +2842,13 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="97"/>
<source>Amnezia DNS</source>
<source>AmneziaDNS</source>
<translation>ایمنیزیا ڈی این ایس</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="98"/>
<source>Sftp file sharing service</source>
<translation>Sftp فائل شیئرنگ سروس</translation>
<source>SFTP file sharing service</source>
<translation>SFTP فائل شیئرنگ سروس</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="104"/>
@@ -2857,7 +2857,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="107"/>
<source>ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
<source>Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
<translation>شیڈو ساکس - VPN ٹریفک کو ماسک کرتا ہے، جو اسے عام ویب ٹریفک جیسا بناتا ہے، لیکن اسے کچھ انتہائی سنسر والے علاقوں میں تجزیہ کے نظام کے ذریعے پہچانا جا سکتا ہے.</translation>
</message>
<message>
@@ -2947,8 +2947,8 @@ For more detailed information, you can
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="124"/>
<source>IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
<translation>IKEv2 - جدید مستحکم پروٹوکول، دوسروں کے مقابلے میں تھوڑا تیز، سگنل ضائع ہونے کے بعد کنکشن بحال کرتا ہے۔</translation>
<source>IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
<translation>IKEv2/IPsec - جدید مستحکم پروٹوکول، دوسروں کے مقابلے میں تھوڑا تیز، سگنل ضائع ہونے کے بعد کنکشن بحال کرتا ہے۔</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="127"/>
@@ -2977,12 +2977,10 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
<source>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&apos;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&apos;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.</source>
<translation>شیڈو ساکس، SOCKS5 پروٹوکول سے متاثر، AEAD سائفر کا استعمال کرتے ہوئے کنکشن کی حفاظت کرتا ہے۔ اگرچہ شیڈو ساکس کو سمجھدار اور شناخت کرنے کے لیے چیلنج کرنے کے لیے ڈیزائن کیا گیا ہے، لیکن یہ معیاری HTTPS کنکشن سے مماثل نہیں ہے۔ تاہم، کچھ ٹریفک تجزیہ نظام اب بھی شیڈو ساکس کنکشن کا پتہ لگا سکتے ہیں۔ Amnezia میں محدود تعاون کی وجہ سے، AmneziaWG پروٹوکول استعمال کرنے کی سفارش کی جاتی ہے۔ * صرف ڈیسک ٹاپ پلیٹ فارمز پر AmneziaVPN میں دستیاب ہے * موبائل آلات پر بجلی کی عام کھپت * قابل ترتیب انکرپشن پروٹوکول * کچھ DPI سسٹمز کے ذریعے قابل شناخت * TCP نیٹ ورک پروٹوکول پر کام کرتا ہے.</translation>
<translation>شیڈو ساکس، SOCKS5 پروٹوکول سے متاثر، AEAD سائفر کا استعمال کرتے ہوئے کنکشن کی حفاظت کرتا ہے۔ اگرچہ شیڈو ساکس کو سمجھدار اور شناخت کرنے کے لیے چیلنج کرنے کے لیے ڈیزائن کیا گیا ہے، لیکن یہ معیاری HTTPS کنکشن سے مماثل نہیں ہے۔ تاہم، کچھ ٹریفک تجزیہ نظام اب بھی شیڈو ساکس کنکشن کا پتہ لگا سکتے ہیں۔ Amnezia میں محدود تعاون کی وجہ سے، AmneziaWG پروٹوکول استعمال کرنے کی سفارش کی جاتی ہے۔ * صرف ڈیسک ٹاپ پلیٹ فارمز پر AmneziaVPN میں دستیاب ہے * قابل ترتیب انکرپشن پروٹوکول * کچھ DPI سسٹمز کے ذریعے قابل شناخت * TCP نیٹ ورک پروٹوکول پر کام کرتا ہے.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="196"/>

View File

@@ -39,7 +39,7 @@
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="115"/>
<source>Settings updated successfully, Reconnnection...</source>
<source>Settings updated successfully, reconnnection...</source>
<translation>, ...</translation>
</message>
<message>
@@ -698,8 +698,8 @@ Already installed containers were found on the server. All installed containers
<name>PageProtocolShadowSocksSettings</name>
<message>
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="80"/>
<source>ShadowSocks settings</source>
<translation>ShadowSocks </translation>
<source>Shadowsocks settings</source>
<translation>Shadowsocks </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="89"/>
@@ -1039,13 +1039,13 @@ And if you don&apos;t like the app, all the more support it - the donation will
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="115"/>
<source>Github</source>
<translation></translation>
<source>GitHub</source>
<translation>GitHub</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="119"/>
<source>https://github.com/amnezia-vpn/amnezia-client</source>
<translation></translation>
<translation>https://github.com/amnezia-vpn/amnezia-client</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="128"/>
@@ -1323,8 +1323,8 @@ And if you don&apos;t like the app, all the more support it - the donation will
<name>PageSettingsDns</name>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="39"/>
<source>Default server does not support custom dns</source>
<translation> dns</translation>
<source>Default server does not support custom DNS</source>
<translation> DNS</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsDns.qml" line="57"/>
@@ -1396,7 +1396,7 @@ And if you don&apos;t like the app, all the more support it - the donation will
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="64"/>
<source>Enabling this function will save application&apos;s logs automatically, By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
<source>Enabling this function will save application&apos;s logs automatically. By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
<translation></translation>
</message>
<message>
@@ -1834,7 +1834,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="89"/>
<source>QR-code</source>
<source>QR code</source>
<translation></translation>
</message>
<message>
@@ -2167,8 +2167,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="79"/>
<source>Save ShadowSocks config</source>
<translation> ShadowSocks </translation>
<source>Save Shadowsocks config</source>
<translation> Shadowsocks </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="86"/>
@@ -2197,8 +2197,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="131"/>
<source>ShadowSocks native format</source>
<translation>ShadowSocks原生格式</translation>
<source>Shadowsocks native format</source>
<translation>Shadowsocks原生格式</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="136"/>
@@ -2681,8 +2681,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<name>QObject</name>
<message>
<location filename="../protocols/protocols_defs.cpp" line="77"/>
<source>Sftp service</source>
<translation>Sftp </translation>
<source>SFTP service</source>
<translation>SFTP </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="11"/>
@@ -2691,7 +2691,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="12"/>
<source>Unknown Error</source>
<source>Unknown error</source>
<translation></translation>
</message>
<message>
@@ -2731,18 +2731,18 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="24"/>
<source>Ssh request was denied</source>
<translation>ssh请求被拒绝</translation>
<source>SSH request was denied</source>
<translation>SSH请求被拒绝</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="25"/>
<source>Ssh request was interrupted</source>
<translation>ssh请求中断</translation>
<source>SSH request was interrupted</source>
<translation>SSH请求中断</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="26"/>
<source>Ssh internal error</source>
<translation>ssh内部错误</translation>
<source>SSH internal error</source>
<translation>SSH内部错误</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="27"/>
@@ -2761,7 +2761,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="32"/>
<source>Scp error: Generic failure</source>
<source>SCP error: Generic failure</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -2887,8 +2887,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="40"/>
<source>ShadowSocks (ss-local) executable missing</source>
<translation>ShadowSocks (ss-local) </translation>
<source>Shadowsocks (ss-local) executable missing</source>
<translation>Shadowsocks (ss-local) </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="41"/>
@@ -2947,12 +2947,12 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="91"/>
<source>Amnezia DNS</source>
<translation></translation>
<source>AmneziaDNS</source>
<translation>AmneziaDNS</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="92"/>
<source>Sftp file sharing service</source>
<source>SFTP file sharing service</source>
<translation>SFTP文件共享服务</translation>
</message>
<message>
@@ -2962,8 +2962,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="101"/>
<source>ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
<translation>ShadowSocks - VPN流量使.</translation>
<source>Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions.</source>
<translation>Shadowsocks - VPN流量使.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="217"/>
@@ -3037,8 +3037,8 @@ WireGuard非常容易被阻挡因为其独特的数据包签名。与一些
UDP网络协议运行</translation>
</message>
<message>
<source>ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but is recognised by analysis systems in some highly censored regions.</source>
<translation type="vanished">ShadowSocks - VPN 使 Web </translation>
<source>Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but is recognised by analysis systems in some highly censored regions.</source>
<translation type="vanished">Shadowsocks - VPN 使 Web </translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="104"/>
@@ -3057,8 +3057,8 @@ WireGuard非常容易被阻挡因为其独特的数据包签名。与一些
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="115"/>
<source>IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
<translation>IKEv2 - </translation>
<source>IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss.</source>
<translation>IKEv2/IPsec - </translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="118"/>
@@ -3098,16 +3098,12 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
<source>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&apos;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&apos;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.</source>
<translation>Shadowsocks SOCKS5 使 AEAD Shadowsocks HTTPS Shadowsocks Amnezia支持有限使AmneziaWG协议
* AmneziaVPN
*
*
* DPI
* TCP </translation>

View File

@@ -123,7 +123,7 @@ void ConnectionController::onConnectionStateChanged(Vpn::ConnectionState state)
void ConnectionController::onCurrentContainerUpdated()
{
if (m_isConnected || m_isConnectionInProgress) {
emit reconnectWithUpdatedContainer(tr("Settings updated successfully, Reconnnection..."));
emit reconnectWithUpdatedContainer(tr("Settings updated successfully, reconnnection..."));
openConnection();
} else {
emit reconnectWithUpdatedContainer(tr("Settings updated successfully"));

View File

@@ -123,6 +123,9 @@ void InstallController::install(DockerContainer container, int port, TransportPr
} else if (container == DockerContainer::Sftp) {
containerConfig.insert(config_key::userName, protocols::sftp::defaultUserName);
containerConfig.insert(config_key::password, Utils::getRandomString(10));
} else if (container == DockerContainer::Socks5Proxy) {
containerConfig.insert(config_key::userName, protocols::socks5Proxy::defaultUserName);
containerConfig.insert(config_key::password, Utils::getRandomString(10));
}
config.insert(config_key::container, ContainerProps::containerToString(container));
@@ -362,7 +365,7 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
if (containerInfo.isEmpty()) {
continue;
}
const static QRegularExpression containerAndPortRegExp("(amnezia[-a-z]*).*?:([0-9]*)->[0-9]*/(udp|tcp).*");
const static QRegularExpression containerAndPortRegExp("(amnezia[-a-z0-9]*).*?:([0-9]*)->[0-9]*/(udp|tcp).*");
QRegularExpressionMatch containerAndPortMatch = containerAndPortRegExp.match(containerInfo);
if (containerAndPortMatch.hasMatch()) {
QString name = containerAndPortMatch.captured(1);
@@ -427,6 +430,20 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
containerConfig.insert(config_key::userName, userName);
containerConfig.insert(config_key::password, password);
} else if (protocol == Proto::Socks5Proxy) {
QString proxyConfig = serverController->getTextFileFromContainer(container, credentials,
protocols::socks5Proxy::proxyConfigPath, errorCode);
const static QRegularExpression usernameAndPasswordRegExp("users (\\w+):CL:(\\w+)");
QRegularExpressionMatch usernameAndPasswordMatch = usernameAndPasswordRegExp.match(proxyConfig);
if (usernameAndPasswordMatch.hasMatch()) {
QString userName = usernameAndPasswordMatch.captured(1);
QString password = usernameAndPasswordMatch.captured(2);
containerConfig.insert(config_key::userName, userName);
containerConfig.insert(config_key::password, password);
}
}
config.insert(config_key::container, ContainerProps::containerToString(container));
@@ -603,6 +620,10 @@ void InstallController::clearCachedProfile(QSharedPointer<ServerController> serv
int serverIndex = m_serversModel->getProcessedServerIndex();
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getProcessedContainerIndex());
if (ContainerProps::containerService(container) == ServiceType::Other) {
return;
}
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
ServerCredentials serverCredentials =
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));

View File

@@ -35,6 +35,7 @@ namespace PageLoader
PageServiceSftpSettings,
PageServiceTorWebsiteSettings,
PageServiceDnsSettings,
PageServiceSocksProxySettings,
PageSetupWizardStart,
PageSetupWizardCredentials,

View File

@@ -281,7 +281,8 @@ ErrorCode ClientManagementModel::wgShow(const DockerContainer container, const S
}
};
for (int i = 0; i < peerList.size() && i < transferredDataList.size(); ++i) {
for (int i = 0; i < peerList.size() && i < transferredDataList.size() && i < latestHandshakeList.size(); ++i) {
const auto transferredData = getStrValue(transferredDataList[i]).split(",");
auto latestHandshake = getStrValue(latestHandshakeList[i]);
auto serverBytesReceived = transferredData.front().trimmed();

View File

@@ -41,6 +41,7 @@ QVariant ContainersModel::data(const QModelIndex &index, int role) const
case IsCurrentlyProcessedRole: return container == static_cast<DockerContainer>(m_processedContainerIndex);
case IsSupportedRole: return ContainerProps::isSupportedByCurrentPlatform(container);
case IsShareableRole: return ContainerProps::isShareable(container);
case InstallPageOrderRole: return ContainerProps::installPageOrder(container);
}
return QVariant();
@@ -112,5 +113,7 @@ QHash<int, QByteArray> ContainersModel::roleNames() const
roles[IsCurrentlyProcessedRole] = "isCurrentlyProcessed";
roles[IsSupportedRole] = "isSupported";
roles[IsShareableRole] = "isShareable";
roles[InstallPageOrderRole] = "installPageOrder";
return roles;
}

View File

@@ -31,7 +31,9 @@ public:
IsCurrentlyProcessedRole,
IsDefaultRole,
IsSupportedRole,
IsShareableRole
IsShareableRole,
InstallPageOrderRole
};
int rowCount(const QModelIndex &parent = QModelIndex()) const override;

View File

@@ -86,6 +86,7 @@ PageLoader::PageEnum ProtocolsModel::protocolPage(Proto protocol) const
case Proto::TorWebSite: return PageLoader::PageEnum::PageServiceTorWebsiteSettings;
case Proto::Dns: return PageLoader::PageEnum::PageServiceDnsSettings;
case Proto::Sftp: return PageLoader::PageEnum::PageServiceSftpSettings;
case Proto::Socks5Proxy: return PageLoader::PageEnum::PageServiceSocksProxySettings;
default: return PageLoader::PageEnum::PageProtocolOpenVpnSettings;
}
}

View File

@@ -548,6 +548,8 @@ QStringList ServersModel::getAllInstalledServicesName(const int serverIndex)
servicesName.append("SFTP");
} else if (container == DockerContainer::TorWebSite) {
servicesName.append("TOR");
} else if (container == DockerContainer::Socks5Proxy) {
servicesName.append("SOCKS5");
}
}
}
@@ -615,15 +617,18 @@ bool ServersModel::isDefaultServerDefaultContainerHasSplitTunneling()
{
auto server = m_servers.at(m_defaultServerIndex).toObject();
auto defaultContainer = ContainerProps::containerFromString(server.value(config_key::defaultContainer).toString());
auto containerConfig = server.value(config_key::containers).toArray().at(defaultContainer).toObject();
auto protocolConfig = containerConfig.value(ContainerProps::containerTypeToString(defaultContainer)).toObject();
if (defaultContainer == DockerContainer::Awg || defaultContainer == DockerContainer::WireGuard) {
return !(protocolConfig.value(config_key::last_config).toString().contains("AllowedIPs = 0.0.0.0/0, ::/0"));
} else if (defaultContainer == DockerContainer::Cloak || defaultContainer == DockerContainer::OpenVpn
|| defaultContainer == DockerContainer::ShadowSocks) {
return !(protocolConfig.value(config_key::last_config).toString().contains("redirect-gateway"));
auto containers = server.value(config_key::containers).toArray();
for (auto i = 0; i < containers.size(); i++) {
auto container = containers.at(i).toObject();
if (defaultContainer == DockerContainer::Awg || defaultContainer == DockerContainer::WireGuard) {
auto containerConfig = container.value(ContainerProps::containerTypeToString(defaultContainer)).toObject();
return !(containerConfig.value(config_key::last_config).toString().contains("AllowedIPs = 0.0.0.0/0, ::/0"));
} else if (defaultContainer == DockerContainer::Cloak || defaultContainer == DockerContainer::OpenVpn
|| defaultContainer == DockerContainer::ShadowSocks) {
auto containerConfig = container.value(ContainerProps::containerTypeToString(DockerContainer::OpenVpn)).toObject();
return !(containerConfig.value(config_key::last_config).toString().contains("redirect-gateway"));
}
}
return false;
}

View File

@@ -0,0 +1,80 @@
#include "socks5ProxyConfigModel.h"
#include "protocols/protocols_defs.h"
Socks5ProxyConfigModel::Socks5ProxyConfigModel(QObject *parent) : QAbstractListModel(parent)
{
}
int Socks5ProxyConfigModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 1;
}
bool Socks5ProxyConfigModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid() || index.row() < 0 || index.row() >= ContainerProps::allContainers().size()) {
return false;
}
switch (role) {
case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break;
case Roles::UserNameRole: m_protocolConfig.insert(config_key::userName, value.toString()); break;
case Roles::PasswordRole: m_protocolConfig.insert(config_key::password, value.toString()); break;
}
emit dataChanged(index, index, QList { role });
return true;
}
QVariant Socks5ProxyConfigModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) {
return false;
}
switch (role) {
case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString();
case Roles::UserNameRole:
return m_protocolConfig.value(config_key::userName).toString();
case Roles::PasswordRole: return m_protocolConfig.value(config_key::password).toString();
}
return QVariant();
}
void Socks5ProxyConfigModel::updateModel(const QJsonObject &config)
{
beginResetModel();
m_container = ContainerProps::containerFromString(config.value(config_key::container).toString());
m_fullConfig = config;
QJsonObject protocolConfig = config.value(config_key::socks5proxy).toObject();
m_protocolConfig.insert(config_key::userName,
protocolConfig.value(config_key::userName).toString());
m_protocolConfig.insert(config_key::password, protocolConfig.value(config_key::password).toString());
m_protocolConfig.insert(config_key::port, protocolConfig.value(config_key::port).toString());
endResetModel();
}
QJsonObject Socks5ProxyConfigModel::getConfig()
{
m_fullConfig.insert(config_key::socks5proxy, m_protocolConfig);
return m_fullConfig;
}
QHash<int, QByteArray> Socks5ProxyConfigModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[PortRole] = "port";
roles[UserNameRole] = "username";
roles[PasswordRole] = "password";
return roles;
}

View File

@@ -0,0 +1,40 @@
#ifndef SOCKS5PROXYCONFIGMODEL_H
#define SOCKS5PROXYCONFIGMODEL_H
#include <QAbstractListModel>
#include <QJsonObject>
#include "containers/containers_defs.h"
class Socks5ProxyConfigModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Roles {
PortRole = Qt::UserRole + 1,
UserNameRole,
PasswordRole
};
explicit Socks5ProxyConfigModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
public slots:
void updateModel(const QJsonObject &config);
QJsonObject getConfig();
protected:
QHash<int, QByteArray> roleNames() const override;
private:
DockerContainer m_container;
QJsonObject m_protocolConfig;
QJsonObject m_fullConfig;
};
#endif // SOCKS5PROXYCONFIGMODEL_H

View File

@@ -6,13 +6,14 @@ import Qt5Compat.GraphicalEffects
import ConnectionState 1.0
import PageEnum 1.0
import Style 1.0
Button {
id: root
property string defaultButtonColor: "#D7D8DB"
property string progressButtonColor: "#D7D8DB"
property string connectedButtonColor: "#FBB26A"
property string defaultButtonColor: AmneziaStyle.color.white
property string progressButtonColor: AmneziaStyle.color.white
property string connectedButtonColor: AmneziaStyle.color.orange
implicitWidth: 190
implicitHeight: 190
@@ -49,13 +50,13 @@ Button {
verticalOffset: 0
radius: 10
samples: 25
color: root.activeFocus ? "#D7D8DB" : "#FBB26A"
color: root.activeFocus ? AmneziaStyle.color.white : AmneziaStyle.color.orange
source: backgroundCircle
}
ShapePath {
fillColor: "transparent"
strokeColor: "#D7D8DB"
fillColor: AmneziaStyle.color.transparent
strokeColor: AmneziaStyle.color.white
strokeWidth: root.activeFocus ? 1 : 0
capStyle: ShapePath.RoundCap
@@ -70,10 +71,10 @@ Button {
}
ShapePath {
fillColor: "transparent"
fillColor: AmneziaStyle.color.transparent
strokeColor: {
if (ConnectionController.isConnectionInProgress) {
return "#261E1A"
return AmneziaStyle.color.connectionInProgress
} else if (ConnectionController.isConnected) {
return connectedButtonColor
} else {
@@ -113,8 +114,8 @@ Button {
visible: ConnectionController.isConnectionInProgress
ShapePath {
fillColor: "transparent"
strokeColor: "#D7D8DB"
fillColor: AmneziaStyle.color.transparent
strokeColor: AmneziaStyle.color.white
strokeWidth: 3
capStyle: ShapePath.RoundCap

View File

@@ -8,6 +8,7 @@ import "../Controls2/TextTypes"
import SortFilterProxyModel 0.2
import InstalledAppsModel 1.0
import Style 1.0
DrawerType2 {
id: root
@@ -133,7 +134,7 @@ DrawerType2 {
anchors.rightMargin: 16
anchors.leftMargin: 16
backgroundColor: "#2C2D30"
backgroundColor: AmneziaStyle.color.greyDark
textFieldPlaceholderText: qsTr("application name")
}

View File

@@ -2,6 +2,8 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Style 1.0
import "../Controls2"
import "../Controls2/TextTypes"
@@ -86,11 +88,11 @@ DrawerType2 {
Layout.rightMargin: 16
Layout.leftMargin: 16
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
disabledColor: "#878B91"
textColor: "#D7D8DB"
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.blackHovered
pressedColor: AmneziaStyle.color.blackPressed
disabledColor: AmneziaStyle.color.grey
textColor: AmneziaStyle.color.white
borderWidth: 1
text: noButtonText

View File

@@ -2,6 +2,8 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Style 1.0
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
@@ -145,8 +147,8 @@ DrawerType2 {
indicator: Rectangle {
width: parent.width - 1
height: parent.height
color: radioButton.hovered ? "#2C2D30" : "#1C1D21"
border.color: radioButton.focus ? "#D7D8DB" : "transparent"
color: radioButton.hovered ? AmneziaStyle.color.greyDark : AmneziaStyle.color.blackLight
border.color: radioButton.focus ? AmneziaStyle.color.white : AmneziaStyle.color.transparent
border.width: radioButton.focus ? 1 : 0
Behavior on color {

View File

@@ -9,6 +9,7 @@ import SortFilterProxyModel 0.2
import PageEnum 1.0
import ContainerProps 1.0
import Style 1.0
import "./"
import "../Controls2"
@@ -111,11 +112,11 @@ DrawerType2 {
Layout.fillWidth: true
Layout.topMargin: 8
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
disabledColor: "#878B91"
textColor: "#D7D8DB"
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.blackHovered
pressedColor: AmneziaStyle.color.blackPressed
disabledColor: AmneziaStyle.color.grey
textColor: AmneziaStyle.color.white
borderWidth: 1
text: qsTr("Copy")
@@ -134,11 +135,11 @@ DrawerType2 {
visible: false
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
disabledColor: "#878B91"
textColor: "#D7D8DB"
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.blackHovered
pressedColor: AmneziaStyle.color.blackPressed
disabledColor: AmneziaStyle.color.grey
textColor: AmneziaStyle.color.white
borderWidth: 1
text: qsTr("Copy config string")
@@ -153,11 +154,11 @@ DrawerType2 {
Layout.fillWidth: true
Layout.topMargin: 24
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
disabledColor: "#878B91"
textColor: "#D7D8DB"
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.blackHovered
pressedColor: AmneziaStyle.color.blackPressed
disabledColor: AmneziaStyle.color.grey
textColor: AmneziaStyle.color.white
borderWidth: 1
text: qsTr("Show connection settings")
@@ -281,9 +282,9 @@ DrawerType2 {
readOnly: true
activeFocusOnTab: false
color: "#D7D8DB"
selectionColor: "#633303"
selectedTextColor: "#D7D8DB"
color: AmneziaStyle.color.white
selectionColor: AmneziaStyle.color.brown
selectedTextColor: AmneziaStyle.color.white
font.pixelSize: 16
font.weight: Font.Medium
@@ -294,7 +295,7 @@ DrawerType2 {
wrapMode: Text.Wrap
background: Rectangle {
color: "transparent"
color: AmneziaStyle.color.transparent
}
}
}

View File

@@ -2,6 +2,8 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Style 1.0
import "../Controls2"
import "../Controls2/TextTypes"
@@ -14,7 +16,7 @@ Rectangle {
implicitWidth: transportProtoButtonGroup.implicitWidth
implicitHeight: transportProtoButtonGroup.implicitHeight
color: "#1C1D21"
color: AmneziaStyle.color.blackLight
radius: 16
onFocusChanged: {

View File

@@ -2,6 +2,8 @@ import QtQuick
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Style 1.0
Item {
id: root
@@ -28,7 +30,7 @@ Item {
ImageButtonType {
id: backButton
image: backButtonImage
imageColor: "#D7D8DB"
imageColor: AmneziaStyle.color.white
implicitWidth: 40
implicitHeight: 40
@@ -46,7 +48,7 @@ Item {
id: background
Layout.fillWidth: true
color: "transparent"
color: AmneziaStyle.color.transparent
}
}

View File

@@ -3,20 +3,22 @@ import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Style 1.0
import "TextTypes"
Button {
id: root
property string hoveredColor: "#C1C2C5"
property string defaultColor: "#D7D8DB"
property string disabledColor: "#494B50"
property string pressedColor: "#979799"
property string hoveredColor: AmneziaStyle.color.whiteHovered
property string defaultColor: AmneziaStyle.color.white
property string disabledColor: AmneziaStyle.color.greyDisabled
property string pressedColor: AmneziaStyle.color.grey
property string textColor: "#0E0E11"
property string textColor: AmneziaStyle.color.black
property string borderColor: "#D7D8DB"
property string borderFocusedColor: "#D7D8DB"
property string borderColor: AmneziaStyle.color.white
property string borderFocusedColor: AmneziaStyle.color.white
property int borderWidth: 0
property int borderFocusedWidth: 1
@@ -46,8 +48,8 @@ Button {
background: Rectangle {
id: focusBorder
color: "transparent"
border.color: root.activeFocus ? root.borderFocusedColor : "transparent"
color: AmneziaStyle.color.transparent
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
border.width: root.activeFocus ? root.borderFocusedWidth : 0
anchors.fill: parent

View File

@@ -2,6 +2,8 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Shapes
import Style 1.0
Popup {
id: root
anchors.centerIn: parent
@@ -16,7 +18,7 @@ Popup {
}
background: Rectangle {
color: "transparent"
color: AmneziaStyle.color.transparent
}
BusyIndicator {
@@ -40,8 +42,8 @@ Popup {
layer.samples: 4
ShapePath {
fillColor: "transparent"
strokeColor: "#787878"
fillColor: AmneziaStyle.color.transparent
strokeColor: AmneziaStyle.color.greyDisabled
strokeWidth: 3
capStyle: ShapePath.RoundCap

View File

@@ -2,6 +2,8 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Style 1.0
RadioButton {
id: root
@@ -9,17 +11,17 @@ RadioButton {
property string bodyText
property string footerText
property string hoveredColor: Qt.rgba(1, 1, 1, 0.05)
property string defaultColor: Qt.rgba(1, 1, 1, 0)
property string disabledColor: Qt.rgba(1, 1, 1, 0)
property string pressedColor: Qt.rgba(1, 1, 1, 0.05)
property string selectedColor: Qt.rgba(1, 1, 1, 0)
property string hoveredColor: AmneziaStyle.color.blackHovered
property string defaultColor: AmneziaStyle.color.transparent
property string disabledColor: AmneziaStyle.color.transparent
property string pressedColor: AmneziaStyle.color.blackPressed
property string selectedColor: AmneziaStyle.color.transparent
property string textColor: "#0E0E11"
property string textColor: AmneziaStyle.color.black
property string pressedBorderColor: Qt.rgba(251/255, 178/255, 106/255, 0.3)
property string selectedBorderColor: "#FBB26A"
property string defaultBodredColor: "transparent"
property string selectedBorderColor: AmneziaStyle.color.orange
property string defaultBodredColor: AmneziaStyle.color.transparent
property int borderWidth: 0
implicitWidth: content.implicitWidth
@@ -82,7 +84,7 @@ RadioButton {
Text {
text: root.headerText
wrapMode: Text.WordWrap
color: "#D7D8DB"
color: AmneziaStyle.color.white
font.pixelSize: 25
font.weight: 700
font.family: "PT Root UI VF"
@@ -97,7 +99,7 @@ RadioButton {
Text {
text: root.bodyText
wrapMode: Text.WordWrap
color: "#D7D8DB"
color: AmneziaStyle.color.white
font.pixelSize: 16
font.weight: 400
font.family: "PT Root UI VF"
@@ -113,7 +115,7 @@ RadioButton {
text: root.footerText
wrapMode: Text.WordWrap
visible: root.footerText !== ""
color: "#878B91"
color: AmneziaStyle.color.grey
font.pixelSize: 13
font.weight: 400
font.family: "PT Root UI VF"

View File

@@ -3,32 +3,34 @@ import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Style 1.0
import "TextTypes"
CheckBox {
id: root
property string descriptionText
property string descriptionTextColor: "#878B91"
property string descriptionTextDisabledColor: "#494B50"
property string descriptionTextColor: AmneziaStyle.color.grey
property string descriptionTextDisabledColor: AmneziaStyle.color.greyDisabled
property string textColor: "#D7D8DB"
property string textDisabledColor: "#878B91"
property string textColor: AmneziaStyle.color.white
property string textDisabledColor: AmneziaStyle.color.grey
property string hoveredColor: Qt.rgba(1, 1, 1, 0.05)
property string defaultColor: "transparent"
property string pressedColor: Qt.rgba(1, 1, 1, 0.05)
property string hoveredColor: AmneziaStyle.color.blackHovered
property string defaultColor: AmneziaStyle.color.transparent
property string pressedColor: AmneziaStyle.color.blackPressed
property string defaultBorderColor: "#D7D8DB"
property string checkedBorderColor: "#FBB26A"
property string checkedBorderDisabledColor: "#402102"
property string defaultBorderColor: AmneziaStyle.color.white
property string checkedBorderColor: AmneziaStyle.color.orange
property string checkedBorderDisabledColor: AmneziaStyle.color.brownDark
property string borderFocusedColor: "#D7D8DB"
property string borderFocusedColor: AmneziaStyle.color.white
property string checkedImageColor: "#FBB26A"
property string pressedImageColor: "#A85809"
property string defaultImageColor: "transparent"
property string checkedDisabledImageColor: "#84603D"
property string checkedImageColor: AmneziaStyle.color.orange
property string pressedImageColor: AmneziaStyle.color.orangeDark
property string defaultImageColor: AmneziaStyle.color.transparent
property string checkedDisabledImageColor: AmneziaStyle.color.brownLight
property string imageSource: "qrc:/images/controls/check.svg"
@@ -45,8 +47,8 @@ CheckBox {
focusPolicy: Qt.NoFocus
background: Rectangle {
color: "transparent"
border.color: root.focus ? borderFocusedColor : "transparent"
color: AmneziaStyle.color.transparent
border.color: root.focus ? borderFocusedColor : AmneziaStyle.color.transparent
border.width: 1
radius: 16
}
@@ -77,7 +79,7 @@ CheckBox {
anchors.centerIn: parent
width: 24
height: 24
color: "transparent"
color: AmneziaStyle.color.transparent
border.color: root.checked ?
(root.enabled ?
checkedBorderColor :

View File

@@ -1,6 +1,8 @@
import QtQuick
import QtQuick.Layouts
import Style 1.0
Rectangle {
Layout.fillWidth: true
@@ -8,5 +10,5 @@ Rectangle {
Layout.rightMargin: 16
height: 1
color: "#2C2D30"
color: AmneziaStyle.color.greyDark
}

View File

@@ -2,6 +2,8 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Style 1.0
import "TextTypes"
Item {
@@ -19,8 +21,8 @@ Item {
property Component collapsedContent
property Component expandedContent
property string defaultColor: "#1C1D21"
property string borderColor: "#2C2D30"
property string defaultColor: AmneziaStyle.color.blackLight
property string borderColor: AmneziaStyle.color.greyDark
property real expandedHeight
property real collapsedHeight: 0
@@ -90,7 +92,7 @@ Item {
id: background
anchors.fill: parent
color: root.isCollapsed ? "transparent" : Qt.rgba(14/255, 14/255, 17/255, 0.8)
color: root.isCollapsed ? AmneziaStyle.color.transparent : Qt.rgba(14/255, 14/255, 17/255, 0.8)
Behavior on color {
PropertyAnimation { duration: 200 }

View File

@@ -2,6 +2,8 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Style 1.0
import "TextTypes"
import "../Config"
@@ -9,31 +11,31 @@ Item {
id: root
property string text
property string textColor: "#d7d8db"
property string textDisabledColor: "#878B91"
property string textColor: AmneziaStyle.color.white
property string textDisabledColor: AmneziaStyle.color.grey
property int textMaximumLineCount: 2
property int textElide: Qt.ElideRight
property string descriptionText
property string descriptionTextColor: "#878B91"
property string descriptionTextDisabledColor: "#494B50"
property string descriptionTextColor: AmneziaStyle.color.grey
property string descriptionTextDisabledColor: AmneziaStyle.color.greyDisabled
property string headerText
property string headerBackButtonImage
property var rootButtonClickedFunction
property string rootButtonImage: "qrc:/images/controls/chevron-down.svg"
property string rootButtonImageColor: "#D7D8DB"
property string rootButtonBackgroundColor: "#1C1D21"
property string rootButtonBackgroundHoveredColor: "#1C1D21"
property string rootButtonBackgroundPressedColor: "#1C1D21"
property string rootButtonImageColor: AmneziaStyle.color.white
property string rootButtonBackgroundColor: AmneziaStyle.color.blackLight
property string rootButtonBackgroundHoveredColor: AmneziaStyle.color.blackLight
property string rootButtonBackgroundPressedColor: AmneziaStyle.color.blackLight
property string borderFocusedColor: "#D7D8DB"
property string borderFocusedColor: AmneziaStyle.color.white
property int borderFocusedWidth: 1
property string rootButtonHoveredBorderColor: "#494B50"
property string rootButtonDefaultBorderColor: "#2C2D30"
property string rootButtonPressedBorderColor: "#D7D8DB"
property string rootButtonHoveredBorderColor: AmneziaStyle.color.greyDisabled
property string rootButtonDefaultBorderColor: AmneziaStyle.color.greyDark
property string rootButtonPressedBorderColor: AmneziaStyle.color.white
property int rootButtonTextLeftMargins: 16
property int rootButtonTextTopMargin: 16
@@ -75,8 +77,8 @@ Item {
Rectangle {
id: focusBorder
color: "transparent"
border.color: root.activeFocus ? root.borderFocusedColor : "transparent"
color: AmneziaStyle.color.transparent
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
border.width: root.activeFocus ? root.borderFocusedWidth : 0
anchors.fill: rootButtonContent
radius: 16
@@ -96,7 +98,7 @@ Item {
}
return root.hovered ? root.rootButtonBackgroundHoveredColor : root.rootButtonBackgroundColor
} else {
return "transparent"
return AmneziaStyle.color.transparent
}
}

View File

@@ -1,6 +1,8 @@
import QtQuick
import QtQuick.Layouts
import Style 1.0
import "TextTypes"
Item {
@@ -37,7 +39,7 @@ Item {
implicitHeight: 40
image: root.actionButtonImage
imageColor: "#D7D8DB"
imageColor: AmneziaStyle.color.white
visible: image ? true : false
@@ -57,7 +59,7 @@ Item {
text: root.descriptionText
color: "#878B91"
color: AmneziaStyle.color.grey
visible: root.descriptionText !== ""
}

View File

@@ -1,6 +1,8 @@
import QtQuick
import QtQuick.Layouts
import Style 1.0
import "TextTypes"
Item {
@@ -46,7 +48,7 @@ Item {
Layout.alignment: Qt.AlignRight
image: root.actionButtonImage
imageColor: "#D7D8DB"
imageColor: AmneziaStyle.color.white
visible: image ? true : false
@@ -66,7 +68,7 @@ Item {
text: root.descriptionText
color: "#878B91"
color: AmneziaStyle.color.grey
visible: root.descriptionText !== ""
}

View File

@@ -2,24 +2,26 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Style 1.0
import "TextTypes"
RadioButton {
id: root
property string hoveredColor: Qt.rgba(1, 1, 1, 0.05)
property string defaultColor: Qt.rgba(1, 1, 1, 0)
property string checkedColor: Qt.rgba(1, 1, 1, 0)
property string disabledColor: "transparent"
property string hoveredColor: AmneziaStyle.color.blackHovered
property string defaultColor: AmneziaStyle.color.transparent
property string checkedColor: AmneziaStyle.color.transparent
property string disabledColor: AmneziaStyle.color.transparent
property string textColor: "#D7D8DB"
property string textDisabledColor: "#878B91"
property string textColor: AmneziaStyle.color.white
property string textDisabledColor: AmneziaStyle.color.grey
property string pressedBorderColor: "#494B50"
property string checkedBorderColor: "#FBB26A"
property string defaultBodredColor: "transparent"
property string checkedDisabledBorderColor: "#84603D"
property string borderFocusedColor: "#D7D8DB"
property string pressedBorderColor: AmneziaStyle.color.greyDisabled
property string checkedBorderColor: AmneziaStyle.color.orange
property string defaultBodredColor: AmneziaStyle.color.transparent
property string checkedDisabledBorderColor: AmneziaStyle.color.brownLight
property string borderFocusedColor: AmneziaStyle.color.white
property int borderWidth: 0
implicitWidth: content.implicitWidth

Some files were not shown because too many files have changed in this diff Show More