Merge remote-tracking branch 'origin/dev' into ios-wireguard
|
@ -2,5 +2,5 @@ TEMPLATE = subdirs
|
||||||
SUBDIRS = client
|
SUBDIRS = client
|
||||||
|
|
||||||
!ios:!android {
|
!ios:!android {
|
||||||
SUBDIRS += service platform
|
SUBDIRS += service
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<manifest package="org.amnezia.vpn" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="-- %%INSERT_VERSION_NAME%% --" android:versionCode="-- %%INSERT_VERSION_CODE%% --" android:installLocation="auto">
|
<manifest package="org.amnezia.vpn" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="-- %%INSERT_VERSION_NAME%% --" android:versionCode="-- %%INSERT_VERSION_CODE%% --" android:installLocation="auto">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
|
||||||
<uses-permission android:name="android.permission.CAMERA"/>
|
<uses-permission android:name="android.permission.CAMERA"/>
|
||||||
|
|
||||||
|
@ -15,27 +15,15 @@
|
||||||
<!-- %%INSERT_FEATURES -->
|
<!-- %%INSERT_FEATURES -->
|
||||||
|
|
||||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
||||||
<application
|
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --" android:extractNativeLibs="true" android:icon="@drawable/icon">
|
||||||
android:hardwareAccelerated="true"
|
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="unspecified" android:launchMode="singleTop" android:theme="@style/splashScreenTheme">
|
||||||
android:name="org.qtproject.qt5.android.bindings.QtApplication"
|
|
||||||
android:label="-- %%INSERT_APP_NAME%% --"
|
|
||||||
android:extractNativeLibs="true"
|
|
||||||
android:icon="@drawable/icon">
|
|
||||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
|
|
||||||
android:name="org.qtproject.qt5.android.bindings.QtActivity"
|
|
||||||
|
|
||||||
android:label="-- %%INSERT_APP_NAME%% --"
|
|
||||||
android:screenOrientation="unspecified"
|
|
||||||
android:launchMode="singleTop">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<!-- Application arguments -->
|
<!-- Application arguments -->
|
||||||
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
|
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
|
||||||
<!-- Application arguments -->
|
<!-- Application arguments -->
|
||||||
|
|
||||||
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
||||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||||
|
@ -43,7 +31,6 @@
|
||||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
||||||
<!-- Deploy Qt libs as part of package -->
|
<!-- Deploy Qt libs as part of package -->
|
||||||
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
|
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
|
||||||
|
|
||||||
<!-- Run with local libs -->
|
<!-- Run with local libs -->
|
||||||
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
|
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
|
||||||
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
||||||
|
@ -58,7 +45,6 @@
|
||||||
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
||||||
<meta-data android:value="@string/unsupported_android_version" android:name="android.app.unsupported_android_version"/>
|
<meta-data android:value="@string/unsupported_android_version" android:name="android.app.unsupported_android_version"/>
|
||||||
<!-- Messages maps -->
|
<!-- Messages maps -->
|
||||||
|
|
||||||
<!-- Splash screen -->
|
<!-- Splash screen -->
|
||||||
<!-- Orientation-specific (portrait/landscape) data is checked first. If not available for current orientation,
|
<!-- Orientation-specific (portrait/landscape) data is checked first. If not available for current orientation,
|
||||||
then android.app.splash_screen_drawable. For best results, use together with splash_screen_sticky and
|
then android.app.splash_screen_drawable. For best results, use together with splash_screen_sticky and
|
||||||
|
@ -69,7 +55,6 @@
|
||||||
<!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
|
<!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
|
||||||
<!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
|
<!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
|
||||||
<!-- Splash screen -->
|
<!-- Splash screen -->
|
||||||
|
|
||||||
<!-- Background running -->
|
<!-- Background running -->
|
||||||
<!-- Warning: changing this value to true may cause unexpected crashes if the
|
<!-- Warning: changing this value to true may cause unexpected crashes if the
|
||||||
application still try to draw after
|
application still try to draw after
|
||||||
|
@ -77,11 +62,9 @@
|
||||||
signal is sent! -->
|
signal is sent! -->
|
||||||
<meta-data android:name="android.app.background_running" android:value="false"/>
|
<meta-data android:name="android.app.background_running" android:value="false"/>
|
||||||
<!-- Background running -->
|
<!-- Background running -->
|
||||||
|
|
||||||
<!-- auto screen scale factor -->
|
<!-- auto screen scale factor -->
|
||||||
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
|
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
|
||||||
<!-- auto screen scale factor -->
|
<!-- auto screen scale factor -->
|
||||||
|
|
||||||
<!-- extract android style -->
|
<!-- extract android style -->
|
||||||
<!-- available android:values :
|
<!-- available android:values :
|
||||||
* default - In most cases this will be the same as "full", but it can also be something else if needed, e.g., for compatibility reasons
|
* default - In most cases this will be the same as "full", but it can also be something else if needed, e.g., for compatibility reasons
|
||||||
|
@ -92,24 +75,19 @@
|
||||||
<meta-data android:name="android.app.extract_android_style" android:value="default"/>
|
<meta-data android:name="android.app.extract_android_style" android:value="default"/>
|
||||||
<!-- extract android style -->
|
<!-- extract android style -->
|
||||||
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/splashscreen"/>
|
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/splashscreen"/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service android:name=".VPNService"
|
<service android:name=".VPNService"
|
||||||
|
android:process=":QtOnlyProcess"
|
||||||
android:permission="android.permission.BIND_VPN_SERVICE">
|
android:permission="android.permission.BIND_VPN_SERVICE">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.net.VpnService"/>
|
<action android:name="android.net.VpnService"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
<service android:name="org.amnezia.vpn.qt.VPNPermissionHelper"
|
<service android:name="org.mozilla.firefox.vpn.qt.VPNPermissionHelper"
|
||||||
|
android:permission="android.permission.BIND_VPN_SERVICE">
|
||||||
android:permission="android.permission.BIND_VPN_SERVICE">
|
</service>
|
||||||
</service>
|
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -102,8 +102,8 @@ android {
|
||||||
resConfig "en"
|
resConfig "en"
|
||||||
minSdkVersion = 24
|
minSdkVersion = 24
|
||||||
targetSdkVersion = 30
|
targetSdkVersion = 30
|
||||||
versionCode 7 // Change to a higher number
|
versionCode 8 // Change to a higher number
|
||||||
versionName "2.0.7" // Change to a higher number
|
versionName "2.0.8" // Change to a higher number
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|
|
@ -37,12 +37,13 @@ class VPNService : android.net.VpnService() {
|
||||||
SharedLibraryLoader.loadSharedLibrary(this, "ovpn3")
|
SharedLibraryLoader.loadSharedLibrary(this, "ovpn3")
|
||||||
Log.i(tag, "Loaded libs")
|
Log.i(tag, "Loaded libs")
|
||||||
Log.e(tag, "Wireguard Version ${wgVersion()}")
|
Log.e(tag, "Wireguard Version ${wgVersion()}")
|
||||||
mOpenVPNThreadv3 = OpenVPNThreadv3 (this)
|
mOpenVPNThreadv3 = OpenVPNThreadv3(this)
|
||||||
mAlreadyInitialised = true
|
mAlreadyInitialised = true
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUnbind(intent: Intent?): Boolean {
|
override fun onUnbind(intent: Intent?): Boolean {
|
||||||
|
Log.v(tag, "Got Unbind request")
|
||||||
if (!isUp) {
|
if (!isUp) {
|
||||||
// If the Qt Client got closed while we were not connected
|
// If the Qt Client got closed while we were not connected
|
||||||
// we do not need to stay as a foreground service.
|
// we do not need to stay as a foreground service.
|
||||||
|
@ -52,9 +53,9 @@ class VPNService : android.net.VpnService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EntryPoint for the Service, gets Called when AndroidController.cpp
|
* EntryPoint for the Service, gets Called when AndroidController.cpp
|
||||||
* calles bindService. Returns the [VPNServiceBinder] so QT can send Requests to it.
|
* calles bindService. Returns the [VPNServiceBinder] so QT can send Requests to it.
|
||||||
*/
|
*/
|
||||||
override fun onBind(intent: Intent?): IBinder? {
|
override fun onBind(intent: Intent?): IBinder? {
|
||||||
Log.v(tag, "Got Bind request")
|
Log.v(tag, "Got Bind request")
|
||||||
init()
|
init()
|
||||||
|
@ -62,10 +63,10 @@ class VPNService : android.net.VpnService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Might be the entryPoint if the Service gets Started via an
|
* Might be the entryPoint if the Service gets Started via an
|
||||||
* Service Intent: Might be from Always-On-Vpn from Settings
|
* Service Intent: Might be from Always-On-Vpn from Settings
|
||||||
* or from Booting the device and having "connect on boot" enabled.
|
* or from Booting the device and having "connect on boot" enabled.
|
||||||
*/
|
*/
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
init()
|
init()
|
||||||
intent?.let {
|
intent?.let {
|
||||||
|
@ -84,28 +85,28 @@ class VPNService : android.net.VpnService() {
|
||||||
Log.e(
|
Log.e(
|
||||||
tag,
|
tag,
|
||||||
"VPN service was triggered without defining a Server or having a tunnel"
|
"VPN service was triggered without defining a Server or having a tunnel"
|
||||||
)
|
)
|
||||||
return super.onStartCommand(intent, flags, startId)
|
return super.onStartCommand(intent, flags, startId)
|
||||||
}
|
|
||||||
this.mConfig = JSONObject(lastConfString)
|
|
||||||
}
|
}
|
||||||
|
this.mConfig = JSONObject(lastConfString)
|
||||||
return super.onStartCommand(intent, flags, startId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoked when the application is revoked.
|
return super.onStartCommand(intent, flags, startId)
|
||||||
// At this moment, the VPN interface is already deactivated by the system.
|
}
|
||||||
override fun onRevoke() {
|
|
||||||
this.turnOff()
|
|
||||||
super.onRevoke()
|
|
||||||
}
|
|
||||||
|
|
||||||
var connectionTime: Long = 0
|
// Invoked when the application is revoked.
|
||||||
|
// At this moment, the VPN interface is already deactivated by the system.
|
||||||
|
override fun onRevoke() {
|
||||||
|
this.turnOff()
|
||||||
|
super.onRevoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
var connectionTime: Long = 0
|
||||||
get() {
|
get() {
|
||||||
return mConnectionTime
|
return mConnectionTime
|
||||||
}
|
}
|
||||||
|
|
||||||
var isUp: Boolean
|
var isUp: Boolean
|
||||||
get() {
|
get() {
|
||||||
return currentTunnelHandle >= 0
|
return currentTunnelHandle >= 0
|
||||||
}
|
}
|
||||||
|
@ -118,7 +119,7 @@ class VPNService : android.net.VpnService() {
|
||||||
mBinder.dispatchEvent(VPNServiceBinder.EVENTS.disconnected, "")
|
mBinder.dispatchEvent(VPNServiceBinder.EVENTS.disconnected, "")
|
||||||
mConnectionTime = 0
|
mConnectionTime = 0
|
||||||
}
|
}
|
||||||
val status: JSONObject
|
val status: JSONObject
|
||||||
get() {
|
get() {
|
||||||
val deviceIpv4: String = ""
|
val deviceIpv4: String = ""
|
||||||
return JSONObject().apply {
|
return JSONObject().apply {
|
||||||
|
@ -128,299 +129,307 @@ class VPNService : android.net.VpnService() {
|
||||||
putOpt("deviceIpv4", mConfig?.getJSONObject("device")?.getString("ipv4Address"))
|
putOpt("deviceIpv4", mConfig?.getJSONObject("device")?.getString("ipv4Address"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
* Checks if the VPN Permission is given.
|
|
||||||
* If the permission is given, returns true
|
|
||||||
* Requests permission and returns false if not.
|
|
||||||
*/
|
|
||||||
fun checkPermissions(): Boolean {
|
|
||||||
// See https://developer.android.com/guide/topics/connectivity/vpn#connect_a_service
|
|
||||||
// Call Prepare, if we get an Intent back, we dont have the VPN Permission
|
|
||||||
// from the user. So we need to pass this to our main Activity and exit here.
|
|
||||||
val intent = prepare(this)
|
|
||||||
if (intent == null) {
|
|
||||||
Log.e(tag, "VPN Permission Already Present")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
Log.e(tag, "Requesting VPN Permission")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun turnOn(json: JSONObject?): Int {
|
/*
|
||||||
if (!checkPermissions()) {
|
* Checks if the VPN Permission is given.
|
||||||
Log.e(tag, "turn on was called without no permissions present!")
|
* If the permission is given, returns true
|
||||||
isUp = false
|
* Requests permission and returns false if not.
|
||||||
return 0
|
*/
|
||||||
}
|
fun checkPermissions(): Boolean {
|
||||||
Log.i(tag, "Permission okay")
|
// See https://developer.android.com/guide/topics/connectivity/vpn#connect_a_service
|
||||||
mConfig = json!!
|
// Call Prepare, if we get an Intent back, we dont have the VPN Permission
|
||||||
mProtocol = mConfig!!.getString("protocol")
|
// from the user. So we need to pass this to our main Activity and exit here.
|
||||||
when (mProtocol) {
|
val intent = prepare(this)
|
||||||
"openvpn" -> startOpenVpn()
|
if (intent == null) {
|
||||||
"wireguard" -> startWireGuard()
|
Log.e(tag, "VPN Permission Already Present")
|
||||||
else -> {
|
|
||||||
Log.e(tag, "No protocol")
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
fun establish(): ParcelFileDescriptor? {
|
|
||||||
mbuilder.allowFamily(OsConstants.AF_INET)
|
|
||||||
mbuilder.allowFamily(OsConstants.AF_INET6)
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) mbuilder.setMetered(false)
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) setUnderlyingNetworks(null)
|
|
||||||
|
|
||||||
return mbuilder.establish()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setMtu(mtu: Int) {
|
|
||||||
mbuilder.setMtu(mtu)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addAddress(ip: String, len: Int){
|
|
||||||
Log.v(tag, "mbuilder.addAddress($ip, $len)")
|
|
||||||
mbuilder.addAddress(ip, len)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addRoute(ip: String, len: Int){
|
|
||||||
Log.v(tag, "mbuilder.addRoute($ip, $len)")
|
|
||||||
mbuilder.addRoute(ip, len)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addDNS(ip: String){
|
|
||||||
Log.v(tag, "mbuilder.addDnsServer($ip)")
|
|
||||||
mbuilder.addDnsServer(ip)
|
|
||||||
if ("samsung".equals(Build.BRAND) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
|
|
||||||
mbuilder.addRoute(ip, 32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setSessionName(name: String){
|
|
||||||
Log.v(tag, "mbuilder.setSession($name)")
|
|
||||||
mbuilder.setSession(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addHttpProxy(host: String, port: Int): Boolean{
|
|
||||||
val proxyInfo = ProxyInfo.buildDirectProxy(host, port)
|
|
||||||
Log.v(tag, "mbuilder.addHttpProxy($host, $port)")
|
|
||||||
mbuilder.setHttpProxy(proxyInfo)
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
Log.e(tag, "Requesting VPN Permission")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
fun setDomain(domain: String) {
|
fun turnOn(json: JSONObject?): Int {
|
||||||
Log.v(tag, "mbuilder.setDomain($domain)")
|
if (!checkPermissions()) {
|
||||||
mbuilder.addSearchDomain(domain)
|
Log.e(tag, "turn on was called without no permissions present!")
|
||||||
}
|
|
||||||
|
|
||||||
fun turnOff() {
|
|
||||||
Log.v(tag, "Try to disable tunnel")
|
|
||||||
when(mProtocol){
|
|
||||||
"wireguard" -> wgTurnOff(currentTunnelHandle)
|
|
||||||
"openvpn" -> ovpnTurnOff()
|
|
||||||
else -> {
|
|
||||||
Log.e(tag, "No protocol")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentTunnelHandle = -1
|
|
||||||
stopForeground(true)
|
|
||||||
|
|
||||||
isUp = false
|
isUp = false
|
||||||
stopSelf();
|
return 0
|
||||||
}
|
}
|
||||||
|
Log.i(tag, "Permission okay")
|
||||||
|
mConfig = json!!
|
||||||
|
mProtocol = mConfig!!.getString("protocol")
|
||||||
|
when (mProtocol) {
|
||||||
|
"openvpn" -> startOpenVpn()
|
||||||
|
"wireguard" -> startWireGuard()
|
||||||
|
else -> {
|
||||||
|
Log.e(tag, "No protocol")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NotificationUtil.show(this) // Go foreground
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun establish(): ParcelFileDescriptor? {
|
||||||
|
mbuilder.allowFamily(OsConstants.AF_INET)
|
||||||
|
mbuilder.allowFamily(OsConstants.AF_INET6)
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) mbuilder.setMetered(false)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) setUnderlyingNetworks(null)
|
||||||
|
|
||||||
|
return mbuilder.establish()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setMtu(mtu: Int) {
|
||||||
|
mbuilder.setMtu(mtu)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addAddress(ip: String, len: Int) {
|
||||||
|
Log.v(tag, "mbuilder.addAddress($ip, $len)")
|
||||||
|
mbuilder.addAddress(ip, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addRoute(ip: String, len: Int) {
|
||||||
|
Log.v(tag, "mbuilder.addRoute($ip, $len)")
|
||||||
|
mbuilder.addRoute(ip, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addDNS(ip: String) {
|
||||||
|
Log.v(tag, "mbuilder.addDnsServer($ip)")
|
||||||
|
mbuilder.addDnsServer(ip)
|
||||||
|
if ("samsung".equals(Build.BRAND) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
mbuilder.addRoute(ip, 32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSessionName(name: String) {
|
||||||
|
Log.v(tag, "mbuilder.setSession($name)")
|
||||||
|
mbuilder.setSession(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addHttpProxy(host: String, port: Int): Boolean {
|
||||||
|
val proxyInfo = ProxyInfo.buildDirectProxy(host, port)
|
||||||
|
Log.v(tag, "mbuilder.addHttpProxy($host, $port)")
|
||||||
|
mbuilder.setHttpProxy(proxyInfo)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setDomain(domain: String) {
|
||||||
|
Log.v(tag, "mbuilder.setDomain($domain)")
|
||||||
|
mbuilder.addSearchDomain(domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun turnOff() {
|
||||||
|
Log.v(tag, "Try to disable tunnel")
|
||||||
|
when (mProtocol) {
|
||||||
|
"wireguard" -> wgTurnOff(currentTunnelHandle)
|
||||||
|
"openvpn" -> ovpnTurnOff()
|
||||||
|
else -> {
|
||||||
|
Log.e(tag, "No protocol")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentTunnelHandle = -1
|
||||||
|
stopForeground(true)
|
||||||
|
|
||||||
|
isUp = false
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun ovpnTurnOff() {
|
private fun ovpnTurnOff() {
|
||||||
mOpenVPNThreadv3?.stop()
|
mOpenVPNThreadv3?.stop()
|
||||||
mOpenVPNThreadv3 = null
|
mOpenVPNThreadv3 = null
|
||||||
Log.e(tag, "mOpenVPNThreadv3 stop!")
|
Log.e(tag, "mOpenVPNThreadv3 stop!")
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Configures an Android VPN Service Tunnel
|
/**
|
||||||
* with a given Wireguard Config
|
* Configures an Android VPN Service Tunnel
|
||||||
*/
|
* with a given Wireguard Config
|
||||||
private fun setupBuilder(config: Config, builder: Builder) {
|
*/
|
||||||
// Setup Split tunnel
|
private fun setupBuilder(config: Config, builder: Builder) {
|
||||||
for (excludedApplication in config.`interface`.excludedApplications)
|
// Setup Split tunnel
|
||||||
|
for (excludedApplication in config.`interface`.excludedApplications)
|
||||||
builder.addDisallowedApplication(excludedApplication)
|
builder.addDisallowedApplication(excludedApplication)
|
||||||
|
|
||||||
// Device IP
|
// Device IP
|
||||||
for (addr in config.`interface`.addresses) builder.addAddress(addr.address, addr.mask)
|
for (addr in config.`interface`.addresses) builder.addAddress(addr.address, addr.mask)
|
||||||
// DNS
|
// DNS
|
||||||
for (addr in config.`interface`.dnsServers) builder.addDnsServer(addr.hostAddress)
|
for (addr in config.`interface`.dnsServers) builder.addDnsServer(addr.hostAddress)
|
||||||
// Add All routes the VPN may route tos
|
// Add All routes the VPN may route tos
|
||||||
for (peer in config.peers) {
|
for (peer in config.peers) {
|
||||||
for (addr in peer.allowedIps) {
|
for (addr in peer.allowedIps) {
|
||||||
builder.addRoute(addr.address, addr.mask)
|
builder.addRoute(addr.address, addr.mask)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
builder.allowFamily(OsConstants.AF_INET)
|
|
||||||
builder.allowFamily(OsConstants.AF_INET6)
|
|
||||||
builder.setMtu(config.`interface`.mtu.orElse(1280))
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) builder.setMetered(false)
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) setUnderlyingNetworks(null)
|
|
||||||
|
|
||||||
builder.setBlocking(true)
|
|
||||||
}
|
}
|
||||||
|
builder.allowFamily(OsConstants.AF_INET)
|
||||||
|
builder.allowFamily(OsConstants.AF_INET6)
|
||||||
|
builder.setMtu(config.`interface`.mtu.orElse(1280))
|
||||||
|
|
||||||
/**
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) builder.setMetered(false)
|
||||||
* Gets config value for {key} from the Current
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) setUnderlyingNetworks(null)
|
||||||
* running Wireguard tunnel
|
|
||||||
*/
|
builder.setBlocking(true)
|
||||||
private fun getConfigValue(key: String): String? {
|
}
|
||||||
if (!isUp) {
|
|
||||||
return null
|
/**
|
||||||
}
|
* Gets config value for {key} from the Current
|
||||||
val config = wgGetConfig(currentTunnelHandle) ?: return null
|
* running Wireguard tunnel
|
||||||
val lines = config.split("\n")
|
*/
|
||||||
for (line in lines) {
|
private fun getConfigValue(key: String): String? {
|
||||||
val parts = line.split("=")
|
if (!isUp) {
|
||||||
val k = parts.first()
|
|
||||||
val value = parts.last()
|
|
||||||
if (key == k) {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
val config = wgGetConfig(currentTunnelHandle) ?: return null
|
||||||
|
val lines = config.split("\n")
|
||||||
|
for (line in lines) {
|
||||||
|
val parts = line.split("=")
|
||||||
|
val k = parts.first()
|
||||||
|
val value = parts.last()
|
||||||
|
if (key == k) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
private fun parseConfigData(data: String): Map<String, Map<String, String>> {
|
private fun parseConfigData(data: String): Map<String, Map<String, String>> {
|
||||||
val parseData = mutableMapOf<String, Map<String, String>>()
|
val parseData = mutableMapOf<String, Map<String, String>>()
|
||||||
var currentSection: Pair<String, MutableMap<String, String>>? = null
|
var currentSection: Pair<String, MutableMap<String, String>>? = null
|
||||||
data.lines().forEach { line ->
|
data.lines().forEach { line ->
|
||||||
if (line.isNotEmpty()) {
|
if (line.isNotEmpty()) {
|
||||||
if (line.startsWith('[')) {
|
if (line.startsWith('[')) {
|
||||||
currentSection?.let {
|
currentSection?.let {
|
||||||
parseData.put(it.first, it.second)
|
parseData.put(it.first, it.second)
|
||||||
}
|
|
||||||
currentSection = line.substring(1, line.indexOfLast { it == ']' }) to mutableMapOf()
|
|
||||||
} else {
|
|
||||||
val parameter = line.split("=", limit = 2)
|
|
||||||
currentSection!!.second.put(parameter.first().trim(), parameter.last().trim())
|
|
||||||
}
|
}
|
||||||
|
currentSection =
|
||||||
|
line.substring(1, line.indexOfLast { it == ']' }) to mutableMapOf()
|
||||||
|
} else {
|
||||||
|
val parameter = line.split("=", limit = 2)
|
||||||
|
currentSection!!.second.put(parameter.first().trim(), parameter.last().trim())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentSection?.let {
|
}
|
||||||
parseData.put(it.first, it.second)
|
currentSection?.let {
|
||||||
|
parseData.put(it.first, it.second)
|
||||||
|
}
|
||||||
|
return parseData
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Wireguard [Config] from a [json] string -
|
||||||
|
* The [json] will be created in AndroidVpnProtocol.cpp
|
||||||
|
*/
|
||||||
|
private fun buildWireugardConfig(obj: JSONObject): Config {
|
||||||
|
val confBuilder = Config.Builder()
|
||||||
|
val wireguardConfigData = obj.getJSONObject("wireguard_config_data")
|
||||||
|
val config = parseConfigData(wireguardConfigData.getString("config"))
|
||||||
|
val peerBuilder = Peer.Builder()
|
||||||
|
val peerConfig = config["Peer"]!!
|
||||||
|
peerBuilder.setPublicKey(Key.fromBase64(peerConfig["PublicKey"]))
|
||||||
|
peerConfig["PresharedKey"]?.let {
|
||||||
|
peerBuilder.setPreSharedKey(Key.fromBase64(it))
|
||||||
|
}
|
||||||
|
val allowedIPList = peerConfig["AllowedIPs"]?.split(",") ?: emptyList()
|
||||||
|
if (allowedIPList.isEmpty()) {
|
||||||
|
val internet = InetNetwork.parse("0.0.0.0/0") // aka The whole internet.
|
||||||
|
peerBuilder.addAllowedIp(internet)
|
||||||
|
} else {
|
||||||
|
allowedIPList.forEach {
|
||||||
|
val network = InetNetwork.parse(it.trim())
|
||||||
|
peerBuilder.addAllowedIp(network)
|
||||||
}
|
}
|
||||||
return parseData
|
}
|
||||||
|
peerBuilder.setEndpoint(InetEndpoint.parse(peerConfig["Endpoint"]))
|
||||||
|
peerConfig["PersistentKeepalive"]?.let {
|
||||||
|
peerBuilder.setPersistentKeepalive(it.toInt())
|
||||||
|
}
|
||||||
|
confBuilder.addPeer(peerBuilder.build())
|
||||||
|
|
||||||
|
val ifaceBuilder = Interface.Builder()
|
||||||
|
val ifaceConfig = config["Interface"]!!
|
||||||
|
ifaceBuilder.parsePrivateKey(ifaceConfig["PrivateKey"])
|
||||||
|
ifaceBuilder.addAddress(InetNetwork.parse(ifaceConfig["Address"]))
|
||||||
|
ifaceConfig["DNS"]!!.split(",").forEach {
|
||||||
|
ifaceBuilder.addDnsServer(InetNetwork.parse(it.trim()).address)
|
||||||
|
}
|
||||||
|
/*val jExcludedApplication = obj.getJSONArray("excludedApps")
|
||||||
|
(0 until jExcludedApplication.length()).toList().forEach {
|
||||||
|
val appName = jExcludedApplication.get(it).toString()
|
||||||
|
ifaceBuilder.excludeApplication(appName)
|
||||||
|
}*/
|
||||||
|
confBuilder.setInterface(ifaceBuilder.build())
|
||||||
|
|
||||||
|
return confBuilder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVpnConfig(): JSONObject {
|
||||||
|
return mConfig!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startOpenVpn() {
|
||||||
|
mOpenVPNThreadv3 = OpenVPNThreadv3(this)
|
||||||
|
Thread({
|
||||||
|
mOpenVPNThreadv3?.run()
|
||||||
|
}).start()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startWireGuard() {
|
||||||
|
val wireguard_conf = buildWireugardConfig(mConfig!!)
|
||||||
|
if (currentTunnelHandle != -1) {
|
||||||
|
Log.e(tag, "Tunnel already up")
|
||||||
|
// Turn the tunnel down because this might be a switch
|
||||||
|
wgTurnOff(currentTunnelHandle)
|
||||||
|
}
|
||||||
|
val wgConfig: String = wireguard_conf!!.toWgUserspaceString()
|
||||||
|
val builder = Builder()
|
||||||
|
setupBuilder(wireguard_conf, builder)
|
||||||
|
builder.setSession("avpn0")
|
||||||
|
builder.establish().use { tun ->
|
||||||
|
if (tun == null) return
|
||||||
|
Log.i(tag, "Go backend " + wgVersion())
|
||||||
|
currentTunnelHandle = wgTurnOn("avpn0", tun.detachFd(), wgConfig)
|
||||||
|
}
|
||||||
|
if (currentTunnelHandle < 0) {
|
||||||
|
Log.e(tag, "Activation Error Code -> $currentTunnelHandle")
|
||||||
|
isUp = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
protect(wgGetSocketV4(currentTunnelHandle))
|
||||||
|
protect(wgGetSocketV6(currentTunnelHandle))
|
||||||
|
isUp = true
|
||||||
|
|
||||||
|
// Store the config in case the service gets
|
||||||
|
// asked boot vpn from the OS
|
||||||
|
val prefs = Prefs.get(this)
|
||||||
|
prefs.edit()
|
||||||
|
.putString("lastConf", mConfig.toString())
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun startService(c: Context) {
|
||||||
|
c.applicationContext.startService(
|
||||||
|
Intent(c.applicationContext, VPNService::class.java).apply {
|
||||||
|
putExtra("startOnly", true)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@JvmStatic
|
||||||
* Create a Wireguard [Config] from a [json] string -
|
private external fun wgGetConfig(handle: Int): String?
|
||||||
* The [json] will be created in AndroidVpnProtocol.cpp
|
|
||||||
*/
|
|
||||||
private fun buildWireugardConfig(obj: JSONObject): Config {
|
|
||||||
val confBuilder = Config.Builder()
|
|
||||||
val wireguardConfigData = obj.getJSONObject("wireguard_config_data")
|
|
||||||
val config = parseConfigData(wireguardConfigData.getString("config"))
|
|
||||||
val peerBuilder = Peer.Builder()
|
|
||||||
val peerConfig = config["Peer"]!!
|
|
||||||
peerBuilder.setPublicKey(Key.fromBase64(peerConfig["PublicKey"]))
|
|
||||||
peerConfig["PresharedKey"]?.let {
|
|
||||||
peerBuilder.setPreSharedKey(Key.fromBase64(it))
|
|
||||||
}
|
|
||||||
val allowedIPList = peerConfig["AllowedIPs"]?.split(",") ?: emptyList()
|
|
||||||
if (allowedIPList.isEmpty()) {
|
|
||||||
val internet = InetNetwork.parse("0.0.0.0/0") // aka The whole internet.
|
|
||||||
peerBuilder.addAllowedIp(internet)
|
|
||||||
} else {
|
|
||||||
allowedIPList.forEach {
|
|
||||||
val network = InetNetwork.parse(it.trim())
|
|
||||||
peerBuilder.addAllowedIp(network)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
peerBuilder.setEndpoint(InetEndpoint.parse(peerConfig["Endpoint"]))
|
|
||||||
peerConfig["PersistentKeepalive"]?.let {
|
|
||||||
peerBuilder.setPersistentKeepalive(it.toInt())
|
|
||||||
}
|
|
||||||
confBuilder.addPeer(peerBuilder.build())
|
|
||||||
|
|
||||||
val ifaceBuilder = Interface.Builder()
|
@JvmStatic
|
||||||
val ifaceConfig = config["Interface"]!!
|
private external fun wgGetSocketV4(handle: Int): Int
|
||||||
ifaceBuilder.parsePrivateKey(ifaceConfig["PrivateKey"])
|
|
||||||
ifaceBuilder.addAddress(InetNetwork.parse(ifaceConfig["Address"]))
|
|
||||||
ifaceConfig["DNS"]!!.split(",").forEach {
|
|
||||||
ifaceBuilder.addDnsServer(InetNetwork.parse(it.trim()).address)
|
|
||||||
}
|
|
||||||
/*val jExcludedApplication = obj.getJSONArray("excludedApps")
|
|
||||||
(0 until jExcludedApplication.length()).toList().forEach {
|
|
||||||
val appName = jExcludedApplication.get(it).toString()
|
|
||||||
ifaceBuilder.excludeApplication(appName)
|
|
||||||
}*/
|
|
||||||
confBuilder.setInterface(ifaceBuilder.build())
|
|
||||||
|
|
||||||
return confBuilder.build()
|
@JvmStatic
|
||||||
}
|
private external fun wgGetSocketV6(handle: Int): Int
|
||||||
|
|
||||||
fun getVpnConfig(): JSONObject {
|
@JvmStatic
|
||||||
return mConfig!!
|
private external fun wgTurnOff(handle: Int)
|
||||||
}
|
|
||||||
|
|
||||||
private fun startOpenVpn() {
|
@JvmStatic
|
||||||
mOpenVPNThreadv3 = OpenVPNThreadv3 (this)
|
private external fun wgTurnOn(ifName: String, tunFd: Int, settings: String): Int
|
||||||
Thread ({
|
|
||||||
mOpenVPNThreadv3?.run()
|
|
||||||
}).start()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startWireGuard(){
|
@JvmStatic
|
||||||
val wireguard_conf = buildWireugardConfig(mConfig!!)
|
private external fun wgVersion(): String?
|
||||||
if (currentTunnelHandle != -1) {
|
}
|
||||||
Log.e(tag, "Tunnel already up")
|
}
|
||||||
// Turn the tunnel down because this might be a switch
|
|
||||||
wgTurnOff(currentTunnelHandle)
|
|
||||||
}
|
|
||||||
val wgConfig: String = wireguard_conf!!.toWgUserspaceString()
|
|
||||||
val builder = Builder()
|
|
||||||
setupBuilder(wireguard_conf, builder)
|
|
||||||
builder.setSession("avpn0")
|
|
||||||
builder.establish().use { tun ->
|
|
||||||
if (tun == null)return
|
|
||||||
Log.i(tag, "Go backend " + wgVersion())
|
|
||||||
currentTunnelHandle = wgTurnOn("avpn0", tun.detachFd(), wgConfig)
|
|
||||||
}
|
|
||||||
if (currentTunnelHandle < 0) {
|
|
||||||
Log.e(tag, "Activation Error Code -> $currentTunnelHandle")
|
|
||||||
isUp = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
protect(wgGetSocketV4(currentTunnelHandle))
|
|
||||||
protect(wgGetSocketV6(currentTunnelHandle))
|
|
||||||
isUp = true
|
|
||||||
|
|
||||||
// Store the config in case the service gets
|
|
||||||
// asked boot vpn from the OS
|
|
||||||
val prefs = Prefs.get(this)
|
|
||||||
prefs.edit()
|
|
||||||
.putString("lastConf", mConfig.toString())
|
|
||||||
.apply()
|
|
||||||
|
|
||||||
NotificationUtil.show(this) // Go foreground
|
|
||||||
}
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
fun startService(c: Context) {
|
|
||||||
c.applicationContext.startService(
|
|
||||||
Intent(c.applicationContext, VPNService::class.java).apply {
|
|
||||||
putExtra("startOnly", true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
private external fun wgGetConfig(handle: Int): String?
|
|
||||||
@JvmStatic
|
|
||||||
private external fun wgGetSocketV4(handle: Int): Int
|
|
||||||
@JvmStatic
|
|
||||||
private external fun wgGetSocketV6(handle: Int): Int
|
|
||||||
@JvmStatic
|
|
||||||
private external fun wgTurnOff(handle: Int)
|
|
||||||
@JvmStatic
|
|
||||||
private external fun wgTurnOn(ifName: String, tunFd: Int, settings: String): Int
|
|
||||||
@JvmStatic
|
|
||||||
private external fun wgVersion(): String?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
QT += widgets core gui network xml remoteobjects quick
|
QT += widgets core gui network xml remoteobjects quick svg
|
||||||
|
|
||||||
TARGET = AmneziaVPN
|
TARGET = AmneziaVPN
|
||||||
TEMPLATE = app
|
TEMPLATE = app
|
||||||
|
@ -200,6 +200,7 @@ linux:!android {
|
||||||
}
|
}
|
||||||
|
|
||||||
win32|macx|linux:!android {
|
win32|macx|linux:!android {
|
||||||
|
DEFINES += AMNEZIA_DESKTOP
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
ui/systemtray_notificationhandler.h \
|
ui/systemtray_notificationhandler.h \
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
Settings &VpnConfigurator::m_settings()
|
Settings &VpnConfigurator::m_settings()
|
||||||
{
|
{
|
||||||
|
@ -41,24 +42,59 @@ QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentia
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString VpnConfigurator::processConfigWithLocalSettings(DockerContainer container, Proto proto, QString config)
|
QPair<QString, QString> VpnConfigurator::getDnsForConfig(int serverIndex)
|
||||||
{
|
{
|
||||||
config.replace("$PRIMARY_DNS", m_settings().primaryDns());
|
QPair<QString, QString> dns;
|
||||||
config.replace("$SECONDARY_DNS", m_settings().secondaryDns());
|
|
||||||
|
bool useAmneziaDns = m_settings().useAmneziaDns();
|
||||||
|
const QJsonObject &server = m_settings().server(serverIndex);
|
||||||
|
|
||||||
|
dns.first = server.value(config_key::dns1).toString();
|
||||||
|
dns.second = server.value(config_key::dns2).toString();
|
||||||
|
|
||||||
|
if (dns.first.isEmpty() || !Utils::checkIPv4Format(dns.first)) {
|
||||||
|
if (useAmneziaDns && m_settings().containers(serverIndex).contains(DockerContainer::Dns)) {
|
||||||
|
dns.first = protocols::dns::amneziaDnsIp;
|
||||||
|
}
|
||||||
|
else dns.first = m_settings().primaryDns();
|
||||||
|
}
|
||||||
|
if (dns.second.isEmpty() || !Utils::checkIPv4Format(dns.second)) {
|
||||||
|
dns.second = m_settings().secondaryDns();
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "VpnConfigurator::getDnsForConfig" << dns.first << dns.second;
|
||||||
|
return dns;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString &VpnConfigurator::processConfigWithDnsSettings(int serverIndex, DockerContainer container,
|
||||||
|
Proto proto, QString &config)
|
||||||
|
{
|
||||||
|
auto dns = getDnsForConfig(serverIndex);
|
||||||
|
|
||||||
|
config.replace("$PRIMARY_DNS", dns.first);
|
||||||
|
config.replace("$SECONDARY_DNS", dns.second);
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString &VpnConfigurator::processConfigWithLocalSettings(int serverIndex, DockerContainer container,
|
||||||
|
Proto proto, QString &config)
|
||||||
|
{
|
||||||
|
processConfigWithDnsSettings(serverIndex, container, proto, config);
|
||||||
|
|
||||||
if (proto == Proto::OpenVpn) {
|
if (proto == Proto::OpenVpn) {
|
||||||
return OpenVpnConfigurator::processConfigWithLocalSettings(config);
|
config = OpenVpnConfigurator::processConfigWithLocalSettings(config);
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString VpnConfigurator::processConfigWithExportSettings(DockerContainer container, Proto proto, QString config)
|
QString &VpnConfigurator::processConfigWithExportSettings(int serverIndex, DockerContainer container,
|
||||||
|
Proto proto, QString &config)
|
||||||
{
|
{
|
||||||
config.replace("$PRIMARY_DNS", m_settings().primaryDns());
|
processConfigWithDnsSettings(serverIndex, container, proto, config);
|
||||||
config.replace("$SECONDARY_DNS", m_settings().secondaryDns());
|
|
||||||
|
|
||||||
if (proto == Proto::OpenVpn) {
|
if (proto == Proto::OpenVpn) {
|
||||||
return OpenVpnConfigurator::processConfigWithExportSettings(config);
|
config = OpenVpnConfigurator::processConfigWithExportSettings(config);
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,11 @@ public:
|
||||||
static QString genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container,
|
static QString genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||||
const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode = nullptr);
|
const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode = nullptr);
|
||||||
|
|
||||||
static QString processConfigWithLocalSettings(DockerContainer container, Proto proto, QString config);
|
static QPair<QString, QString> getDnsForConfig(int serverIndex);
|
||||||
static QString processConfigWithExportSettings(DockerContainer container, Proto proto, QString config);
|
static QString &processConfigWithDnsSettings(int serverIndex, DockerContainer container, Proto proto, QString &config);
|
||||||
|
|
||||||
|
static QString &processConfigWithLocalSettings(int serverIndex, DockerContainer container, Proto proto, QString &config);
|
||||||
|
static QString &processConfigWithExportSettings(int serverIndex, DockerContainer container, Proto proto, QString &config);
|
||||||
|
|
||||||
// workaround for containers which is not support normal configaration
|
// workaround for containers which is not support normal configaration
|
||||||
static void updateContainerConfigAfterInstallation(DockerContainer container,
|
static void updateContainerConfigAfterInstallation(DockerContainer container,
|
||||||
|
|
|
@ -149,7 +149,11 @@ bool ContainerProps::isWorkingOnPlatform(DockerContainer c)
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
#elif defined (Q_OS_MAC)
|
#elif defined (Q_OS_MAC)
|
||||||
return false;
|
switch (c) {
|
||||||
|
case DockerContainer::WireGuard: return false;
|
||||||
|
case DockerContainer::Ipsec: return false;
|
||||||
|
default: return true;
|
||||||
|
}
|
||||||
|
|
||||||
#elif defined (Q_OS_ANDROID)
|
#elif defined (Q_OS_ANDROID)
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
|
|
@ -79,6 +79,8 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug().noquote() << "EXEC" << lineToExec;
|
qDebug().noquote() << "EXEC" << lineToExec;
|
||||||
|
Debug::appendSshLog("Run command:" + lineToExec);
|
||||||
|
|
||||||
QSharedPointer<SshRemoteProcess> proc = client->createRemoteProcess(lineToExec.toUtf8());
|
QSharedPointer<SshRemoteProcess> proc = client->createRemoteProcess(lineToExec.toUtf8());
|
||||||
|
|
||||||
if (!proc) {
|
if (!proc) {
|
||||||
|
@ -101,7 +103,9 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
||||||
|
|
||||||
QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardOutput, &wait, [proc, cbReadStdOut](){
|
QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardOutput, &wait, [proc, cbReadStdOut](){
|
||||||
QString s = proc->readAllStandardOutput();
|
QString s = proc->readAllStandardOutput();
|
||||||
|
|
||||||
if (s != "." && !s.isEmpty()) {
|
if (s != "." && !s.isEmpty()) {
|
||||||
|
Debug::appendSshLog("Output: " + s);
|
||||||
qDebug().noquote() << "stdout" << s;
|
qDebug().noquote() << "stdout" << s;
|
||||||
}
|
}
|
||||||
if (cbReadStdOut) cbReadStdOut(s, proc);
|
if (cbReadStdOut) cbReadStdOut(s, proc);
|
||||||
|
@ -110,6 +114,7 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
||||||
QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardError, &wait, [proc, cbReadStdErr](){
|
QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardError, &wait, [proc, cbReadStdErr](){
|
||||||
QString s = proc->readAllStandardError();
|
QString s = proc->readAllStandardError();
|
||||||
if (s != "." && !s.isEmpty()) {
|
if (s != "." && !s.isEmpty()) {
|
||||||
|
Debug::appendSshLog("Output: " + s);
|
||||||
qDebug().noquote() << "stderr" << s;
|
qDebug().noquote() << "stderr" << s;
|
||||||
}
|
}
|
||||||
if (cbReadStdErr) cbReadStdErr(s, proc);
|
if (cbReadStdErr) cbReadStdErr(s, proc);
|
||||||
|
@ -135,6 +140,7 @@ ErrorCode ServerController::runContainerScript(const ServerCredentials &credenti
|
||||||
const std::function<void (const QString &, QSharedPointer<QSsh::SshRemoteProcess>)> &cbReadStdErr)
|
const std::function<void (const QString &, QSharedPointer<QSsh::SshRemoteProcess>)> &cbReadStdErr)
|
||||||
{
|
{
|
||||||
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
|
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
|
||||||
|
Debug::appendSshLog("Run container script for " + ContainerProps::containerToString(container) + ":\n" + script);
|
||||||
|
|
||||||
ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName);
|
ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName);
|
||||||
if (e) return e;
|
if (e) return e;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include "sshconnection.h"
|
#include "sshconnection.h"
|
||||||
#include "sshremoteprocess.h"
|
#include "sshremoteprocess.h"
|
||||||
|
#include "debug.h"
|
||||||
#include "defs.h"
|
#include "defs.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,15 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <core/ipcclient.h>
|
||||||
|
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
QFile Debug::m_file;
|
QFile Debug::m_file;
|
||||||
QTextStream Debug::m_textStream;
|
QTextStream Debug::m_textStream;
|
||||||
QString Debug::m_logFileName;
|
QString Debug::m_logFileName = QString("%1.log").arg(APPLICATION_NAME);
|
||||||
|
|
||||||
void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
|
void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
|
||||||
{
|
{
|
||||||
|
@ -26,10 +28,30 @@ void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, cons
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug::m_textStream << qFormatLogMessage(type, context, msg) << endl << flush;
|
Debug::m_textStream << qFormatLogMessage(type, context, msg) << endl << flush;
|
||||||
|
Debug::appendAllLog(qFormatLogMessage(type, context, msg));
|
||||||
|
|
||||||
std::cout << qFormatLogMessage(type, context, msg).toStdString() << std::endl << std::flush;
|
std::cout << qFormatLogMessage(type, context, msg).toStdString() << std::endl << std::flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Debug &Debug::Instance()
|
||||||
|
{
|
||||||
|
static Debug s;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debug::appendSshLog(const QString &log)
|
||||||
|
{
|
||||||
|
QString dt = QDateTime::currentDateTime().toString();
|
||||||
|
Instance().m_sshLog.append(dt + ": " + log + "\n");
|
||||||
|
emit Instance().sshLogChanged(Instance().sshLog());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debug::appendAllLog(const QString &log)
|
||||||
|
{
|
||||||
|
Instance().m_allLog.append(log + "\n");
|
||||||
|
emit Instance().allLogChanged(Instance().allLog());
|
||||||
|
}
|
||||||
|
|
||||||
bool Debug::init()
|
bool Debug::init()
|
||||||
{
|
{
|
||||||
qSetMessagePattern("%{time yyyy-MM-dd hh:mm:ss} %{type} %{message}");
|
qSetMessagePattern("%{time yyyy-MM-dd hh:mm:ss} %{type} %{message}");
|
||||||
|
@ -40,11 +62,8 @@ bool Debug::init()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_logFileName = QString("%1.log").arg(APPLICATION_NAME);
|
|
||||||
|
|
||||||
|
|
||||||
m_file.setFileName(appDir.filePath(m_logFileName));
|
m_file.setFileName(appDir.filePath(m_logFileName));
|
||||||
if (!m_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
if (!m_file.open(QIODevice::Append)) {
|
||||||
qWarning() << "Cannot open log file:" << m_logFileName;
|
qWarning() << "Cannot open log file:" << m_logFileName;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -63,6 +82,20 @@ QString Debug::userLogsDir()
|
||||||
return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/log";
|
return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/log";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Debug::userLogsFilePath()
|
||||||
|
{
|
||||||
|
return userLogsDir() + QDir::separator() + m_logFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Debug::getLogFile()
|
||||||
|
{
|
||||||
|
m_file.flush();
|
||||||
|
QFile file(userLogsFilePath());
|
||||||
|
|
||||||
|
file.open(QIODevice::ReadOnly);
|
||||||
|
return file.readAll();
|
||||||
|
}
|
||||||
|
|
||||||
bool Debug::openLogsFolder()
|
bool Debug::openLogsFolder()
|
||||||
{
|
{
|
||||||
QString path = userLogsDir();
|
QString path = userLogsDir();
|
||||||
|
@ -88,3 +121,48 @@ QString Debug::appLogFileNamePath()
|
||||||
{
|
{
|
||||||
return m_file.fileName();
|
return m_file.fileName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Debug::clearLogs()
|
||||||
|
{
|
||||||
|
bool isLogActive = m_file.isOpen();
|
||||||
|
m_file.close();
|
||||||
|
|
||||||
|
QFile file(userLogsFilePath());
|
||||||
|
|
||||||
|
file.open(QIODevice::WriteOnly | QIODevice::Truncate);
|
||||||
|
file.resize(0);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
if (isLogActive) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debug::clearServiceLogs()
|
||||||
|
{
|
||||||
|
IpcClient *m_IpcClient = new IpcClient;
|
||||||
|
|
||||||
|
if (!m_IpcClient->isSocketConnected()) {
|
||||||
|
if (!IpcClient::init(m_IpcClient)) {
|
||||||
|
qWarning() << "Error occured when init IPC client";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_IpcClient->Interface()) {
|
||||||
|
m_IpcClient->Interface()->setLogsEnabled(false);
|
||||||
|
m_IpcClient->Interface()->cleanUp();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qWarning() << "Error occured cleaning up service logs";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debug::cleanUp()
|
||||||
|
{
|
||||||
|
clearLogs();
|
||||||
|
QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
|
||||||
|
dir.removeRecursively();
|
||||||
|
|
||||||
|
clearServiceLogs();
|
||||||
|
}
|
||||||
|
|
|
@ -7,15 +7,37 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
|
|
||||||
class Debug
|
#include "ui/property_helper.h"
|
||||||
|
|
||||||
|
class Debug : public QObject
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
AUTO_PROPERTY(QString, sshLog)
|
||||||
|
AUTO_PROPERTY(QString, allLog)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static Debug& Instance();
|
||||||
|
|
||||||
|
static void appendSshLog(const QString &log);
|
||||||
|
static void appendAllLog(const QString &log);
|
||||||
|
|
||||||
|
|
||||||
static bool init();
|
static bool init();
|
||||||
static bool openLogsFolder();
|
static bool openLogsFolder();
|
||||||
static bool openServiceLogsFolder();
|
static bool openServiceLogsFolder();
|
||||||
static QString appLogFileNamePath();
|
static QString appLogFileNamePath();
|
||||||
|
static void clearLogs();
|
||||||
|
static void clearServiceLogs();
|
||||||
|
static void cleanUp();
|
||||||
|
|
||||||
|
static QString userLogsFilePath();
|
||||||
|
static QString getLogFile();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Debug() {}
|
||||||
|
Debug(Debug const &) = delete;
|
||||||
|
Debug& operator= (Debug const&) = delete;
|
||||||
|
|
||||||
static QString userLogsDir();
|
static QString userLogsDir();
|
||||||
|
|
||||||
static QFile m_file;
|
static QFile m_file;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#define APPLICATION_NAME "AmneziaVPN"
|
#define APPLICATION_NAME "AmneziaVPN"
|
||||||
#define SERVICE_NAME "AmneziaVPN-service"
|
#define SERVICE_NAME "AmneziaVPN-service"
|
||||||
#define ORGANIZATION_NAME "AmneziaVPN.ORG"
|
#define ORGANIZATION_NAME "AmneziaVPN.ORG"
|
||||||
#define APP_MAJOR_VERSION "2.0.7"
|
#define APP_MAJOR_VERSION "2.0.8"
|
||||||
#define APP_VERSION "2.0.7.0"
|
#define APP_VERSION "2.0.8.0"
|
||||||
|
|
||||||
#endif // DEFINES_H
|
#endif // DEFINES_H
|
||||||
|
|
Before Width: | Height: | Size: 324 B |
Before Width: | Height: | Size: 690 B |
Before Width: | Height: | Size: 454 B |
1
client/images/svg/close_black_24dp.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/></svg>
|
After Width: | Height: | Size: 268 B |
1
client/images/svg/control_point_black_24dp.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.49 2 2 6.49 2 12s4.49 10 10 10 10-4.49 10-10S17.51 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></svg>
|
After Width: | Height: | Size: 317 B |
1
client/images/svg/delete_black_24dp.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M16 9v10H8V9h8m-1.5-6h-5l-1 1H5v2h14V4h-3.5l-1-1zM18 7H6v12c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7z"/></svg>
|
After Width: | Height: | Size: 252 B |
1
client/images/svg/density_small_black_24dp.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><g><rect height="2" width="18" x="3" y="2"/><rect height="2" width="18" x="3" y="20"/><rect height="2" width="18" x="3" y="14"/><rect height="2" width="18" x="3" y="8"/></g></g></svg>
|
After Width: | Height: | Size: 371 B |
1
client/images/svg/done_black_24dp.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/></svg>
|
After Width: | Height: | Size: 209 B |
1
client/images/svg/format_list_bulleted_black_24dp.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z"/></svg>
|
After Width: | Height: | Size: 430 B |
1
client/images/svg/gpp_good_black_24dp.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><path d="M0,0h24v24H0V0z" fill="none"/></g><g><path d="M12,2L4,5v6.09c0,5.05,3.41,9.76,8,10.91c4.59-1.15,8-5.86,8-10.91V5L12,2z M18,11.09c0,4-2.55,7.7-6,8.83 c-3.45-1.13-6-4.82-6-8.83V6.31l6-2.12l6,2.12V11.09z M8.82,10.59L7.4,12l3.54,3.54l5.66-5.66l-1.41-1.41l-4.24,4.24L8.82,10.59z"/></g></svg>
|
After Width: | Height: | Size: 434 B |
1
client/images/svg/gpp_maybe_black_24dp.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><path d="M0,0h24v24H0V0z" fill="none"/></g><g><g><path d="M12,2L4,5v6.09c0,5.05,3.41,9.76,8,10.91c4.59-1.15,8-5.86,8-10.91V5L12,2z M18,11.09c0,4-2.55,7.7-6,8.83 c-3.45-1.13-6-4.82-6-8.83v-4.7l6-2.25l6,2.25V11.09z"/><rect height="2" width="2" x="11" y="14"/><rect height="5" width="2" x="11" y="7"/></g></g></svg>
|
After Width: | Height: | Size: 451 B |
1
client/images/svg/logout_black_24dp.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><path d="M0,0h24v24H0V0z" fill="none"/></g><g><path d="M17,8l-1.41,1.41L17.17,11H9v2h8.17l-1.58,1.58L17,16l4-4L17,8z M5,5h7V3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h7v-2H5V5z"/></g></svg>
|
After Width: | Height: | Size: 324 B |
1
client/images/svg/miscellaneous_services_black_24dp.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><g><path d="M14.17,13.71l1.4-2.42c0.09-0.15,0.05-0.34-0.08-0.45l-1.48-1.16c0.03-0.22,0.05-0.45,0.05-0.68s-0.02-0.46-0.05-0.69 l1.48-1.16c0.13-0.11,0.17-0.3,0.08-0.45l-1.4-2.42c-0.09-0.15-0.27-0.21-0.43-0.15L12,4.83c-0.36-0.28-0.75-0.51-1.18-0.69 l-0.26-1.85C10.53,2.13,10.38,2,10.21,2h-2.8C7.24,2,7.09,2.13,7.06,2.3L6.8,4.15C6.38,4.33,5.98,4.56,5.62,4.84l-1.74-0.7 c-0.16-0.06-0.34,0-0.43,0.15l-1.4,2.42C1.96,6.86,2,7.05,2.13,7.16l1.48,1.16C3.58,8.54,3.56,8.77,3.56,9s0.02,0.46,0.05,0.69 l-1.48,1.16C2,10.96,1.96,11.15,2.05,11.3l1.4,2.42c0.09,0.15,0.27,0.21,0.43,0.15l1.74-0.7c0.36,0.28,0.75,0.51,1.18,0.69 l0.26,1.85C7.09,15.87,7.24,16,7.41,16h2.8c0.17,0,0.32-0.13,0.35-0.3l0.26-1.85c0.42-0.18,0.82-0.41,1.18-0.69l1.74,0.7 C13.9,13.92,14.08,13.86,14.17,13.71z M8.81,11c-1.1,0-2-0.9-2-2c0-1.1,0.9-2,2-2s2,0.9,2,2C10.81,10.1,9.91,11,8.81,11z"/><path d="M21.92,18.67l-0.96-0.74c0.02-0.14,0.04-0.29,0.04-0.44c0-0.15-0.01-0.3-0.04-0.44l0.95-0.74 c0.08-0.07,0.11-0.19,0.05-0.29l-0.9-1.55c-0.05-0.1-0.17-0.13-0.28-0.1l-1.11,0.45c-0.23-0.18-0.48-0.33-0.76-0.44l-0.17-1.18 C18.73,13.08,18.63,13,18.53,13h-1.79c-0.11,0-0.21,0.08-0.22,0.19l-0.17,1.18c-0.27,0.12-0.53,0.26-0.76,0.44l-1.11-0.45 c-0.1-0.04-0.22,0-0.28,0.1l-0.9,1.55c-0.05,0.1-0.04,0.22,0.05,0.29l0.95,0.74c-0.02,0.14-0.03,0.29-0.03,0.44 c0,0.15,0.01,0.3,0.03,0.44l-0.95,0.74c-0.08,0.07-0.11,0.19-0.05,0.29l0.9,1.55c0.05,0.1,0.17,0.13,0.28,0.1l1.11-0.45 c0.23,0.18,0.48,0.33,0.76,0.44l0.17,1.18c0.02,0.11,0.11,0.19,0.22,0.19h1.79c0.11,0,0.21-0.08,0.22-0.19l0.17-1.18 c0.27-0.12,0.53-0.26,0.75-0.44l1.12,0.45c0.1,0.04,0.22,0,0.28-0.1l0.9-1.55C22.03,18.86,22,18.74,21.92,18.67z M17.63,18.83 c-0.74,0-1.35-0.6-1.35-1.35s0.6-1.35,1.35-1.35s1.35,0.6,1.35,1.35S18.37,18.83,17.63,18.83z"/></g></g></svg>
|
After Width: | Height: | Size: 1.9 KiB |
1
client/images/svg/refresh_black_24dp.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
|
After Width: | Height: | Size: 361 B |
1
client/images/svg/settings_black_24dp.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M19.43 12.98c.04-.32.07-.64.07-.98 0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.09-.16-.26-.25-.44-.25-.06 0-.12.01-.17.03l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.06-.02-.12-.03-.18-.03-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98 0 .33.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.09.16.26.25.44.25.06 0 .12-.01.17-.03l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.06.02.12.03.18.03.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zm-1.98-1.71c.04.31.05.52.05.73 0 .21-.02.43-.05.73l-.14 1.13.89.7 1.08.84-.7 1.21-1.27-.51-1.04-.42-.9.68c-.43.32-.84.56-1.25.73l-1.06.43-.16 1.13-.2 1.35h-1.4l-.19-1.35-.16-1.13-1.06-.43c-.43-.18-.83-.41-1.23-.71l-.91-.7-1.06.43-1.27.51-.7-1.21 1.08-.84.89-.7-.14-1.13c-.03-.31-.05-.54-.05-.74s.02-.43.05-.73l.14-1.13-.89-.7-1.08-.84.7-1.21 1.27.51 1.04.42.9-.68c.43-.32.84-.56 1.25-.73l1.06-.43.16-1.13.2-1.35h1.39l.19 1.35.16 1.13 1.06.43c.43.18.83.41 1.23.71l.91.7 1.06-.43 1.27-.51.7 1.21-1.07.85-.89.7.14 1.13zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></svg>
|
After Width: | Height: | Size: 1.4 KiB |
1
client/images/svg/settings_suggest_black_24dp.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><path d="M10,13c0.55,0,1,0.45,1,1s-0.45,1-1,1s-1-0.45-1-1S9.45,13,10,13 M10,11c-1.66,0-3,1.34-3,3s1.34,3,3,3s3-1.34,3-3 S11.66,11,10,11L10,11z M18.5,9l1.09-2.41L22,5.5l-2.41-1.09L18.5,2l-1.09,2.41L15,5.5l2.41,1.09L18.5,9z M21.28,12.72L20.5,11 l-0.78,1.72L18,13.5l1.72,0.78L20.5,16l0.78-1.72L23,13.5L21.28,12.72z M16.25,14c0-0.12,0-0.25-0.01-0.37l1.94-1.47l-2.5-4.33 l-2.24,0.94c-0.2-0.13-0.42-0.26-0.64-0.37L12.5,6h-5L7.2,8.41C6.98,8.52,6.77,8.65,6.56,8.78L4.32,7.83l-2.5,4.33l1.94,1.47 C3.75,13.75,3.75,13.88,3.75,14s0,0.25,0.01,0.37l-1.94,1.47l2.5,4.33l2.24-0.94c0.2,0.13,0.42,0.26,0.64,0.37L7.5,22h5l0.3-2.41 c0.22-0.11,0.43-0.23,0.64-0.37l2.24,0.94l2.5-4.33l-1.94-1.47C16.25,14.25,16.25,14.12,16.25,14z M14.83,17.64l-1.73-0.73 c-0.56,0.6-1.3,1.04-2.13,1.23L10.73,20H9.27l-0.23-1.86c-0.83-0.19-1.57-0.63-2.13-1.23l-1.73,0.73l-0.73-1.27l1.49-1.13 c-0.12-0.39-0.18-0.8-0.18-1.23c0-0.43,0.06-0.84,0.18-1.23l-1.49-1.13l0.73-1.27l1.73,0.73c0.56-0.6,1.3-1.04,2.13-1.23L9.27,8 h1.47l0.23,1.86c0.83,0.19,1.57,0.63,2.13,1.23l1.73-0.73l0.73,1.27l-1.49,1.13c0.12,0.39,0.18,0.8,0.18,1.23 c0,0.43-0.06,0.84-0.18,1.23l1.49,1.13L14.83,17.64z"/></svg>
|
After Width: | Height: | Size: 1.3 KiB |
1
client/images/svg/share_black_24dp.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92s2.92-1.31 2.92-2.92c0-1.61-1.31-2.92-2.92-2.92zM18 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM6 13c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm12 7.02c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z"/></svg>
|
After Width: | Height: | Size: 679 B |
1
client/images/svg/vpn_key_black_24dp.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M22 19h-6v-4h-2.68c-1.14 2.42-3.6 4-6.32 4-3.86 0-7-3.14-7-7s3.14-7 7-7c2.72 0 5.17 1.58 6.32 4H24v6h-2v4zm-4-2h2v-4h2v-2H11.94l-.23-.67C11.01 8.34 9.11 7 7 7c-2.76 0-5 2.24-5 5s2.24 5 5 5c2.11 0 4.01-1.34 4.71-3.33l.23-.67H18v4zM7 15c-1.65 0-3-1.35-3-3s1.35-3 3-3 3 1.35 3 3-1.35 3-3 3zm0-4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1z"/></svg>
|
After Width: | Height: | Size: 498 B |
|
@ -86,15 +86,15 @@ int main(int argc, char *argv[])
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID)
|
|
||||||
NativeHelpers::registerApplicationInstance(&app);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
AllowSetForegroundWindow(0);
|
AllowSetForegroundWindow(0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(Q_OS_ANDROID)
|
||||||
|
NativeHelpers::registerApplicationInstance(&app);
|
||||||
|
#endif
|
||||||
|
|
||||||
loadTranslator();
|
loadTranslator();
|
||||||
|
|
||||||
QFontDatabase::addApplicationFont(":/fonts/Lato-Black.ttf");
|
QFontDatabase::addApplicationFont(":/fonts/Lato-Black.ttf");
|
||||||
|
@ -120,10 +120,26 @@ int main(int argc, char *argv[])
|
||||||
QCommandLineOption c_autostart {{"a", "autostart"}, "System autostart"};
|
QCommandLineOption c_autostart {{"a", "autostart"}, "System autostart"};
|
||||||
parser.addOption(c_autostart);
|
parser.addOption(c_autostart);
|
||||||
|
|
||||||
|
QCommandLineOption c_cleanup {{"c", "cleanup"}, "Cleanup logs"};
|
||||||
|
parser.addOption(c_cleanup);
|
||||||
|
|
||||||
parser.process(app);
|
parser.process(app);
|
||||||
|
|
||||||
if (!Debug::init()) {
|
if (parser.isSet(c_cleanup)) {
|
||||||
qWarning() << "Initialization of debug subsystem failed";
|
Debug::cleanUp();
|
||||||
|
QTimer::singleShot(100,[&app]{
|
||||||
|
app.quit();
|
||||||
|
});
|
||||||
|
app.exec();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings settings;
|
||||||
|
|
||||||
|
if (settings.isSaveLogs()) {
|
||||||
|
if (!Debug::init()) {
|
||||||
|
qWarning() << "Initialization of debug subsystem failed";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app.setQuitOnLastWindowClosed(false);
|
app.setQuitOnLastWindowClosed(false);
|
||||||
|
@ -165,6 +181,8 @@ int main(int argc, char *argv[])
|
||||||
QCoreApplication::exit(-1);
|
QCoreApplication::exit(-1);
|
||||||
}, Qt::QueuedConnection);
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
engine->rootContext()->setContextProperty("Debug", &Debug::Instance());
|
||||||
|
|
||||||
engine->rootContext()->setContextProperty("UiLogic", uiLogic);
|
engine->rootContext()->setContextProperty("UiLogic", uiLogic);
|
||||||
|
|
||||||
engine->rootContext()->setContextProperty("AppSettingsLogic", uiLogic->appSettingsLogic());
|
engine->rootContext()->setContextProperty("AppSettingsLogic", uiLogic->appSettingsLogic());
|
||||||
|
@ -196,26 +214,23 @@ int main(int argc, char *argv[])
|
||||||
uiLogic->setQmlRoot(engine->rootObjects().at(0));
|
uiLogic->setQmlRoot(engine->rootObjects().at(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
if (parser.isSet("a")) uiLogic->showOnStartup();
|
||||||
|
else emit uiLogic->show();
|
||||||
|
#else
|
||||||
|
uiLogic->showOnStartup();
|
||||||
|
#endif
|
||||||
|
|
||||||
// TODO - fix
|
// TODO - fix
|
||||||
//#ifdef Q_OS_WIN
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
// if (parser.isSet("a")) mainWindow.showOnStartup();
|
if (app.isPrimary()) {
|
||||||
// else mainWindow.show();
|
QObject::connect(&app, &SingleApplication::instanceStarted, uiLogic, [&](){
|
||||||
//#else
|
qDebug() << "Secondary instance started, showing this window instead";
|
||||||
// mainWindow.showOnStartup();
|
emit uiLogic->show();
|
||||||
//#endif
|
emit uiLogic->raise();
|
||||||
|
});
|
||||||
|
}
|
||||||
// TODO - fix
|
#endif
|
||||||
//#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
|
||||||
// if (app.isPrimary()) {
|
|
||||||
// QObject::connect(&app, &SingleApplication::instanceStarted, &mainWindow, [&](){
|
|
||||||
// qDebug() << "Secondary instance started, showing this window instead";
|
|
||||||
// mainWindow.show();
|
|
||||||
// mainWindow.showNormal();
|
|
||||||
// mainWindow.raise();
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//#endif
|
|
||||||
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,8 +159,8 @@ ErrorCode OpenVpnProtocol::start()
|
||||||
return lastError();
|
return lastError();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString vpnLogFileNamePath = Utils::systemLogPath() + "/openvpn.log";
|
// QString vpnLogFileNamePath = Utils::systemLogPath() + "/openvpn.log";
|
||||||
Utils::createEmptyFile(vpnLogFileNamePath);
|
// Utils::createEmptyFile(vpnLogFileNamePath);
|
||||||
|
|
||||||
if (!m_managementServer.start(m_managementHost, m_managementPort)) {
|
if (!m_managementServer.start(m_managementHost, m_managementPort)) {
|
||||||
setLastError(ErrorCode::OpenVpnManagementServerError);
|
setLastError(ErrorCode::OpenVpnManagementServerError);
|
||||||
|
@ -186,8 +186,7 @@ ErrorCode OpenVpnProtocol::start()
|
||||||
m_openVpnProcess->setProgram(openVpnExecPath());
|
m_openVpnProcess->setProgram(openVpnExecPath());
|
||||||
QStringList arguments({"--config" , configPath(),
|
QStringList arguments({"--config" , configPath(),
|
||||||
"--management", m_managementHost, QString::number(m_managementPort),
|
"--management", m_managementHost, QString::number(m_managementPort),
|
||||||
"--management-client",
|
"--management-client"/*, "--log", vpnLogFileNamePath */
|
||||||
"--log", vpnLogFileNamePath
|
|
||||||
});
|
});
|
||||||
m_openVpnProcess->setArguments(arguments);
|
m_openVpnProcess->setArguments(arguments);
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,10 @@ constexpr char password[] = "password";
|
||||||
constexpr char port[] = "port";
|
constexpr char port[] = "port";
|
||||||
constexpr char local_port[] = "local_port";
|
constexpr char local_port[] = "local_port";
|
||||||
|
|
||||||
|
constexpr char dns1[] = "dns1";
|
||||||
|
constexpr char dns2[] = "dns2";
|
||||||
|
|
||||||
|
|
||||||
constexpr char description[] = "description";
|
constexpr char description[] = "description";
|
||||||
constexpr char cert[] = "cert";
|
constexpr char cert[] = "cert";
|
||||||
constexpr char config[] = "config";
|
constexpr char config[] = "config";
|
||||||
|
@ -55,6 +59,9 @@ constexpr char last_config[] = "last_config";
|
||||||
|
|
||||||
namespace protocols {
|
namespace protocols {
|
||||||
|
|
||||||
|
namespace dns {
|
||||||
|
constexpr char amneziaDnsIp[] = "172.29.172.254";
|
||||||
|
}
|
||||||
|
|
||||||
namespace openvpn {
|
namespace openvpn {
|
||||||
constexpr char defaultSubnetAddress[] = "10.8.0.0";
|
constexpr char defaultSubnetAddress[] = "10.8.0.0";
|
||||||
|
|
|
@ -103,12 +103,14 @@ QString VpnProtocol::vpnGateway() const
|
||||||
VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject& configuration)
|
VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject& configuration)
|
||||||
{
|
{
|
||||||
switch (container) {
|
switch (container) {
|
||||||
|
#if defined(Q_OS_WINDOWS)
|
||||||
|
case DockerContainer::Ipsec: return new Ikev2Protocol(configuration);
|
||||||
|
#endif
|
||||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||||
case DockerContainer::OpenVpn: return new OpenVpnProtocol(configuration);
|
case DockerContainer::OpenVpn: return new OpenVpnProtocol(configuration);
|
||||||
case DockerContainer::Cloak: return new OpenVpnOverCloakProtocol(configuration);
|
case DockerContainer::Cloak: return new OpenVpnOverCloakProtocol(configuration);
|
||||||
case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration);
|
case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration);
|
||||||
case DockerContainer::WireGuard: return new WireguardProtocol(configuration);
|
case DockerContainer::WireGuard: return new WireguardProtocol(configuration);
|
||||||
case DockerContainer::Ipsec: return new Ikev2Protocol(configuration);
|
|
||||||
#endif
|
#endif
|
||||||
default: return nullptr;
|
default: return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,11 +27,9 @@
|
||||||
<file>fonts/Lato-Thin.ttf</file>
|
<file>fonts/Lato-Thin.ttf</file>
|
||||||
<file>fonts/Lato-ThinItalic.ttf</file>
|
<file>fonts/Lato-ThinItalic.ttf</file>
|
||||||
<file>images/AmneziaVPN.png</file>
|
<file>images/AmneziaVPN.png</file>
|
||||||
<file>images/server_settings.png</file>
|
|
||||||
<file>images/share.png</file>
|
<file>images/share.png</file>
|
||||||
<file>server_scripts/remove_container.sh</file>
|
<file>server_scripts/remove_container.sh</file>
|
||||||
<file>server_scripts/setup_host_firewall.sh</file>
|
<file>server_scripts/setup_host_firewall.sh</file>
|
||||||
<file>images/reload.png</file>
|
|
||||||
<file>server_scripts/openvpn_cloak/Dockerfile</file>
|
<file>server_scripts/openvpn_cloak/Dockerfile</file>
|
||||||
<file>server_scripts/openvpn_cloak/configure_container.sh</file>
|
<file>server_scripts/openvpn_cloak/configure_container.sh</file>
|
||||||
<file>server_scripts/openvpn_cloak/start.sh</file>
|
<file>server_scripts/openvpn_cloak/start.sh</file>
|
||||||
|
@ -42,7 +40,6 @@
|
||||||
<file>images/check.png</file>
|
<file>images/check.png</file>
|
||||||
<file>images/uncheck.png</file>
|
<file>images/uncheck.png</file>
|
||||||
<file>images/settings_grey.png</file>
|
<file>images/settings_grey.png</file>
|
||||||
<file>images/plus.png</file>
|
|
||||||
<file>server_scripts/check_connection.sh</file>
|
<file>server_scripts/check_connection.sh</file>
|
||||||
<file>server_scripts/remove_all_containers.sh</file>
|
<file>server_scripts/remove_all_containers.sh</file>
|
||||||
<file>server_scripts/openvpn_cloak/run_container.sh</file>
|
<file>server_scripts/openvpn_cloak/run_container.sh</file>
|
||||||
|
@ -142,5 +139,23 @@
|
||||||
<file>images/connected.png</file>
|
<file>images/connected.png</file>
|
||||||
<file>images/disconnected.png</file>
|
<file>images/disconnected.png</file>
|
||||||
<file>ui/qml/Pages/PageQrDecoder.qml</file>
|
<file>ui/qml/Pages/PageQrDecoder.qml</file>
|
||||||
|
<file>ui/qml/Pages/PageAbout.qml</file>
|
||||||
|
<file>ui/qml/Controls/RichLabelType.qml</file>
|
||||||
|
<file>images/svg/gpp_good_black_24dp.svg</file>
|
||||||
|
<file>ui/qml/Controls/SvgImageType.qml</file>
|
||||||
|
<file>images/svg/gpp_maybe_black_24dp.svg</file>
|
||||||
|
<file>images/svg/close_black_24dp.svg</file>
|
||||||
|
<file>images/svg/delete_black_24dp.svg</file>
|
||||||
|
<file>images/svg/done_black_24dp.svg</file>
|
||||||
|
<file>images/svg/format_list_bulleted_black_24dp.svg</file>
|
||||||
|
<file>images/svg/logout_black_24dp.svg</file>
|
||||||
|
<file>images/svg/miscellaneous_services_black_24dp.svg</file>
|
||||||
|
<file>images/svg/refresh_black_24dp.svg</file>
|
||||||
|
<file>images/svg/settings_black_24dp.svg</file>
|
||||||
|
<file>images/svg/share_black_24dp.svg</file>
|
||||||
|
<file>images/svg/vpn_key_black_24dp.svg</file>
|
||||||
|
<file>images/svg/control_point_black_24dp.svg</file>
|
||||||
|
<file>images/svg/settings_suggest_black_24dp.svg</file>
|
||||||
|
<file>ui/qml/Controls/SvgButtonType.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
# Run container
|
# Run container
|
||||||
sudo docker run -d --restart always --cap-add=NET_ADMIN -p 53:53/udp -p 53:53/tcp --name $CONTAINER_NAME $CONTAINER_NAME
|
sudo docker run -d --restart always --network amnezia-dns-net --ip=172.29.172.254 --name $CONTAINER_NAME $CONTAINER_NAME
|
||||||
|
|
|
@ -5,3 +5,4 @@ sudo docker run \
|
||||||
-d --privileged \
|
-d --privileged \
|
||||||
--name $CONTAINER_NAME $CONTAINER_NAME
|
--name $CONTAINER_NAME $CONTAINER_NAME
|
||||||
|
|
||||||
|
sudo docker network connect amnezia-dns-net $CONTAINER_NAME
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
# Run container
|
# Run container
|
||||||
sudo docker run -d --restart always --cap-add=NET_ADMIN -p $OPENVPN_PORT:$OPENVPN_PORT/$OPENVPN_TRANSPORT_PROTO --name $CONTAINER_NAME $CONTAINER_NAME
|
sudo docker run \
|
||||||
|
-d --restart always \
|
||||||
|
--cap-add=NET_ADMIN \
|
||||||
|
-p $OPENVPN_PORT:$OPENVPN_PORT/$OPENVPN_TRANSPORT_PROTO \
|
||||||
|
--name $CONTAINER_NAME $CONTAINER_NAME
|
||||||
|
|
||||||
|
sudo docker network connect amnezia-dns-net $CONTAINER_NAME
|
||||||
|
|
||||||
# Create tun device if not exist
|
# Create tun device if not exist
|
||||||
sudo docker exec -i $CONTAINER_NAME bash -c 'mkdir -p /dev/net; if [ ! -c /dev/net/tun ]; then mknod /dev/net/tun c 10 200; fi'
|
sudo docker exec -i $CONTAINER_NAME bash -c 'mkdir -p /dev/net; if [ ! -c /dev/net/tun ]; then mknod /dev/net/tun c 10 200; fi'
|
||||||
|
|
|
@ -14,9 +14,12 @@ iptables -A OUTPUT -o tun0 -j ACCEPT
|
||||||
|
|
||||||
# Allow forwarding traffic only from the VPN.
|
# Allow forwarding traffic only from the VPN.
|
||||||
iptables -A FORWARD -i tun0 -o eth0 -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -j ACCEPT
|
iptables -A FORWARD -i tun0 -o eth0 -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -j ACCEPT
|
||||||
|
iptables -A FORWARD -i tun0 -o eth1 -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -j ACCEPT
|
||||||
|
|
||||||
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
|
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||||
|
|
||||||
iptables -t nat -A POSTROUTING -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -o eth0 -j MASQUERADE
|
iptables -t nat -A POSTROUTING -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -o eth0 -j MASQUERADE
|
||||||
|
iptables -t nat -A POSTROUTING -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -o eth1 -j MASQUERADE
|
||||||
|
|
||||||
# kill daemons in case of restart
|
# kill daemons in case of restart
|
||||||
killall -KILL openvpn
|
killall -KILL openvpn
|
||||||
|
|
|
@ -2,6 +2,10 @@ FROM alpine:latest
|
||||||
|
|
||||||
LABEL maintainer="AmneziaVPN"
|
LABEL maintainer="AmneziaVPN"
|
||||||
|
|
||||||
|
ARG SS_RELEASE="v1.13.1"
|
||||||
|
ARG CLOAK_RELEASE="v2.5.5"
|
||||||
|
ARG SERVER_ARCH
|
||||||
|
|
||||||
#Install required packages
|
#Install required packages
|
||||||
RUN apk add --no-cache curl openvpn easy-rsa bash netcat-openbsd dumb-init rng-tools
|
RUN apk add --no-cache curl openvpn easy-rsa bash netcat-openbsd dumb-init rng-tools
|
||||||
RUN apk --update upgrade --no-cache
|
RUN apk --update upgrade --no-cache
|
||||||
|
@ -13,10 +17,16 @@ RUN mkdir -p /opt/amnezia
|
||||||
RUN echo -e "#!/bin/bash\ntail -f /dev/null" > /opt/amnezia/start.sh
|
RUN echo -e "#!/bin/bash\ntail -f /dev/null" > /opt/amnezia/start.sh
|
||||||
RUN chmod a+x /opt/amnezia/start.sh
|
RUN chmod a+x /opt/amnezia/start.sh
|
||||||
|
|
||||||
RUN curl -L https://github.com/cbeuw/Cloak/releases/download/v2.5.3/ck-server-linux-amd64-v2.5.3 > /usr/bin/ck-server
|
RUN if [ $SERVER_ARCH="x86_64" ]; then CK_ARCH="amd64"; \
|
||||||
|
elif [ $SERVER_ARCH="i686" ]; then CK_ARCH="386"; \
|
||||||
|
elif [ $SERVER_ARCH="aarch64" ]; then CK_ARCH="arm64"; \
|
||||||
|
elif [ $SERVER_ARCH="arm" ]; then CK_ARCH="arm"; \
|
||||||
|
else exit -1; fi && \
|
||||||
|
curl -L https://github.com/cbeuw/Cloak/releases/download/${CLOAK_RELEASE}/ck-server-linux-${CK_ARCH}-${CLOAK_RELEASE} > /usr/bin/ck-server
|
||||||
RUN chmod a+x /usr/bin/ck-server
|
RUN chmod a+x /usr/bin/ck-server
|
||||||
|
|
||||||
RUN curl -L https://github.com/shadowsocks/shadowsocks-rust/releases/download/v1.10.9/shadowsocks-v1.10.9.x86_64-unknown-linux-musl.tar.xz > /usr/bin/ss.tar.xz
|
RUN curl -L https://github.com/shadowsocks/shadowsocks-rust/releases/download/${SS_RELEASE}/shadowsocks-${SS_RELEASE}.${SERVER_ARCH}-unknown-linux-musl.tar.xz > /usr/bin/ss.tar.xz
|
||||||
|
|
||||||
RUN tar -Jxvf /usr/bin/ss.tar.xz -C /usr/bin/
|
RUN tar -Jxvf /usr/bin/ss.tar.xz -C /usr/bin/
|
||||||
RUN chmod a+x /usr/bin/ssserver
|
RUN chmod a+x /usr/bin/ssserver
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
# Run container
|
# Run container
|
||||||
sudo docker run -d --restart always --cap-add=NET_ADMIN -p $CLOAK_SERVER_PORT:443/tcp --name $CONTAINER_NAME $CONTAINER_NAME
|
sudo docker run \
|
||||||
|
-d --restart always \
|
||||||
|
--cap-add=NET_ADMIN \
|
||||||
|
-p $CLOAK_SERVER_PORT:443/tcp \
|
||||||
|
--name $CONTAINER_NAME $CONTAINER_NAME
|
||||||
|
|
||||||
|
sudo docker network connect amnezia-dns-net $CONTAINER_NAME
|
||||||
|
|
||||||
# Create tun device if not exist
|
# Create tun device if not exist
|
||||||
sudo docker exec -i $CONTAINER_NAME bash -c 'mkdir -p /dev/net; if [ ! -c /dev/net/tun ]; then mknod /dev/net/tun c 10 200; fi'
|
sudo docker exec -i $CONTAINER_NAME bash -c 'mkdir -p /dev/net; if [ ! -c /dev/net/tun ]; then mknod /dev/net/tun c 10 200; fi'
|
||||||
|
|
|
@ -14,9 +14,12 @@ iptables -A OUTPUT -o tun0 -j ACCEPT
|
||||||
|
|
||||||
# Allow forwarding traffic only from the VPN.
|
# Allow forwarding traffic only from the VPN.
|
||||||
iptables -A FORWARD -i tun0 -o eth0 -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -j ACCEPT
|
iptables -A FORWARD -i tun0 -o eth0 -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -j ACCEPT
|
||||||
|
iptables -A FORWARD -i tun0 -o eth1 -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -j ACCEPT
|
||||||
|
|
||||||
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
|
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||||
|
|
||||||
iptables -t nat -A POSTROUTING -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -o eth0 -j MASQUERADE
|
iptables -t nat -A POSTROUTING -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -o eth0 -j MASQUERADE
|
||||||
|
iptables -t nat -A POSTROUTING -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -o eth1 -j MASQUERADE
|
||||||
|
|
||||||
# kill daemons in case of restart
|
# kill daemons in case of restart
|
||||||
killall -KILL openvpn
|
killall -KILL openvpn
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
LABEL maintainer="AmneziaVPN"
|
LABEL maintainer="AmneziaVPN"
|
||||||
|
|
||||||
ARG SS_RELEASE="v1.11.2"
|
ARG SS_RELEASE="v1.13.1"
|
||||||
ARG SERVER_ARCH
|
ARG SERVER_ARCH
|
||||||
|
|
||||||
#Install required packages
|
#Install required packages
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
# Run container
|
# Run container
|
||||||
sudo docker run -d --restart always --cap-add=NET_ADMIN -p $SHADOWSOCKS_SERVER_PORT:$SHADOWSOCKS_SERVER_PORT/tcp --name $CONTAINER_NAME $CONTAINER_NAME
|
sudo docker run \
|
||||||
|
-d --restart always \
|
||||||
|
--cap-add=NET_ADMIN \
|
||||||
|
-p $SHADOWSOCKS_SERVER_PORT:$SHADOWSOCKS_SERVER_PORT/tcp \
|
||||||
|
--name $CONTAINER_NAME $CONTAINER_NAME
|
||||||
|
|
||||||
|
sudo docker network connect amnezia-dns-net $CONTAINER_NAME
|
||||||
|
|
||||||
# Create tun device if not exist
|
# Create tun device if not exist
|
||||||
sudo docker exec -i $CONTAINER_NAME bash -c 'mkdir -p /dev/net; if [ ! -c /dev/net/tun ]; then mknod /dev/net/tun c 10 200; fi'
|
sudo docker exec -i $CONTAINER_NAME bash -c 'mkdir -p /dev/net; if [ ! -c /dev/net/tun ]; then mknod /dev/net/tun c 10 200; fi'
|
||||||
|
|
|
@ -14,9 +14,12 @@ iptables -A OUTPUT -o tun0 -j ACCEPT
|
||||||
|
|
||||||
# Allow forwarding traffic only from the VPN.
|
# Allow forwarding traffic only from the VPN.
|
||||||
iptables -A FORWARD -i tun0 -o eth0 -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -j ACCEPT
|
iptables -A FORWARD -i tun0 -o eth0 -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -j ACCEPT
|
||||||
|
iptables -A FORWARD -i tun0 -o eth1 -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -j ACCEPT
|
||||||
|
|
||||||
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
|
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||||
|
|
||||||
iptables -t nat -A POSTROUTING -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -o eth0 -j MASQUERADE
|
iptables -t nat -A POSTROUTING -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -o eth0 -j MASQUERADE
|
||||||
|
iptables -t nat -A POSTROUTING -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -o eth1 -j MASQUERADE
|
||||||
|
|
||||||
# kill daemons in case of restart
|
# kill daemons in case of restart
|
||||||
killall -KILL openvpn
|
killall -KILL openvpn
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
CUR_USER=$(whoami);\
|
CUR_USER=$(whoami);\
|
||||||
sudo mkdir -p $DOCKERFILE_FOLDER;\
|
sudo mkdir -p $DOCKERFILE_FOLDER;\
|
||||||
sudo chown $CUR_USER $DOCKERFILE_FOLDER
|
sudo chown $CUR_USER $DOCKERFILE_FOLDER
|
||||||
|
if ! sudo docker network ls | grep -q amnezia-dns-net; then sudo docker network create --driver bridge --subnet=172.29.172.0/24 --opt com.docker.network.bridge.name=amn0 amnezia-dns-net; fi
|
||||||
|
|
|
@ -10,6 +10,8 @@ sudo docker run -d \
|
||||||
--name $CONTAINER_NAME \
|
--name $CONTAINER_NAME \
|
||||||
$CONTAINER_NAME
|
$CONTAINER_NAME
|
||||||
|
|
||||||
|
sudo docker network connect amnezia-dns-net $CONTAINER_NAME
|
||||||
|
|
||||||
# Prevent to route packets outside of the container in case if server behind of the NAT
|
# Prevent to route packets outside of the container in case if server behind of the NAT
|
||||||
#sudo docker exec -i $CONTAINER_NAME sh -c "ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up"
|
#sudo docker exec -i $CONTAINER_NAME sh -c "ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up"
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,11 @@ iptables -A OUTPUT -o wg0 -j ACCEPT
|
||||||
|
|
||||||
# Allow forwarding traffic only from the VPN.
|
# Allow forwarding traffic only from the VPN.
|
||||||
iptables -A FORWARD -i wg0 -o eth0 -s $WIREGUARD_SUBNET_IP/$WIREGUARD_SUBNET_CIDR -j ACCEPT
|
iptables -A FORWARD -i wg0 -o eth0 -s $WIREGUARD_SUBNET_IP/$WIREGUARD_SUBNET_CIDR -j ACCEPT
|
||||||
|
iptables -A FORWARD -i wg0 -o eth1 -s $WIREGUARD_SUBNET_IP/$WIREGUARD_SUBNET_CIDR -j ACCEPT
|
||||||
|
|
||||||
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
|
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||||
|
|
||||||
iptables -t nat -A POSTROUTING -s $WIREGUARD_SUBNET_IP/$WIREGUARD_SUBNET_CIDR -o eth0 -j MASQUERADE
|
iptables -t nat -A POSTROUTING -s $WIREGUARD_SUBNET_IP/$WIREGUARD_SUBNET_CIDR -o eth0 -j MASQUERADE
|
||||||
|
iptables -t nat -A POSTROUTING -s $WIREGUARD_SUBNET_IP/$WIREGUARD_SUBNET_CIDR -o eth1 -j MASQUERADE
|
||||||
|
|
||||||
tail -f /dev/null
|
tail -f /dev/null
|
||||||
|
|
|
@ -228,6 +228,21 @@ void Settings::addVpnSite(RouteMode mode, const QString &site, const QString &ip
|
||||||
setVpnSites(mode, sites);
|
setVpnSites(mode, sites);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Settings::addVpnSites(RouteMode mode, const QMap<QString, QString> &sites)
|
||||||
|
{
|
||||||
|
QVariantMap allSites = vpnSites(mode);
|
||||||
|
for (auto i = sites.constBegin(); i != sites.constEnd(); ++i) {
|
||||||
|
const QString &site = i.key();
|
||||||
|
const QString &ip = i.value();
|
||||||
|
|
||||||
|
if (allSites.contains(site) && allSites.value(site) == ip) continue;
|
||||||
|
|
||||||
|
allSites.insert(site, ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
setVpnSites(mode, allSites);
|
||||||
|
}
|
||||||
|
|
||||||
QStringList Settings::getVpnIps(RouteMode mode) const
|
QStringList Settings::getVpnIps(RouteMode mode) const
|
||||||
{
|
{
|
||||||
QStringList ips;
|
QStringList ips;
|
||||||
|
|
|
@ -67,6 +67,9 @@ public:
|
||||||
bool isStartMinimized() const { return m_settings.value("Conf/startMinimized", false).toBool(); }
|
bool isStartMinimized() const { return m_settings.value("Conf/startMinimized", false).toBool(); }
|
||||||
void setStartMinimized(bool enabled) { m_settings.setValue("Conf/startMinimized", enabled); }
|
void setStartMinimized(bool enabled) { m_settings.setValue("Conf/startMinimized", enabled); }
|
||||||
|
|
||||||
|
bool isSaveLogs() const { return m_settings.value("Conf/saveLogs", false).toBool(); }
|
||||||
|
void setSaveLogs(bool enabled) { m_settings.setValue("Conf/saveLogs", enabled); }
|
||||||
|
|
||||||
enum RouteMode {
|
enum RouteMode {
|
||||||
VpnAllSites,
|
VpnAllSites,
|
||||||
VpnOnlyForwardSites,
|
VpnOnlyForwardSites,
|
||||||
|
@ -82,12 +85,15 @@ public:
|
||||||
QVariantMap vpnSites(RouteMode mode) const { return m_settings.value("Conf/" + routeModeString(mode)).toMap(); }
|
QVariantMap vpnSites(RouteMode mode) const { return m_settings.value("Conf/" + routeModeString(mode)).toMap(); }
|
||||||
void setVpnSites(RouteMode mode, const QVariantMap &sites) { m_settings.setValue("Conf/"+ routeModeString(mode), sites); m_settings.sync(); }
|
void setVpnSites(RouteMode mode, const QVariantMap &sites) { m_settings.setValue("Conf/"+ routeModeString(mode), sites); m_settings.sync(); }
|
||||||
void addVpnSite(RouteMode mode, const QString &site, const QString &ip= "");
|
void addVpnSite(RouteMode mode, const QString &site, const QString &ip= "");
|
||||||
|
void addVpnSites(RouteMode mode, const QMap<QString, QString> &sites); // map <site, ip>
|
||||||
QStringList getVpnIps(RouteMode mode) const;
|
QStringList getVpnIps(RouteMode mode) const;
|
||||||
void removeVpnSite(RouteMode mode, const QString &site);
|
void removeVpnSite(RouteMode mode, const QString &site);
|
||||||
|
|
||||||
void addVpnIps(RouteMode mode, const QStringList &ip);
|
void addVpnIps(RouteMode mode, const QStringList &ip);
|
||||||
void removeVpnSites(RouteMode mode, const QStringList &sites);
|
void removeVpnSites(RouteMode mode, const QStringList &sites);
|
||||||
|
|
||||||
|
bool useAmneziaDns() const { return m_settings.value("Conf/useAmneziaDns", true).toBool(); }
|
||||||
|
void setUseAmneziaDns(bool enabled) { m_settings.setValue("Conf/useAmneziaDns", enabled); }
|
||||||
|
|
||||||
QString primaryDns() const;
|
QString primaryDns() const;
|
||||||
QString secondaryDns() const;
|
QString secondaryDns() const;
|
||||||
|
|
|
@ -24,7 +24,7 @@ enum class Page {Start = 0, NewServer, NewServerProtocols, Vpn,
|
||||||
Wizard, WizardLow, WizardMedium, WizardHigh, WizardVpnMode, ServerConfiguringProgress,
|
Wizard, WizardLow, WizardMedium, WizardHigh, WizardVpnMode, ServerConfiguringProgress,
|
||||||
GeneralSettings, AppSettings, NetworkSettings, ServerSettings,
|
GeneralSettings, AppSettings, NetworkSettings, ServerSettings,
|
||||||
ServerContainers, ServersList, ShareConnection, Sites,
|
ServerContainers, ServersList, ShareConnection, Sites,
|
||||||
ProtocolSettings, ProtocolShare, QrDecoder};
|
ProtocolSettings, ProtocolShare, QrDecoder, About};
|
||||||
Q_ENUM_NS(Page)
|
Q_ENUM_NS(Page)
|
||||||
|
|
||||||
static void declareQmlPageEnum() {
|
static void declareQmlPageEnum() {
|
||||||
|
|
|
@ -4,6 +4,10 @@
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include "ui/qautostart.h"
|
#include "ui/qautostart.h"
|
||||||
|
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
using namespace PageEnumNS;
|
using namespace PageEnumNS;
|
||||||
|
|
||||||
|
@ -11,7 +15,8 @@ AppSettingsLogic::AppSettingsLogic(UiLogic *logic, QObject *parent):
|
||||||
PageLogicBase(logic, parent),
|
PageLogicBase(logic, parent),
|
||||||
m_checkBoxAutostartChecked{false},
|
m_checkBoxAutostartChecked{false},
|
||||||
m_checkBoxAutoConnectChecked{false},
|
m_checkBoxAutoConnectChecked{false},
|
||||||
m_checkBoxStartMinimizedChecked{false}
|
m_checkBoxStartMinimizedChecked{false},
|
||||||
|
m_checkBoxSaveLogsChecked{false}
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +26,7 @@ void AppSettingsLogic::onUpdatePage()
|
||||||
set_checkBoxAutostartChecked(Autostart::isAutostart());
|
set_checkBoxAutostartChecked(Autostart::isAutostart());
|
||||||
set_checkBoxAutoConnectChecked(m_settings.isAutoConnect());
|
set_checkBoxAutoConnectChecked(m_settings.isAutoConnect());
|
||||||
set_checkBoxStartMinimizedChecked(m_settings.isStartMinimized());
|
set_checkBoxStartMinimizedChecked(m_settings.isStartMinimized());
|
||||||
|
set_checkBoxSaveLogsChecked(m_settings.isSaveLogs());
|
||||||
|
|
||||||
QString ver = QString("%1: %2 (%3)")
|
QString ver = QString("%1: %2 (%3)")
|
||||||
.arg(tr("Software version"))
|
.arg(tr("Software version"))
|
||||||
|
@ -47,7 +53,38 @@ void AppSettingsLogic::onCheckBoxStartMinimizedToggled(bool checked)
|
||||||
m_settings.setStartMinimized(checked);
|
m_settings.setStartMinimized(checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AppSettingsLogic::onCheckBoxSaveLogsCheckedToggled(bool checked)
|
||||||
|
{
|
||||||
|
m_settings.setSaveLogs(checked);
|
||||||
|
}
|
||||||
|
|
||||||
void AppSettingsLogic::onPushButtonOpenLogsClicked()
|
void AppSettingsLogic::onPushButtonOpenLogsClicked()
|
||||||
{
|
{
|
||||||
Debug::openLogsFolder();
|
Debug::openLogsFolder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AppSettingsLogic::onPushButtonExportLogsClicked()
|
||||||
|
{
|
||||||
|
QString log = Debug::getLogFile();
|
||||||
|
QString ext = ".log";
|
||||||
|
|
||||||
|
QString fileName = QFileDialog::getSaveFileName(nullptr, tr("Save log"),
|
||||||
|
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*" + ext);
|
||||||
|
|
||||||
|
if (fileName.isEmpty()) return;
|
||||||
|
if (!fileName.endsWith(ext)) fileName.append(ext);
|
||||||
|
|
||||||
|
QFile save(fileName);
|
||||||
|
save.open(QIODevice::WriteOnly);
|
||||||
|
save.write(log.toUtf8());
|
||||||
|
save.close();
|
||||||
|
|
||||||
|
QFileInfo fi(fileName);
|
||||||
|
QDesktopServices::openUrl(fi.absoluteDir().absolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppSettingsLogic::onPushButtonClearLogsClicked()
|
||||||
|
{
|
||||||
|
Debug::clearLogs();
|
||||||
|
Debug::clearServiceLogs();
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ class AppSettingsLogic : public PageLogicBase
|
||||||
AUTO_PROPERTY(bool, checkBoxAutostartChecked)
|
AUTO_PROPERTY(bool, checkBoxAutostartChecked)
|
||||||
AUTO_PROPERTY(bool, checkBoxAutoConnectChecked)
|
AUTO_PROPERTY(bool, checkBoxAutoConnectChecked)
|
||||||
AUTO_PROPERTY(bool, checkBoxStartMinimizedChecked)
|
AUTO_PROPERTY(bool, checkBoxStartMinimizedChecked)
|
||||||
|
AUTO_PROPERTY(bool, checkBoxSaveLogsChecked)
|
||||||
AUTO_PROPERTY(QString, labelVersionText)
|
AUTO_PROPERTY(QString, labelVersionText)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -19,7 +20,10 @@ public:
|
||||||
Q_INVOKABLE void onCheckBoxAutostartToggled(bool checked);
|
Q_INVOKABLE void onCheckBoxAutostartToggled(bool checked);
|
||||||
Q_INVOKABLE void onCheckBoxAutoconnectToggled(bool checked);
|
Q_INVOKABLE void onCheckBoxAutoconnectToggled(bool checked);
|
||||||
Q_INVOKABLE void onCheckBoxStartMinimizedToggled(bool checked);
|
Q_INVOKABLE void onCheckBoxStartMinimizedToggled(bool checked);
|
||||||
|
Q_INVOKABLE void onCheckBoxSaveLogsCheckedToggled(bool checked);
|
||||||
Q_INVOKABLE void onPushButtonOpenLogsClicked();
|
Q_INVOKABLE void onPushButtonOpenLogsClicked();
|
||||||
|
Q_INVOKABLE void onPushButtonExportLogsClicked();
|
||||||
|
Q_INVOKABLE void onPushButtonClearLogsClicked();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AppSettingsLogic(UiLogic *uiLogic, QObject *parent = nullptr);
|
explicit AppSettingsLogic(UiLogic *uiLogic, QObject *parent = nullptr);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
NetworkSettingsLogic::NetworkSettingsLogic(UiLogic *logic, QObject *parent):
|
NetworkSettingsLogic::NetworkSettingsLogic(UiLogic *logic, QObject *parent):
|
||||||
PageLogicBase(logic, parent),
|
PageLogicBase(logic, parent),
|
||||||
|
m_checkBoxUseAmneziaDnsChecked{false},
|
||||||
m_ipAddressRegex{Utils::ipAddressRegExp()}
|
m_ipAddressRegex{Utils::ipAddressRegExp()}
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -12,6 +13,8 @@ NetworkSettingsLogic::NetworkSettingsLogic(UiLogic *logic, QObject *parent):
|
||||||
|
|
||||||
void NetworkSettingsLogic::onUpdatePage()
|
void NetworkSettingsLogic::onUpdatePage()
|
||||||
{
|
{
|
||||||
|
set_checkBoxUseAmneziaDnsChecked(m_settings.useAmneziaDns());
|
||||||
|
|
||||||
set_lineEditDns1Text(m_settings.primaryDns());
|
set_lineEditDns1Text(m_settings.primaryDns());
|
||||||
set_lineEditDns2Text(m_settings.secondaryDns());
|
set_lineEditDns2Text(m_settings.secondaryDns());
|
||||||
}
|
}
|
||||||
|
@ -41,3 +44,8 @@ void NetworkSettingsLogic::onPushButtonResetDns2Clicked()
|
||||||
m_settings.setSecondaryDns(m_settings.cloudFlareNs2);
|
m_settings.setSecondaryDns(m_settings.cloudFlareNs2);
|
||||||
onUpdatePage();
|
onUpdatePage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NetworkSettingsLogic::onCheckBoxUseAmneziaDnsToggled(bool checked)
|
||||||
|
{
|
||||||
|
m_settings.setUseAmneziaDns(checked);
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ class NetworkSettingsLogic : public PageLogicBase
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
AUTO_PROPERTY(bool, checkBoxUseAmneziaDnsChecked)
|
||||||
|
|
||||||
AUTO_PROPERTY(QString, lineEditDns1Text)
|
AUTO_PROPERTY(QString, lineEditDns1Text)
|
||||||
AUTO_PROPERTY(QString, lineEditDns2Text)
|
AUTO_PROPERTY(QString, lineEditDns2Text)
|
||||||
READONLY_PROPERTY(QRegExp, ipAddressRegex)
|
READONLY_PROPERTY(QRegExp, ipAddressRegex)
|
||||||
|
@ -21,6 +23,8 @@ public:
|
||||||
Q_INVOKABLE void onPushButtonResetDns1Clicked();
|
Q_INVOKABLE void onPushButtonResetDns1Clicked();
|
||||||
Q_INVOKABLE void onPushButtonResetDns2Clicked();
|
Q_INVOKABLE void onPushButtonResetDns2Clicked();
|
||||||
|
|
||||||
|
Q_INVOKABLE void onCheckBoxUseAmneziaDnsToggled(bool checked);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit NetworkSettingsLogic(UiLogic *uiLogic, QObject *parent = nullptr);
|
explicit NetworkSettingsLogic(UiLogic *uiLogic, QObject *parent = nullptr);
|
||||||
~NetworkSettingsLogic() = default;
|
~NetworkSettingsLogic() = default;
|
||||||
|
|
|
@ -38,8 +38,6 @@ void QrDecoderLogic::onDetectedQrCode(const QString &code)
|
||||||
|
|
||||||
|
|
||||||
if (magic == amnezia::qrMagicCode) {
|
if (magic == amnezia::qrMagicCode) {
|
||||||
qDebug() << "QrDecoderLogic::onDetectedQrCode magic code detected" << magic << ba.size();
|
|
||||||
|
|
||||||
quint8 chunksCount; s >> chunksCount;
|
quint8 chunksCount; s >> chunksCount;
|
||||||
if (totalChunksCount() != chunksCount) {
|
if (totalChunksCount() != chunksCount) {
|
||||||
m_chunks.clear();
|
m_chunks.clear();
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include "../uilogic.h"
|
#include "../uilogic.h"
|
||||||
|
#include "../pages_logic/VpnLogic.h"
|
||||||
|
#include "vpnconnection.h"
|
||||||
|
|
||||||
|
|
||||||
ServerContainersLogic::ServerContainersLogic(UiLogic *logic, QObject *parent):
|
ServerContainersLogic::ServerContainersLogic(UiLogic *logic, QObject *parent):
|
||||||
PageLogicBase(logic, parent)
|
PageLogicBase(logic, parent)
|
||||||
|
@ -42,8 +45,17 @@ void ServerContainersLogic::onPushButtonProtoSettingsClicked(DockerContainer c,
|
||||||
|
|
||||||
void ServerContainersLogic::onPushButtonDefaultClicked(DockerContainer c)
|
void ServerContainersLogic::onPushButtonDefaultClicked(DockerContainer c)
|
||||||
{
|
{
|
||||||
|
if (m_settings.defaultContainer(uiLogic()->selectedServerIndex) == c) return;
|
||||||
|
|
||||||
m_settings.setDefaultContainer(uiLogic()->selectedServerIndex, c);
|
m_settings.setDefaultContainer(uiLogic()->selectedServerIndex, c);
|
||||||
uiLogic()->onUpdateAllPages();
|
uiLogic()->onUpdateAllPages();
|
||||||
|
|
||||||
|
if (uiLogic()->selectedServerIndex != m_settings.defaultServerIndex()) return;
|
||||||
|
if (!uiLogic()->m_vpnConnection) return;
|
||||||
|
if (!uiLogic()->m_vpnConnection->isConnected()) return;
|
||||||
|
|
||||||
|
uiLogic()->vpnLogic()->onDisconnect();
|
||||||
|
uiLogic()->vpnLogic()->onConnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerContainersLogic::onPushButtonShareClicked(DockerContainer c)
|
void ServerContainersLogic::onPushButtonShareClicked(DockerContainer c)
|
||||||
|
|
|
@ -28,8 +28,8 @@ void ServerSettingsLogic::onUpdatePage()
|
||||||
set_pushButtonClearVisible(m_settings.haveAuthData(uiLogic()->selectedServerIndex));
|
set_pushButtonClearVisible(m_settings.haveAuthData(uiLogic()->selectedServerIndex));
|
||||||
set_pushButtonClearClientCacheVisible(m_settings.haveAuthData(uiLogic()->selectedServerIndex));
|
set_pushButtonClearClientCacheVisible(m_settings.haveAuthData(uiLogic()->selectedServerIndex));
|
||||||
set_pushButtonShareFullVisible(m_settings.haveAuthData(uiLogic()->selectedServerIndex));
|
set_pushButtonShareFullVisible(m_settings.haveAuthData(uiLogic()->selectedServerIndex));
|
||||||
QJsonObject server = m_settings.server(uiLogic()->selectedServerIndex);
|
const QJsonObject &server = m_settings.server(uiLogic()->selectedServerIndex);
|
||||||
QString port = server.value(config_key::port).toString();
|
const QString &port = server.value(config_key::port).toString();
|
||||||
set_labelServerText(QString("%1@%2%3%4")
|
set_labelServerText(QString("%1@%2%3%4")
|
||||||
.arg(server.value(config_key::userName).toString())
|
.arg(server.value(config_key::userName).toString())
|
||||||
.arg(server.value(config_key::hostName).toString())
|
.arg(server.value(config_key::hostName).toString())
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
#include <QZXing>
|
//#include <QZXing>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include "QZXing.h"
|
||||||
|
#include "QZXingImageProvider.h"
|
||||||
|
#include "QZXingFilter.h"
|
||||||
|
|
||||||
#include "ShareConnectionLogic.h"
|
#include "ShareConnectionLogic.h"
|
||||||
|
|
||||||
#include "configurators/cloak_configurator.h"
|
#include "configurators/cloak_configurator.h"
|
||||||
|
@ -60,21 +64,24 @@ void ShareConnectionLogic::onPushButtonShareAmneziaGenerateClicked()
|
||||||
set_shareAmneziaQrCodeTextSeriesLength(0);
|
set_shareAmneziaQrCodeTextSeriesLength(0);
|
||||||
|
|
||||||
QJsonObject serverConfig;
|
QJsonObject serverConfig;
|
||||||
|
int serverIndex = uiLogic()->selectedServerIndex;
|
||||||
|
DockerContainer container = uiLogic()->selectedDockerContainer;
|
||||||
|
|
||||||
// Full access
|
// Full access
|
||||||
if (shareFullAccess()) {
|
if (shareFullAccess()) {
|
||||||
serverConfig = m_settings.server(uiLogic()->selectedServerIndex);
|
serverConfig = m_settings.server(serverIndex);
|
||||||
}
|
}
|
||||||
// Container share
|
// Container share
|
||||||
else {
|
else {
|
||||||
ServerCredentials credentials = m_settings.serverCredentials(uiLogic()->selectedServerIndex);
|
ServerCredentials credentials = m_settings.serverCredentials(serverIndex);
|
||||||
QJsonObject containerConfig = m_settings.containerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer);
|
QJsonObject containerConfig = m_settings.containerConfig(serverIndex, container);
|
||||||
containerConfig.insert(config_key::container, ContainerProps::containerToString(uiLogic()->selectedDockerContainer));
|
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
|
||||||
|
|
||||||
ErrorCode e = ErrorCode::NoError;
|
ErrorCode e = ErrorCode::NoError;
|
||||||
for (Proto p: ContainerProps::protocolsForContainer(uiLogic()->selectedDockerContainer)) {
|
for (Proto p: ContainerProps::protocolsForContainer(container)) {
|
||||||
QJsonObject protoConfig = m_settings.protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, p);
|
QJsonObject protoConfig = m_settings.protocolConfig(serverIndex, container, p);
|
||||||
|
|
||||||
QString cfg = VpnConfigurator::genVpnProtocolConfig(credentials, uiLogic()->selectedDockerContainer, containerConfig, p, &e);
|
QString cfg = VpnConfigurator::genVpnProtocolConfig(credentials, container, containerConfig, p, &e);
|
||||||
if (e) {
|
if (e) {
|
||||||
cfg = "Error generating config";
|
cfg = "Error generating config";
|
||||||
break;
|
break;
|
||||||
|
@ -85,12 +92,17 @@ void ShareConnectionLogic::onPushButtonShareAmneziaGenerateClicked()
|
||||||
|
|
||||||
QByteArray ba;
|
QByteArray ba;
|
||||||
if (!e) {
|
if (!e) {
|
||||||
serverConfig = m_settings.server(uiLogic()->selectedServerIndex);
|
serverConfig = m_settings.server(serverIndex);
|
||||||
serverConfig.remove(config_key::userName);
|
serverConfig.remove(config_key::userName);
|
||||||
serverConfig.remove(config_key::password);
|
serverConfig.remove(config_key::password);
|
||||||
serverConfig.remove(config_key::port);
|
serverConfig.remove(config_key::port);
|
||||||
serverConfig.insert(config_key::containers, QJsonArray {containerConfig});
|
serverConfig.insert(config_key::containers, QJsonArray {containerConfig});
|
||||||
serverConfig.insert(config_key::defaultContainer, ContainerProps::containerToString(uiLogic()->selectedDockerContainer));
|
serverConfig.insert(config_key::defaultContainer, ContainerProps::containerToString(container));
|
||||||
|
|
||||||
|
auto dns = VpnConfigurator::getDnsForConfig(serverIndex);
|
||||||
|
serverConfig.insert(config_key::dns1, dns.first);
|
||||||
|
serverConfig.insert(config_key::dns2, dns.second);
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
set_textEditShareAmneziaCodeText(tr("Error while generating connection profile"));
|
set_textEditShareAmneziaCodeText(tr("Error while generating connection profile"));
|
||||||
|
@ -111,12 +123,15 @@ void ShareConnectionLogic::onPushButtonShareAmneziaGenerateClicked()
|
||||||
|
|
||||||
void ShareConnectionLogic::onPushButtonShareOpenVpnGenerateClicked()
|
void ShareConnectionLogic::onPushButtonShareOpenVpnGenerateClicked()
|
||||||
{
|
{
|
||||||
ServerCredentials credentials = m_settings.serverCredentials(uiLogic()->selectedServerIndex);
|
int serverIndex = uiLogic()->selectedServerIndex;
|
||||||
const QJsonObject &containerConfig = m_settings.containerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer);
|
DockerContainer container = uiLogic()->selectedDockerContainer;
|
||||||
|
ServerCredentials credentials = m_settings.serverCredentials(serverIndex);
|
||||||
|
|
||||||
|
const QJsonObject &containerConfig = m_settings.containerConfig(serverIndex, container);
|
||||||
|
|
||||||
ErrorCode e = ErrorCode::NoError;
|
ErrorCode e = ErrorCode::NoError;
|
||||||
QString cfg = OpenVpnConfigurator::genOpenVpnConfig(credentials, uiLogic()->selectedDockerContainer, containerConfig, &e);
|
QString cfg = OpenVpnConfigurator::genOpenVpnConfig(credentials, container, containerConfig, &e);
|
||||||
cfg = VpnConfigurator::processConfigWithExportSettings(uiLogic()->selectedDockerContainer, Proto::OpenVpn, cfg);
|
cfg = VpnConfigurator::processConfigWithExportSettings(serverIndex, container, Proto::OpenVpn, cfg);
|
||||||
|
|
||||||
set_textEditShareOpenVpnCodeText(QJsonDocument::fromJson(cfg.toUtf8()).object()[config_key::config].toString());
|
set_textEditShareOpenVpnCodeText(QJsonDocument::fromJson(cfg.toUtf8()).object()[config_key::config].toString());
|
||||||
}
|
}
|
||||||
|
@ -202,7 +217,7 @@ void ShareConnectionLogic::onPushButtonShareWireGuardGenerateClicked()
|
||||||
errorString(e));
|
errorString(e));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cfg = VpnConfigurator::processConfigWithExportSettings(container, Proto::WireGuard, cfg);
|
cfg = VpnConfigurator::processConfigWithExportSettings(serverIndex, container, Proto::WireGuard, cfg);
|
||||||
cfg = QJsonDocument::fromJson(cfg.toUtf8()).object()[config_key::config].toString();
|
cfg = QJsonDocument::fromJson(cfg.toUtf8()).object()[config_key::config].toString();
|
||||||
|
|
||||||
set_textEditShareWireGuardCodeText(cfg);
|
set_textEditShareWireGuardCodeText(cfg);
|
||||||
|
@ -223,7 +238,7 @@ void ShareConnectionLogic::onPushButtonShareIkev2GenerateClicked()
|
||||||
Ikev2Configurator::ConnectionData connData = Ikev2Configurator::prepareIkev2Config(credentials, container);
|
Ikev2Configurator::ConnectionData connData = Ikev2Configurator::prepareIkev2Config(credentials, container);
|
||||||
|
|
||||||
QString cfg = Ikev2Configurator::genIkev2Config(connData);
|
QString cfg = Ikev2Configurator::genIkev2Config(connData);
|
||||||
cfg = VpnConfigurator::processConfigWithExportSettings(container, Proto::Ikev2, cfg);
|
cfg = VpnConfigurator::processConfigWithExportSettings(serverIndex, container, Proto::Ikev2, cfg);
|
||||||
cfg = QJsonDocument::fromJson(cfg.toUtf8()).object()[config_key::cert].toString();
|
cfg = QJsonDocument::fromJson(cfg.toUtf8()).object()[config_key::cert].toString();
|
||||||
|
|
||||||
set_textEditShareIkev2CertText(cfg);
|
set_textEditShareIkev2CertText(cfg);
|
||||||
|
|
|
@ -98,27 +98,32 @@ void SitesLogic::onPushButtonAddCustomSitesClicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SitesLogic::onPushButtonSitesDeleteClicked(int row)
|
void SitesLogic::onPushButtonSitesDeleteClicked(QStringList items)
|
||||||
{
|
{
|
||||||
Settings::RouteMode mode = m_settings.routeMode();
|
Settings::RouteMode mode = m_settings.routeMode();
|
||||||
|
|
||||||
auto siteModel = qobject_cast<SitesModel*> (tableViewSitesModel());
|
auto siteModel = qobject_cast<SitesModel*> (tableViewSitesModel());
|
||||||
if (!siteModel) {
|
if (!siteModel || items.isEmpty()) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (row < 0 || row >= siteModel->rowCount()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
QStringList sites;
|
||||||
QStringList sites;
|
QStringList ips;
|
||||||
|
|
||||||
|
for (const QString &s: items) {
|
||||||
|
bool ok;
|
||||||
|
int row = s.toInt(&ok);
|
||||||
|
if (!ok || row < 0 || row >= siteModel->rowCount()) return;
|
||||||
sites.append(siteModel->data(row, 0).toString());
|
sites.append(siteModel->data(row, 0).toString());
|
||||||
m_settings.removeVpnSites(mode, sites);
|
|
||||||
|
if (uiLogic()->m_vpnConnection->connectionState() == VpnProtocol::Connected) {
|
||||||
|
ips.append(siteModel->data(row, 1).toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_settings.removeVpnSites(mode, sites);
|
||||||
|
|
||||||
if (uiLogic()->m_vpnConnection->connectionState() == VpnProtocol::Connected) {
|
if (uiLogic()->m_vpnConnection->connectionState() == VpnProtocol::Connected) {
|
||||||
QStringList ips;
|
|
||||||
ips.append(siteModel->data(row, 1).toString());
|
|
||||||
uiLogic()->m_vpnConnection->deleteRoutes(ips);
|
uiLogic()->m_vpnConnection->deleteRoutes(ips);
|
||||||
uiLogic()->m_vpnConnection->flushDns();
|
uiLogic()->m_vpnConnection->flushDns();
|
||||||
}
|
}
|
||||||
|
@ -137,18 +142,53 @@ void SitesLogic::onPushButtonSitesImportClicked(const QString& fileName)
|
||||||
Settings::RouteMode mode = m_settings.routeMode();
|
Settings::RouteMode mode = m_settings.routeMode();
|
||||||
|
|
||||||
QStringList ips;
|
QStringList ips;
|
||||||
|
QMap<QString, QString> sites;
|
||||||
|
|
||||||
while (!file.atEnd()) {
|
while (!file.atEnd()) {
|
||||||
QString line = file.readLine();
|
QString line = file.readLine();
|
||||||
|
QStringList line_ips;
|
||||||
|
QStringList line_sites;
|
||||||
|
|
||||||
int pos = 0;
|
int posDomain = 0;
|
||||||
QRegExp rx = Utils::ipAddressWithSubnetRegExp();
|
QRegExp domainRx = Utils::domainRegExp();
|
||||||
while ((pos = rx.indexIn(line, pos)) != -1) {
|
while ((posDomain = domainRx.indexIn(line, posDomain)) != -1) {
|
||||||
ips << rx.cap(0);
|
line_sites.append(domainRx.cap(0));
|
||||||
pos += rx.matchedLength();
|
posDomain += domainRx.matchedLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int posIp = 0;
|
||||||
|
QRegExp ipRx = Utils::ipAddressWithSubnetRegExp();
|
||||||
|
while ((posIp = ipRx.indexIn(line, posIp)) != -1) {
|
||||||
|
line_ips.append(ipRx.cap(0));
|
||||||
|
posIp += ipRx.matchedLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
// domain regex cover ip regex, so remove ips from sites
|
||||||
|
for (const QString& ip: line_ips) {
|
||||||
|
line_sites.removeAll(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line_sites.size() == 1 && line_ips.size() == 1) {
|
||||||
|
sites.insert(line_sites.at(0), line_ips.at(0));
|
||||||
|
}
|
||||||
|
else if (line_sites.size() > 0 && line_ips.size() == 0) {
|
||||||
|
for (const QString& site: line_sites) {
|
||||||
|
sites.insert(site, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (const QString& site: line_sites) {
|
||||||
|
sites.insert(site, "");
|
||||||
|
}
|
||||||
|
for (const QString& ip: line_ips) {
|
||||||
|
ips.append(ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_settings.addVpnIps(mode, ips);
|
m_settings.addVpnIps(mode, ips);
|
||||||
|
m_settings.addVpnSites(mode, sites);
|
||||||
|
|
||||||
uiLogic()->m_vpnConnection->addRoutes(QStringList() << ips);
|
uiLogic()->m_vpnConnection->addRoutes(QStringList() << ips);
|
||||||
uiLogic()->m_vpnConnection->flushDns();
|
uiLogic()->m_vpnConnection->flushDns();
|
||||||
|
@ -156,3 +196,16 @@ void SitesLogic::onPushButtonSitesImportClicked(const QString& fileName)
|
||||||
onUpdatePage();
|
onUpdatePage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SitesLogic::onPushButtonSitesExportClicked()
|
||||||
|
{
|
||||||
|
Settings::RouteMode mode = m_settings.routeMode();
|
||||||
|
|
||||||
|
QVariantMap sites = m_settings.vpnSites(mode);
|
||||||
|
|
||||||
|
QString data;
|
||||||
|
for (auto s : sites.keys()) {
|
||||||
|
data += s + "\t" + sites.value(s).toString() + "\n";
|
||||||
|
}
|
||||||
|
uiLogic()->saveTextFile("Sites", ".txt", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,10 @@ public:
|
||||||
Q_INVOKABLE void onUpdatePage() override;
|
Q_INVOKABLE void onUpdatePage() override;
|
||||||
|
|
||||||
Q_INVOKABLE void onPushButtonAddCustomSitesClicked();
|
Q_INVOKABLE void onPushButtonAddCustomSitesClicked();
|
||||||
Q_INVOKABLE void onPushButtonSitesDeleteClicked(int row);
|
Q_INVOKABLE void onPushButtonSitesDeleteClicked(QStringList items);
|
||||||
Q_INVOKABLE void onPushButtonSitesImportClicked(const QString &fileName);
|
Q_INVOKABLE void onPushButtonSitesImportClicked(const QString &fileName);
|
||||||
|
Q_INVOKABLE void onPushButtonSitesExportClicked();
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SitesLogic(UiLogic *uiLogic, QObject *parent = nullptr);
|
explicit SitesLogic(UiLogic *uiLogic, QObject *parent = nullptr);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include "../uilogic.h"
|
#include "../uilogic.h"
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
|
#include <configurators/vpn_configurator.h>
|
||||||
|
|
||||||
|
|
||||||
VpnLogic::VpnLogic(UiLogic *logic, QObject *parent):
|
VpnLogic::VpnLogic(UiLogic *logic, QObject *parent):
|
||||||
|
@ -33,15 +34,24 @@ VpnLogic::VpnLogic(UiLogic *logic, QObject *parent):
|
||||||
onConnect();
|
onConnect();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
onConnectionStateChanged(VpnProtocol::Disconnected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void VpnLogic::onUpdatePage()
|
void VpnLogic::onUpdatePage()
|
||||||
{
|
{
|
||||||
Settings::RouteMode mode = m_settings.routeMode();
|
Settings::RouteMode mode = m_settings.routeMode();
|
||||||
set_radioButtonVpnModeAllSitesChecked(mode == Settings::VpnAllSites);
|
DockerContainer selectedContainer = m_settings.defaultContainer(m_settings.defaultServerIndex());
|
||||||
set_radioButtonVpnModeForwardSitesChecked(mode == Settings::VpnOnlyForwardSites);
|
|
||||||
set_radioButtonVpnModeExceptSitesChecked(mode == Settings::VpnAllExceptSites);
|
set_isCustomRoutesSupported (selectedContainer == DockerContainer::OpenVpn ||
|
||||||
|
selectedContainer == DockerContainer::ShadowSocks||
|
||||||
|
selectedContainer == DockerContainer::Cloak);
|
||||||
|
|
||||||
|
set_radioButtonVpnModeAllSitesChecked(mode == Settings::VpnAllSites || !isCustomRoutesSupported());
|
||||||
|
set_radioButtonVpnModeForwardSitesChecked(mode == Settings::VpnOnlyForwardSites && isCustomRoutesSupported());
|
||||||
|
set_radioButtonVpnModeExceptSitesChecked(mode == Settings::VpnAllExceptSites && isCustomRoutesSupported());
|
||||||
|
|
||||||
const QJsonObject &server = uiLogic()->m_settings.defaultServer();
|
const QJsonObject &server = uiLogic()->m_settings.defaultServer();
|
||||||
QString serverString = QString("%2 (%3)")
|
QString serverString = QString("%2 (%3)")
|
||||||
|
@ -49,10 +59,19 @@ void VpnLogic::onUpdatePage()
|
||||||
.arg(server.value(config_key::hostName).toString());
|
.arg(server.value(config_key::hostName).toString());
|
||||||
set_labelCurrentServer(serverString);
|
set_labelCurrentServer(serverString);
|
||||||
|
|
||||||
DockerContainer selectedContainer = m_settings.defaultContainer(m_settings.defaultServerIndex());
|
|
||||||
QString selectedContainerName = ContainerProps::containerHumanNames().value(selectedContainer);
|
QString selectedContainerName = ContainerProps::containerHumanNames().value(selectedContainer);
|
||||||
set_labelCurrentService(selectedContainerName);
|
set_labelCurrentService(selectedContainerName);
|
||||||
|
|
||||||
|
auto dns = VpnConfigurator::getDnsForConfig(m_settings.defaultServerIndex());
|
||||||
|
set_amneziaDnsEnabled(dns.first == protocols::dns::amneziaDnsIp);
|
||||||
|
if (dns.first == protocols::dns::amneziaDnsIp) {
|
||||||
|
set_labelCurrentDns("On your server");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
set_labelCurrentDns(dns.first + ", " + dns.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
set_isContainerWorkingOnPlatform(ContainerProps::isWorkingOnPlatform(selectedContainer));
|
set_isContainerWorkingOnPlatform(ContainerProps::isWorkingOnPlatform(selectedContainer));
|
||||||
if (!isContainerWorkingOnPlatform()) {
|
if (!isContainerWorkingOnPlatform()) {
|
||||||
set_labelErrorText(tr("AmneziaVPN not supporting selected protocol on this device. Select another protocol."));
|
set_labelErrorText(tr("AmneziaVPN not supporting selected protocol on this device. Select another protocol."));
|
||||||
|
|
|
@ -16,6 +16,9 @@ class VpnLogic : public PageLogicBase
|
||||||
AUTO_PROPERTY(QString, labelStateText)
|
AUTO_PROPERTY(QString, labelStateText)
|
||||||
AUTO_PROPERTY(QString, labelCurrentServer)
|
AUTO_PROPERTY(QString, labelCurrentServer)
|
||||||
AUTO_PROPERTY(QString, labelCurrentService)
|
AUTO_PROPERTY(QString, labelCurrentService)
|
||||||
|
AUTO_PROPERTY(QString, labelCurrentDns)
|
||||||
|
AUTO_PROPERTY(bool, amneziaDnsEnabled)
|
||||||
|
|
||||||
AUTO_PROPERTY(bool, pushButtonConnectEnabled)
|
AUTO_PROPERTY(bool, pushButtonConnectEnabled)
|
||||||
AUTO_PROPERTY(bool, pushButtonConnectVisible)
|
AUTO_PROPERTY(bool, pushButtonConnectVisible)
|
||||||
AUTO_PROPERTY(bool, widgetVpnModeEnabled)
|
AUTO_PROPERTY(bool, widgetVpnModeEnabled)
|
||||||
|
@ -24,6 +27,8 @@ class VpnLogic : public PageLogicBase
|
||||||
AUTO_PROPERTY(QString, labelErrorText)
|
AUTO_PROPERTY(QString, labelErrorText)
|
||||||
AUTO_PROPERTY(QString, labelVersionText)
|
AUTO_PROPERTY(QString, labelVersionText)
|
||||||
|
|
||||||
|
AUTO_PROPERTY(bool, isCustomRoutesSupported)
|
||||||
|
|
||||||
AUTO_PROPERTY(bool, radioButtonVpnModeAllSitesChecked)
|
AUTO_PROPERTY(bool, radioButtonVpnModeAllSitesChecked)
|
||||||
AUTO_PROPERTY(bool, radioButtonVpnModeForwardSitesChecked)
|
AUTO_PROPERTY(bool, radioButtonVpnModeForwardSitesChecked)
|
||||||
AUTO_PROPERTY(bool, radioButtonVpnModeExceptSitesChecked)
|
AUTO_PROPERTY(bool, radioButtonVpnModeExceptSitesChecked)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QStorageInfo>
|
#include <QStorageInfo>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
#include "OtherProtocolsLogic.h"
|
#include "OtherProtocolsLogic.h"
|
||||||
#include "core/servercontroller.h"
|
#include "core/servercontroller.h"
|
||||||
|
@ -17,7 +18,9 @@ using namespace amnezia;
|
||||||
using namespace PageEnumNS;
|
using namespace PageEnumNS;
|
||||||
|
|
||||||
OtherProtocolsLogic::OtherProtocolsLogic(UiLogic *logic, QObject *parent):
|
OtherProtocolsLogic::OtherProtocolsLogic(UiLogic *logic, QObject *parent):
|
||||||
PageProtocolLogicBase(logic, parent)
|
PageProtocolLogicBase(logic, parent),
|
||||||
|
m_checkBoxSftpRestoreChecked{false}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -41,17 +44,12 @@ void OtherProtocolsLogic::updateProtocolPage(const QJsonObject &config, DockerCo
|
||||||
set_labelTftpPortText(config.value(config_key::port).toString());
|
set_labelTftpPortText(config.value(config_key::port).toString());
|
||||||
|
|
||||||
set_labelTorWebSiteAddressText(config.value(config_key::site).toString());
|
set_labelTorWebSiteAddressText(config.value(config_key::site).toString());
|
||||||
|
set_pushButtonSftpMountEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
//QJsonObject OtherProtocolsLogic::getProtocolConfigFromPage(QJsonObject oldConfig)
|
|
||||||
//{
|
|
||||||
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
void OtherProtocolsLogic::onPushButtonSftpMountDriveClicked()
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
|
QString OtherProtocolsLogic::getNextDriverLetter() const
|
||||||
|
{
|
||||||
QProcess drivesProc;
|
QProcess drivesProc;
|
||||||
drivesProc.start("wmic logicaldisk get caption");
|
drivesProc.start("wmic logicaldisk get caption");
|
||||||
drivesProc.waitForFinished();
|
drivesProc.waitForFinished();
|
||||||
|
@ -68,53 +66,93 @@ void OtherProtocolsLogic::onPushButtonSftpMountDriveClicked()
|
||||||
if (letter == "C:") {
|
if (letter == "C:") {
|
||||||
// set err info
|
// set err info
|
||||||
qDebug() << "Can't find free drive letter";
|
qDebug() << "Can't find free drive letter";
|
||||||
return;
|
return "";
|
||||||
|
}
|
||||||
|
return letter;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//QJsonObject OtherProtocolsLogic::getProtocolConfigFromPage(QJsonObject oldConfig)
|
||||||
|
//{
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
void OtherProtocolsLogic::onPushButtonSftpMountDriveClicked()
|
||||||
|
{
|
||||||
|
QString mountPath;
|
||||||
|
QString cmd;
|
||||||
|
QString host = m_settings.serverCredentials(uiLogic()->selectedServerIndex).hostName;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
mountPath = getNextDriverLetter() + ":";
|
||||||
|
// QString cmd = QString("net use \\\\sshfs\\%1@x.x.x.x!%2 /USER:%1 %3")
|
||||||
|
// .arg(labelTftpUserNameText())
|
||||||
|
// .arg(labelTftpPortText())
|
||||||
|
// .arg(labelTftpPasswordText());
|
||||||
|
|
||||||
|
cmd = "C:\\Program Files\\SSHFS-Win\\bin\\sshfs.exe";
|
||||||
|
#elif defined AMNEZIA_DESKTOP
|
||||||
|
mountPath = QString("%1/sftp:%2:%3")
|
||||||
|
.arg(QStandardPaths::writableLocation(QStandardPaths::HomeLocation))
|
||||||
|
.arg(host)
|
||||||
|
.arg(labelTftpPortText());
|
||||||
|
QDir dir(mountPath);
|
||||||
|
if (!dir.exists()){
|
||||||
|
dir.mkpath(mountPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd = "/usr/local/bin/sshfs";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef AMNEZIA_DESKTOP
|
||||||
set_pushButtonSftpMountEnabled(false);
|
set_pushButtonSftpMountEnabled(false);
|
||||||
QProcess *p = new QProcess;
|
QProcess *p = new QProcess;
|
||||||
m_sftpMountProcesses.append(p);
|
m_sftpMountProcesses.append(p);
|
||||||
p->setProcessChannelMode(QProcess::MergedChannels);
|
p->setProcessChannelMode(QProcess::MergedChannels);
|
||||||
|
|
||||||
connect(p, &QProcess::readyRead, this, [this, p, letter](){
|
connect(p, &QProcess::readyRead, this, [this, p, mountPath](){
|
||||||
QString s = p->readAll();
|
QString s = p->readAll();
|
||||||
if (s.contains("The service sshfs has been started")) {
|
if (s.contains("The service sshfs has been started")) {
|
||||||
QDesktopServices::openUrl(QUrl("file:///" + letter + ":"));
|
QDesktopServices::openUrl(QUrl("file:///" + mountPath));
|
||||||
set_pushButtonSftpMountEnabled(true);
|
set_pushButtonSftpMountEnabled(true);
|
||||||
}
|
}
|
||||||
|
qDebug() << s;
|
||||||
});
|
});
|
||||||
|
|
||||||
// QString cmd = QString("net use \\\\sshfs\\%1@51.77.32.168!%2 /USER:%1 %3")
|
|
||||||
// .arg(labelTftpUserNameText())
|
|
||||||
// .arg(labelTftpPortText())
|
|
||||||
// .arg(labelTftpPasswordText());
|
|
||||||
|
|
||||||
p->setProgram("C:\\Program Files\\SSHFS-Win\\bin\\sshfs.exe");
|
|
||||||
|
|
||||||
QString host = m_settings.serverCredentials(uiLogic()->selectedServerIndex).hostName;
|
p->setProgram(cmd);
|
||||||
|
|
||||||
QString args = QString(
|
QString args = QString(
|
||||||
"%1@%2:/ %3: "
|
"%1@%2:/ %3 "
|
||||||
"-o port=%4 "
|
"-o port=%4 "
|
||||||
"-f "
|
"-f "
|
||||||
"-o reconnect"
|
"-o reconnect "
|
||||||
"-orellinks "
|
"-o rellinks "
|
||||||
"-ofstypename=SSHFS "
|
"-o fstypename=SSHFS "
|
||||||
"-o ssh_command=/usr/bin/ssh.exe "
|
"-o ssh_command=/usr/bin/ssh.exe "
|
||||||
"-oUserKnownHostsFile=/dev/null "
|
"-o UserKnownHostsFile=/dev/null "
|
||||||
"-oStrictHostKeyChecking=no "
|
"-o StrictHostKeyChecking=no "
|
||||||
"-o password_stdin")
|
"-o password_stdin")
|
||||||
.arg(labelTftpUserNameText())
|
.arg(labelTftpUserNameText())
|
||||||
.arg(host)
|
.arg(host)
|
||||||
.arg(letter)
|
.arg(mountPath)
|
||||||
.arg(labelTftpPortText());
|
.arg(labelTftpPortText());
|
||||||
|
|
||||||
|
|
||||||
p->setNativeArguments(args);
|
// args.replace("\n", " ");
|
||||||
|
// args.replace("\r", " ");
|
||||||
|
//#ifndef Q_OS_WIN
|
||||||
|
// args.replace("reconnect-orellinks", "");
|
||||||
|
//#endif
|
||||||
|
p->setArguments(args.split(" ", QString::SkipEmptyParts));
|
||||||
p->start();
|
p->start();
|
||||||
p->waitForStarted(50);
|
p->waitForStarted(50);
|
||||||
if (p->state() != QProcess::Running) {
|
if (p->state() != QProcess::Running) {
|
||||||
qDebug() << "onPushButtonSftpMountDriveClicked process not started";
|
qDebug() << "onPushButtonSftpMountDriveClicked process not started";
|
||||||
|
qDebug() << args;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
p->write((labelTftpPasswordText() + "\n").toUtf8());
|
p->write((labelTftpPasswordText() + "\n").toUtf8());
|
||||||
|
@ -123,7 +161,6 @@ void OtherProtocolsLogic::onPushButtonSftpMountDriveClicked()
|
||||||
//qDebug().noquote() << "onPushButtonSftpMountDriveClicked" << args;
|
//qDebug().noquote() << "onPushButtonSftpMountDriveClicked" << args;
|
||||||
|
|
||||||
set_pushButtonSftpMountEnabled(true);
|
set_pushButtonSftpMountEnabled(true);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,9 +34,13 @@ private:
|
||||||
Settings m_settings;
|
Settings m_settings;
|
||||||
UiLogic *m_uiLogic;
|
UiLogic *m_uiLogic;
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef AMNEZIA_DESKTOP
|
||||||
QList <QProcess *> m_sftpMountProcesses;
|
QList <QProcess *> m_sftpMountProcesses;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
QString getNextDriverLetter() const;
|
||||||
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
#endif // OTHER_PROTOCOLS_LOGIC_H
|
#endif // OTHER_PROTOCOLS_LOGIC_H
|
||||||
|
|
|
@ -3,11 +3,13 @@ import QtQuick.Controls 2.12
|
||||||
|
|
||||||
CheckBox {
|
CheckBox {
|
||||||
id: root
|
id: root
|
||||||
|
property int imageWidth : 20
|
||||||
|
property int imageHeight : 20
|
||||||
indicator: Image {
|
indicator: Image {
|
||||||
// y: 5
|
id: indicator
|
||||||
anchors.verticalCenter: root.verticalCenter
|
anchors.verticalCenter: root.verticalCenter
|
||||||
height: 20
|
height: imageHeight
|
||||||
width: 20
|
width: imageWidth
|
||||||
source: root.checked ? "qrc:/images/controls/check_on.png"
|
source: root.checked ? "qrc:/images/controls/check_on.png"
|
||||||
: "qrc:/images/controls/check_off.png"
|
: "qrc:/images/controls/check_off.png"
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ RadioButton {
|
||||||
font.family: "Lato"
|
font.family: "Lato"
|
||||||
font.styleName: "normal"
|
font.styleName: "normal"
|
||||||
font.pixelSize: 16
|
font.pixelSize: 16
|
||||||
color: "#181922"
|
color: enabled ? "#181922" : "#686972"
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
leftPadding: root.indicator.width + root.spacing
|
leftPadding: root.indicator.width + root.spacing
|
||||||
}
|
}
|
||||||
|
|
17
client/ui/qml/Controls/RichLabelType.qml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import QtQuick 2.12
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
id: label_connection_code
|
||||||
|
width: parent.width - 60
|
||||||
|
x: 30
|
||||||
|
font.pixelSize: 14
|
||||||
|
textFormat: Text.RichText
|
||||||
|
onLinkActivated: Qt.openUrlExternally(link)
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,10 +8,13 @@ BasicButtonType {
|
||||||
background: Item {}
|
background: Item {}
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
Image {
|
SvgImageType {
|
||||||
source: root.icon.source
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
svg.source: root.icon.source
|
||||||
|
color: "#100A44"
|
||||||
|
width: 25
|
||||||
|
height: 25
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
id: textItem
|
id: textItem
|
||||||
|
|
16
client/ui/qml/Controls/SvgButtonType.qml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
import "."
|
||||||
|
|
||||||
|
BasicButtonType {
|
||||||
|
id: root
|
||||||
|
icon.color: "#181922"
|
||||||
|
|
||||||
|
background: Item {}
|
||||||
|
contentItem: SvgImageType {
|
||||||
|
svg.source: icon.source
|
||||||
|
color: icon.color
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: parent.containsMouse ? 0 : 1
|
||||||
|
}
|
||||||
|
}
|
23
client/ui/qml/Controls/SvgImageType.qml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
import QtGraphicalEffects 1.15
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
property color color: "#181922"
|
||||||
|
property alias svg: image
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent
|
||||||
|
id: image
|
||||||
|
sourceSize: Qt.size(root.width, root.height)
|
||||||
|
|
||||||
|
antialiasing: true
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorOverlay {
|
||||||
|
anchors.fill: image
|
||||||
|
source: image
|
||||||
|
color: root.color
|
||||||
|
}
|
||||||
|
}
|
90
client/ui/qml/Pages/PageAbout.qml
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
import PageEnum 1.0
|
||||||
|
import "./"
|
||||||
|
import "../Controls"
|
||||||
|
import "../Config"
|
||||||
|
|
||||||
|
PageBase {
|
||||||
|
id: root
|
||||||
|
page: PageEnum.About
|
||||||
|
|
||||||
|
BackButton {
|
||||||
|
id: back_from_start
|
||||||
|
}
|
||||||
|
|
||||||
|
Caption {
|
||||||
|
id: caption
|
||||||
|
font.pixelSize: 22
|
||||||
|
text: qsTr("About Amnezia")
|
||||||
|
}
|
||||||
|
|
||||||
|
RichLabelType {
|
||||||
|
id: label_about
|
||||||
|
anchors.top: caption.bottom
|
||||||
|
|
||||||
|
text: qsTr("AmneziaVPN is opensource software, it's free forever. Our goal is to make the best VPN client in the world.
|
||||||
|
<ul>
|
||||||
|
<li>Sources on <a href=\"https://github.com/amnezia-vpn/desktop-client\">GitHub</a></li>
|
||||||
|
<li><a href=\"https://amnezia.org/\">Web Site</a></li>
|
||||||
|
<li><a href=\"https://t.me/amnezia_vpn_en\">Telegram group</a></li>
|
||||||
|
<li><a href=\"https://signal.group/#CjQKIB2gUf8QH_IXnOJMGQWMDjYz9cNfmRQipGWLFiIgc4MwEhAKBONrSiWHvoUFbbD0xwdh\">Signal group</a></li>
|
||||||
|
</ul>
|
||||||
|
")
|
||||||
|
}
|
||||||
|
|
||||||
|
Caption {
|
||||||
|
id: caption2
|
||||||
|
anchors.topMargin: 20
|
||||||
|
font.pixelSize: 22
|
||||||
|
text: qsTr("Support")
|
||||||
|
anchors.top: label_about.bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
RichLabelType {
|
||||||
|
id: label_support
|
||||||
|
anchors.top: caption2.bottom
|
||||||
|
|
||||||
|
text: qsTr("Have questions? You can get support by:
|
||||||
|
<ul>
|
||||||
|
<li><a href=\"https://t.me/amnezia_vpn_en\">Telegram group</a> (preferred way)</li>
|
||||||
|
<li>Create issue on <a href=\"https://github.com/amnezia-vpn/desktop-client/issues\">GitHub</a></li>
|
||||||
|
<li>Email to: <a href=\"support@amnezia.org\">support@amnezia.org</a></li>
|
||||||
|
</ul>")
|
||||||
|
}
|
||||||
|
|
||||||
|
Caption {
|
||||||
|
id: caption3
|
||||||
|
anchors.topMargin: 20
|
||||||
|
font.pixelSize: 22
|
||||||
|
text: qsTr("Donate")
|
||||||
|
width: undefined
|
||||||
|
anchors.top: label_support.bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
anchors.bottom: caption3.bottom
|
||||||
|
anchors.left: caption3.right
|
||||||
|
anchors.leftMargin: 5
|
||||||
|
font.pixelSize: 24
|
||||||
|
text: "♥"
|
||||||
|
color: "red"
|
||||||
|
}
|
||||||
|
|
||||||
|
RichLabelType {
|
||||||
|
id: label_donate
|
||||||
|
anchors.top: caption3.bottom
|
||||||
|
|
||||||
|
text: qsTr("Please support Amnezia project by donation, we really need it now more than ever.
|
||||||
|
<ul>
|
||||||
|
<li>By credit card on <a href=\"https://www.patreon.com/amneziavpn\">Patreon</a> (starting from $1)</li>
|
||||||
|
<li>Send some coins to addresses listed <a href=\"https://github.com/amnezia-vpn/desktop-client/blob/master/README.md\">on GitHub page</a></li>
|
||||||
|
</ul>
|
||||||
|
")
|
||||||
|
}
|
||||||
|
|
||||||
|
Logo {
|
||||||
|
id: logo
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
import PageEnum 1.0
|
import PageEnum 1.0
|
||||||
import "./"
|
import "./"
|
||||||
import "../Controls"
|
import "../Controls"
|
||||||
|
@ -14,75 +15,127 @@ PageBase {
|
||||||
id: back
|
id: back
|
||||||
}
|
}
|
||||||
Caption {
|
Caption {
|
||||||
|
id: caption
|
||||||
text: qsTr("Application Settings")
|
text: qsTr("Application Settings")
|
||||||
}
|
}
|
||||||
CheckBoxType {
|
|
||||||
x: 30
|
Flickable {
|
||||||
y: 140
|
id: fl
|
||||||
width: 211
|
width: root.width
|
||||||
height: 31
|
anchors.top: caption.bottom
|
||||||
text: qsTr("Auto connect")
|
anchors.topMargin: 20
|
||||||
checked: AppSettingsLogic.checkBoxAutoConnectChecked
|
anchors.bottom: logo.top
|
||||||
onCheckedChanged: {
|
anchors.bottomMargin: 20
|
||||||
AppSettingsLogic.checkBoxAutoConnectChecked = checked
|
anchors.left: root.left
|
||||||
AppSettingsLogic.onCheckBoxAutoconnectToggled(checked)
|
anchors.leftMargin: 30
|
||||||
}
|
anchors.right: root.right
|
||||||
}
|
anchors.rightMargin: 30
|
||||||
CheckBoxType {
|
|
||||||
x: 30
|
contentHeight: content.height
|
||||||
y: 100
|
clip: true
|
||||||
width: 211
|
|
||||||
height: 31
|
ColumnLayout {
|
||||||
text: qsTr("Auto start")
|
id: content
|
||||||
checked: AppSettingsLogic.checkBoxAutostartChecked
|
enabled: logic.pageEnabled
|
||||||
onCheckedChanged: {
|
anchors.top: parent.top
|
||||||
AppSettingsLogic.checkBoxAutostartChecked = checked
|
anchors.left: parent.left
|
||||||
AppSettingsLogic.onCheckBoxAutostartToggled(checked)
|
anchors.right: parent.right
|
||||||
}
|
|
||||||
}
|
CheckBoxType {
|
||||||
CheckBoxType {
|
Layout.fillWidth: true
|
||||||
x: 30
|
text: qsTr("Auto connect")
|
||||||
y: 180
|
checked: AppSettingsLogic.checkBoxAutoConnectChecked
|
||||||
width: 211
|
onCheckedChanged: {
|
||||||
height: 31
|
AppSettingsLogic.checkBoxAutoConnectChecked = checked
|
||||||
text: qsTr("Start minimized")
|
AppSettingsLogic.onCheckBoxAutoconnectToggled(checked)
|
||||||
checked: AppSettingsLogic.checkBoxStartMinimizedChecked
|
}
|
||||||
onCheckedChanged: {
|
}
|
||||||
AppSettingsLogic.checkBoxStartMinimizedChecked = checked
|
CheckBoxType {
|
||||||
AppSettingsLogic.onCheckBoxStartMinimizedToggled(checked)
|
Layout.fillWidth: true
|
||||||
}
|
text: qsTr("Auto start")
|
||||||
}
|
checked: AppSettingsLogic.checkBoxAutostartChecked
|
||||||
LabelType {
|
onCheckedChanged: {
|
||||||
x: 30
|
AppSettingsLogic.checkBoxAutostartChecked = checked
|
||||||
y: 240
|
AppSettingsLogic.onCheckBoxAutostartToggled(checked)
|
||||||
width: 281
|
}
|
||||||
height: 21
|
}
|
||||||
text: AppSettingsLogic.labelVersionText
|
CheckBoxType {
|
||||||
}
|
Layout.fillWidth: true
|
||||||
BlueButtonType {
|
text: qsTr("Start minimized")
|
||||||
x: 30
|
checked: AppSettingsLogic.checkBoxStartMinimizedChecked
|
||||||
y: 280
|
onCheckedChanged: {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
AppSettingsLogic.checkBoxStartMinimizedChecked = checked
|
||||||
width: parent.width - 40
|
AppSettingsLogic.onCheckBoxStartMinimizedToggled(checked)
|
||||||
height: 41
|
}
|
||||||
text: qsTr("Check for updates")
|
}
|
||||||
onClicked: {
|
LabelType {
|
||||||
Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
|
Layout.fillWidth: true
|
||||||
}
|
Layout.topMargin: 15
|
||||||
}
|
text: AppSettingsLogic.labelVersionText
|
||||||
BlueButtonType {
|
}
|
||||||
x: 30
|
BlueButtonType {
|
||||||
y: 340
|
Layout.fillWidth: true
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
Layout.preferredHeight: 41
|
||||||
width: parent.width - 40
|
text: qsTr("Check for updates")
|
||||||
height: 41
|
onClicked: {
|
||||||
text: qsTr("Open logs folder")
|
Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
|
||||||
onClicked: {
|
}
|
||||||
AppSettingsLogic.onPushButtonOpenLogsClicked()
|
}
|
||||||
|
|
||||||
|
CheckBoxType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 15
|
||||||
|
text: qsTr("Keep logs")
|
||||||
|
checked: AppSettingsLogic.checkBoxSaveLogsChecked
|
||||||
|
onCheckedChanged: {
|
||||||
|
AppSettingsLogic.checkBoxSaveLogsChecked = checked
|
||||||
|
AppSettingsLogic.onCheckBoxSaveLogsCheckedToggled(checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BlueButtonType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 41
|
||||||
|
text: qsTr("Open logs folder")
|
||||||
|
onClicked: {
|
||||||
|
AppSettingsLogic.onPushButtonOpenLogsClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlueButtonType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 15
|
||||||
|
Layout.preferredHeight: 41
|
||||||
|
text: qsTr("Export logs")
|
||||||
|
onClicked: {
|
||||||
|
AppSettingsLogic.onPushButtonExportLogsClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlueButtonType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 15
|
||||||
|
Layout.preferredHeight: 41
|
||||||
|
|
||||||
|
property string start_text: qsTr("Clear logs")
|
||||||
|
property string end_text: qsTr("Cleared")
|
||||||
|
text: start_text
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: timer
|
||||||
|
interval: 1000; running: false; repeat: false
|
||||||
|
onTriggered: parent.text = parent.start_text
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
text = end_text
|
||||||
|
timer.running = true
|
||||||
|
AppSettingsLogic.onPushButtonClearLogsClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logo {
|
Logo {
|
||||||
|
id: logo
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
|
import QtGraphicalEffects 1.15
|
||||||
import PageEnum 1.0
|
import PageEnum 1.0
|
||||||
import "./"
|
import "./"
|
||||||
import "../Controls"
|
import "../Controls"
|
||||||
|
@ -31,9 +32,9 @@ PageBase {
|
||||||
anchors.top: l1.bottom
|
anchors.top: l1.bottom
|
||||||
anchors.topMargin: GC.isMobile() ? 0: 15
|
anchors.topMargin: GC.isMobile() ? 0: 15
|
||||||
x: 30
|
x: 30
|
||||||
width: parent.width - 40
|
width: parent.width - 80
|
||||||
height: GC.isMobile() ? 0: 30
|
height: GC.isMobile() ? 0: 30
|
||||||
icon.source: "qrc:/images/settings.png"
|
icon.source: "qrc:/images/svg/settings_black_24dp.svg"
|
||||||
text: qsTr("App settings")
|
text: qsTr("App settings")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
UiLogic.goToPage(PageEnum.AppSettings)
|
UiLogic.goToPage(PageEnum.AppSettings)
|
||||||
|
@ -57,7 +58,7 @@ PageBase {
|
||||||
anchors.topMargin: 15
|
anchors.topMargin: 15
|
||||||
width: parent.width - 40
|
width: parent.width - 40
|
||||||
height: 30
|
height: 30
|
||||||
icon.source: "qrc:/images/settings.png"
|
icon.source: "qrc:/images/svg/settings_suggest_black_24dp.svg"
|
||||||
text: qsTr("Network settings")
|
text: qsTr("Network settings")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
UiLogic.goToPage(PageEnum.NetworkSettings)
|
UiLogic.goToPage(PageEnum.NetworkSettings)
|
||||||
|
@ -81,7 +82,7 @@ PageBase {
|
||||||
anchors.topMargin: 15
|
anchors.topMargin: 15
|
||||||
width: 330
|
width: 330
|
||||||
height: 30
|
height: 30
|
||||||
icon.source: "qrc:/images/server_settings.png"
|
icon.source: "qrc:/images/svg/vpn_key_black_24dp.svg"
|
||||||
text: qsTr("Server Settings")
|
text: qsTr("Server Settings")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
GeneralSettingsLogic.onPushButtonGeneralSettingsServerSettingsClicked()
|
GeneralSettingsLogic.onPushButtonGeneralSettingsServerSettingsClicked()
|
||||||
|
@ -105,7 +106,7 @@ PageBase {
|
||||||
anchors.topMargin: 15
|
anchors.topMargin: 15
|
||||||
width: 330
|
width: 330
|
||||||
height: 30
|
height: 30
|
||||||
icon.source: "qrc:/images/share.png"
|
icon.source: "qrc:/images/svg/share_black_24dp.svg"
|
||||||
text: qsTr("Share connection")
|
text: qsTr("Share connection")
|
||||||
enabled: GeneralSettingsLogic.pushButtonGeneralSettingsShareConnectionEnable
|
enabled: GeneralSettingsLogic.pushButtonGeneralSettingsShareConnectionEnable
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
@ -130,7 +131,7 @@ PageBase {
|
||||||
anchors.topMargin: 15
|
anchors.topMargin: 15
|
||||||
width: 330
|
width: 330
|
||||||
height: 30
|
height: 30
|
||||||
icon.source: "qrc:/images/server_settings.png"
|
icon.source: "qrc:/images/svg/format_list_bulleted_black_24dp.svg"
|
||||||
text: qsTr("Servers")
|
text: qsTr("Servers")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
UiLogic.goToPage(PageEnum.ServersList)
|
UiLogic.goToPage(PageEnum.ServersList)
|
||||||
|
@ -154,7 +155,7 @@ PageBase {
|
||||||
anchors.topMargin: 15
|
anchors.topMargin: 15
|
||||||
width: 330
|
width: 330
|
||||||
height: 30
|
height: 30
|
||||||
icon.source: "qrc:/images/plus.png"
|
icon.source: "qrc:/images/svg/control_point_black_24dp.svg"
|
||||||
text: qsTr("Add server")
|
text: qsTr("Add server")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
UiLogic.goToPage(PageEnum.Start)
|
UiLogic.goToPage(PageEnum.Start)
|
||||||
|
@ -178,7 +179,7 @@ PageBase {
|
||||||
anchors.bottomMargin: 20
|
anchors.bottomMargin: 20
|
||||||
width: 330
|
width: 330
|
||||||
height: 30
|
height: 30
|
||||||
icon.source: "qrc:/images/settings.png"
|
icon.source: "qrc:/images/svg/logout_black_24dp.svg"
|
||||||
text: qsTr("Exit")
|
text: qsTr("Exit")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Qt.quit()
|
Qt.quit()
|
||||||
|
|
|
@ -17,17 +17,42 @@ PageBase {
|
||||||
id: caption
|
id: caption
|
||||||
text: qsTr("DNS Servers")
|
text: qsTr("DNS Servers")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CheckBoxType {
|
||||||
|
id: cb_amnezia_dns
|
||||||
|
anchors.top: caption.bottom
|
||||||
|
x: 30
|
||||||
|
width: parent.width - 60
|
||||||
|
text: qsTr("Use AmneziaDNS service (recommended)")
|
||||||
|
checked: NetworkSettingsLogic.checkBoxUseAmneziaDnsChecked
|
||||||
|
onCheckedChanged: {
|
||||||
|
NetworkSettingsLogic.checkBoxUseAmneziaDnsChecked = checked
|
||||||
|
NetworkSettingsLogic.onCheckBoxUseAmneziaDnsToggled(checked)
|
||||||
|
UiLogic.onUpdateAllPages()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
id: lb_amnezia_dns
|
||||||
|
x: 30
|
||||||
|
anchors.top: cb_amnezia_dns.bottom
|
||||||
|
width: parent.width - 60
|
||||||
|
text: qsTr("Use AmneziaDNS container on your server, when it installed.\n
|
||||||
|
Your AmneziaDNS server available only when it installed and VPN connected, it has internal IP address 172.29.172.254\n
|
||||||
|
If AmneziaDNS service is not installed on the same server, or this option is unchecked, the following DNS servers will be used:")
|
||||||
|
}
|
||||||
|
|
||||||
LabelType {
|
LabelType {
|
||||||
id: l1
|
id: l1
|
||||||
x: 40
|
x: 30
|
||||||
anchors.top: caption.bottom
|
anchors.top: lb_amnezia_dns.bottom
|
||||||
width: parent.width - 40
|
width: parent.width - 30
|
||||||
height: 21
|
height: 21
|
||||||
text: qsTr("Primary DNS server")
|
text: qsTr("Primary DNS server")
|
||||||
}
|
}
|
||||||
TextFieldType {
|
TextFieldType {
|
||||||
id: dns1
|
id: dns1
|
||||||
x: 40
|
x: 30
|
||||||
anchors.top: l1.bottom
|
anchors.top: l1.bottom
|
||||||
width: parent.width - 90
|
width: parent.width - 90
|
||||||
height: 40
|
height: 40
|
||||||
|
@ -35,36 +60,38 @@ PageBase {
|
||||||
onEditingFinished: {
|
onEditingFinished: {
|
||||||
NetworkSettingsLogic.lineEditDns1Text = text
|
NetworkSettingsLogic.lineEditDns1Text = text
|
||||||
NetworkSettingsLogic.onLineEditDns1EditFinished(text)
|
NetworkSettingsLogic.onLineEditDns1EditFinished(text)
|
||||||
|
UiLogic.onUpdateAllPages()
|
||||||
}
|
}
|
||||||
validator: RegExpValidator {
|
validator: RegExpValidator {
|
||||||
regExp: NetworkSettingsLogic.ipAddressRegex
|
regExp: NetworkSettingsLogic.ipAddressRegex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImageButtonType {
|
SvgButtonType {
|
||||||
id: resetDNS1
|
id: resetDNS1
|
||||||
anchors. left: dns1.right
|
anchors. left: dns1.right
|
||||||
anchors.leftMargin: 10
|
anchors.leftMargin: 10
|
||||||
anchors.verticalCenter: dns1.verticalCenter
|
anchors.verticalCenter: dns1.verticalCenter
|
||||||
width: 24
|
width: 24
|
||||||
height: 24
|
height: 24
|
||||||
icon.source: "qrc:/images/reload.png"
|
icon.source: "qrc:/images/svg/refresh_black_24dp.svg"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
NetworkSettingsLogic.onPushButtonResetDns1Clicked()
|
NetworkSettingsLogic.onPushButtonResetDns1Clicked()
|
||||||
|
UiLogic.onUpdateAllPages()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelType {
|
LabelType {
|
||||||
id: l2
|
id: l2
|
||||||
x: 40
|
x: 30
|
||||||
anchors.top: dns1.bottom
|
anchors.top: dns1.bottom
|
||||||
anchors.topMargin: 20
|
anchors.topMargin: 20
|
||||||
width: parent.width - 40
|
width: parent.width - 60
|
||||||
height: 21
|
height: 21
|
||||||
text: qsTr("Secondray DNS server")
|
text: qsTr("Secondray DNS server")
|
||||||
}
|
}
|
||||||
TextFieldType {
|
TextFieldType {
|
||||||
id: dns2
|
id: dns2
|
||||||
x: 40
|
x: 30
|
||||||
anchors.top: l2.bottom
|
anchors.top: l2.bottom
|
||||||
width: parent.width - 90
|
width: parent.width - 90
|
||||||
height: 40
|
height: 40
|
||||||
|
@ -72,21 +99,23 @@ PageBase {
|
||||||
onEditingFinished: {
|
onEditingFinished: {
|
||||||
NetworkSettingsLogic.lineEditDns2Text = text
|
NetworkSettingsLogic.lineEditDns2Text = text
|
||||||
NetworkSettingsLogic.onLineEditDns2EditFinished(text)
|
NetworkSettingsLogic.onLineEditDns2EditFinished(text)
|
||||||
|
UiLogic.onUpdateAllPages()
|
||||||
}
|
}
|
||||||
validator: RegExpValidator {
|
validator: RegExpValidator {
|
||||||
regExp: NetworkSettingsLogic.ipAddressRegex
|
regExp: NetworkSettingsLogic.ipAddressRegex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImageButtonType {
|
SvgButtonType {
|
||||||
id: resetDNS2
|
id: resetDNS2
|
||||||
anchors. left: dns2.right
|
anchors. left: dns2.right
|
||||||
anchors.leftMargin: 10
|
anchors.leftMargin: 10
|
||||||
anchors.verticalCenter: dns2.verticalCenter
|
anchors.verticalCenter: dns2.verticalCenter
|
||||||
width: 24
|
width: 24
|
||||||
height: 24
|
height: 24
|
||||||
icon.source: "qrc:/images/reload.png"
|
icon.source: "qrc:/images/svg/refresh_black_24dp.svg"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
NetworkSettingsLogic.onPushButtonResetDns2Clicked()
|
NetworkSettingsLogic.onPushButtonResetDns2Clicked()
|
||||||
|
UiLogic.onUpdateAllPages()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,10 @@ PageBase {
|
||||||
|
|
||||||
SelectContainer {
|
SelectContainer {
|
||||||
id: container_selector
|
id: container_selector
|
||||||
|
onAboutToHide: {
|
||||||
|
pageLoader.focus = true
|
||||||
|
}
|
||||||
|
|
||||||
onContainerSelected: {
|
onContainerSelected: {
|
||||||
var containerProto = ContainerProps.defaultProtocol(c_index)
|
var containerProto = ContainerProps.defaultProtocol(c_index)
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,10 @@ PageBase {
|
||||||
SelectContainer {
|
SelectContainer {
|
||||||
id: container_selector
|
id: container_selector
|
||||||
|
|
||||||
|
onAboutToHide: {
|
||||||
|
pageLoader.focus = true
|
||||||
|
}
|
||||||
|
|
||||||
onContainerSelected: {
|
onContainerSelected: {
|
||||||
var containerProto = ContainerProps.defaultProtocol(c_index)
|
var containerProto = ContainerProps.defaultProtocol(c_index)
|
||||||
|
|
||||||
|
|
|
@ -19,17 +19,20 @@ PageBase {
|
||||||
text: qsTr("Servers list")
|
text: qsTr("Servers list")
|
||||||
width: undefined
|
width: undefined
|
||||||
}
|
}
|
||||||
ImageButtonType {
|
|
||||||
|
SvgButtonType {
|
||||||
anchors.bottom: caption.bottom
|
anchors.bottom: caption.bottom
|
||||||
anchors.leftMargin: 10
|
anchors.leftMargin: 10
|
||||||
anchors.left: caption.right
|
anchors.left: caption.right
|
||||||
width: 24
|
width: 24
|
||||||
height: 24
|
height: 24
|
||||||
icon.source: "qrc:/images/plus.png"
|
|
||||||
|
icon.source: "qrc:/images/svg/control_point_black_24dp.svg"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
UiLogic.goToPage(PageEnum.Start);
|
UiLogic.goToPage(PageEnum.Start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: listWidget_servers
|
id: listWidget_servers
|
||||||
x: 20
|
x: 20
|
||||||
|
@ -124,13 +127,13 @@ PageBase {
|
||||||
checked: is_default
|
checked: is_default
|
||||||
enabled: !is_default
|
enabled: !is_default
|
||||||
}
|
}
|
||||||
ImageButtonType {
|
SvgButtonType {
|
||||||
id: pushButtonSetting
|
id: pushButtonSetting
|
||||||
x: parent.width - 70
|
x: parent.width - 70
|
||||||
y: 15
|
y: 15
|
||||||
width: 30
|
width: 30
|
||||||
height: 30
|
height: 30
|
||||||
icon.source: "qrc:/images/settings.png"
|
icon.source: "qrc:/images/svg/settings_black_24dp.svg"
|
||||||
opacity: 0
|
opacity: 0
|
||||||
|
|
||||||
OpacityAnimator {
|
OpacityAnimator {
|
||||||
|
|
|
@ -28,7 +28,16 @@ PageBase {
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
text: ServerSettingsLogic.labelCurrentVpnProtocolText
|
text: ServerSettingsLogic.labelCurrentVpnProtocolText
|
||||||
}
|
}
|
||||||
LabelType {
|
// LabelType {
|
||||||
|
// anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
// y: 120
|
||||||
|
// width: 341
|
||||||
|
// height: 31
|
||||||
|
// font.pixelSize: 20
|
||||||
|
// horizontalAlignment: Text.AlignHCenter
|
||||||
|
// text: ServerSettingsLogic.labelServerText
|
||||||
|
// }
|
||||||
|
TextFieldType {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
y: 120
|
y: 120
|
||||||
width: 341
|
width: 341
|
||||||
|
@ -36,7 +45,10 @@ PageBase {
|
||||||
font.pixelSize: 20
|
font.pixelSize: 20
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
text: ServerSettingsLogic.labelServerText
|
text: ServerSettingsLogic.labelServerText
|
||||||
|
readOnly: true
|
||||||
|
background: Item {}
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelType {
|
LabelType {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
y: 530
|
y: 530
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
import PageEnum 1.0
|
import PageEnum 1.0
|
||||||
import "./"
|
import "./"
|
||||||
import "../Controls"
|
import "../Controls"
|
||||||
|
@ -14,93 +15,99 @@ PageBase {
|
||||||
id: back_from_setup_wizard
|
id: back_from_setup_wizard
|
||||||
}
|
}
|
||||||
Caption {
|
Caption {
|
||||||
|
id: caption
|
||||||
text: qsTr("Setup your server to use VPN")
|
text: qsTr("Setup your server to use VPN")
|
||||||
}
|
}
|
||||||
Item {
|
|
||||||
x: 10
|
Flickable {
|
||||||
y: 70
|
id: fl
|
||||||
width: 361
|
width: root.width
|
||||||
height: 561
|
anchors.top: caption.bottom
|
||||||
LabelType {
|
anchors.topMargin: 20
|
||||||
horizontalAlignment: Text.AlignLeft
|
anchors.bottom: root.bottom
|
||||||
verticalAlignment: Text.AlignTop
|
anchors.bottomMargin: 20
|
||||||
text: qsTr("I'm living in country with high censorship level. Many of foreign web sites and VPNs blocked by my government. I want to setup reliable VPN, which is invisible for government.")
|
anchors.left: root.left
|
||||||
wrapMode: Text.Wrap
|
anchors.leftMargin: 30
|
||||||
x: 30
|
anchors.right: root.right
|
||||||
y: 40
|
anchors.rightMargin: 30
|
||||||
width: 321
|
|
||||||
height: 121
|
contentHeight: content.height
|
||||||
}
|
clip: true
|
||||||
LabelType {
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
ColumnLayout {
|
||||||
verticalAlignment: Text.AlignTop
|
id: content
|
||||||
text: qsTr("I'm living in country with medium censorship level. Some web sites blocked by my government, but VPNs are not blocked at all. I want to setup flexible solution.")
|
enabled: logic.pageEnabled
|
||||||
wrapMode: Text.Wrap
|
anchors.top: parent.top
|
||||||
x: 30
|
anchors.left: parent.left
|
||||||
y: 210
|
anchors.right: parent.right
|
||||||
width: 321
|
|
||||||
height: 121
|
RadioButtonType {
|
||||||
}
|
id: radioButton_setup_wizard_high
|
||||||
LabelType {
|
Layout.fillWidth: true
|
||||||
horizontalAlignment: Text.AlignLeft
|
text: qsTr("High censorship level")
|
||||||
verticalAlignment: Text.AlignTop
|
checked: WizardLogic.radioButtonHighChecked
|
||||||
text: qsTr("I just want to improve my privacy in internet.")
|
onCheckedChanged: {
|
||||||
wrapMode: Text.Wrap
|
WizardLogic.radioButtonHighChecked = checked
|
||||||
x: 30
|
|
||||||
y: 360
|
|
||||||
width: 321
|
|
||||||
height: 121
|
|
||||||
}
|
|
||||||
BlueButtonType {
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
y: 490
|
|
||||||
width: 321
|
|
||||||
height: 40
|
|
||||||
text: qsTr("Next")
|
|
||||||
onClicked: {
|
|
||||||
if (radioButton_setup_wizard_high.checked) {
|
|
||||||
UiLogic.goToPage(PageEnum.WizardHigh);
|
|
||||||
} else if (radioButton_setup_wizard_medium.checked) {
|
|
||||||
UiLogic.goToPage(PageEnum.WizardMedium);
|
|
||||||
} else if (radioButton_setup_wizard_low.checked) {
|
|
||||||
UiLogic.goToPage(PageEnum.WizardLow);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
LabelType {
|
||||||
RadioButtonType {
|
Layout.fillWidth: true
|
||||||
id: radioButton_setup_wizard_high
|
Layout.leftMargin: 25
|
||||||
x: 10
|
verticalAlignment: Text.AlignTop
|
||||||
y: 10
|
text: qsTr("I'm living in a country with a high censorship level. Many of the foreign websites and VPNs are blocked by my government. I want to setup a reliable VPN, which can not be detected by my internet provider and my government.
|
||||||
width: 331
|
OpenVPN and ShadowSocks over Cloak (VPN obfuscation) profiles will be installed.\n")
|
||||||
height: 25
|
|
||||||
text: qsTr("High censorship level")
|
|
||||||
checked: WizardLogic.radioButtonHighChecked
|
|
||||||
onCheckedChanged: {
|
|
||||||
WizardLogic.radioButtonHighChecked = checked
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
RadioButtonType {
|
|
||||||
id: radioButton_setup_wizard_medium
|
RadioButtonType {
|
||||||
x: 10
|
id: radioButton_setup_wizard_medium
|
||||||
y: 330
|
Layout.fillWidth: true
|
||||||
width: 331
|
text: qsTr("Medium censorship level")
|
||||||
height: 25
|
checked: WizardLogic.radioButtonMediumChecked
|
||||||
text: qsTr("Low censorship level")
|
onCheckedChanged: {
|
||||||
checked: WizardLogic.radioButtonLowChecked
|
WizardLogic.radioButtonMediumChecked = checked
|
||||||
onCheckedChanged: {
|
}
|
||||||
WizardLogic.radioButtonLowChecked = checked
|
|
||||||
}
|
}
|
||||||
}
|
LabelType {
|
||||||
RadioButtonType {
|
Layout.fillWidth: true
|
||||||
id: radioButton_setup_wizard_low
|
Layout.leftMargin: 25
|
||||||
x: 10
|
verticalAlignment: Text.AlignTop
|
||||||
y: 180
|
text: qsTr("I'm living in a country with a medium censorship level. Some websites are blocked by my government, but VPNs are not blocked at all. I want to setup a flexible solution.
|
||||||
width: 331
|
OpenVPN over ShadowSocks profile will be installed.\n")
|
||||||
height: 25
|
}
|
||||||
text: qsTr("Medium censorship level")
|
|
||||||
checked: WizardLogic.radioButtonMediumChecked
|
|
||||||
onCheckedChanged: {
|
RadioButtonType {
|
||||||
WizardLogic.radioButtonMediumChecked = checked
|
id: radioButton_setup_wizard_low
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Low censorship level")
|
||||||
|
checked: WizardLogic.radioButtonLowChecked
|
||||||
|
onCheckedChanged: {
|
||||||
|
WizardLogic.radioButtonLowChecked = checked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LabelType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 25
|
||||||
|
verticalAlignment: Text.AlignTop
|
||||||
|
text: qsTr("I want to improve my privacy on the internet.
|
||||||
|
OpenVPN profile will be installed.\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BlueButtonType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 41
|
||||||
|
text: qsTr("Next")
|
||||||
|
onClicked: {
|
||||||
|
if (radioButton_setup_wizard_high.checked) {
|
||||||
|
UiLogic.goToPage(PageEnum.WizardHigh, false);
|
||||||
|
} else if (radioButton_setup_wizard_medium.checked) {
|
||||||
|
UiLogic.goToPage(PageEnum.WizardMedium, false);
|
||||||
|
} else if (radioButton_setup_wizard_low.checked) {
|
||||||
|
UiLogic.goToPage(PageEnum.WizardLow, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
import PageEnum 1.0
|
import PageEnum 1.0
|
||||||
import "./"
|
import "./"
|
||||||
import "../Controls"
|
import "../Controls"
|
||||||
|
@ -14,69 +15,90 @@ PageBase {
|
||||||
id: back_from_setup_wizard
|
id: back_from_setup_wizard
|
||||||
}
|
}
|
||||||
Caption {
|
Caption {
|
||||||
|
id: caption
|
||||||
text: qsTr("Setup Wizard")
|
text: qsTr("Setup Wizard")
|
||||||
}
|
}
|
||||||
Item {
|
|
||||||
x: 10
|
Flickable {
|
||||||
y: 70
|
id: fl
|
||||||
width: 361
|
width: root.width
|
||||||
height: 561
|
anchors.top: caption.bottom
|
||||||
LabelType {
|
anchors.topMargin: 20
|
||||||
x: 30
|
anchors.bottom: root.bottom
|
||||||
y: 10
|
anchors.bottomMargin: 20
|
||||||
width: 321
|
anchors.left: root.left
|
||||||
height: 321
|
anchors.leftMargin: 30
|
||||||
text: qsTr("AmneziaVPN will install VPN protocol which is not visible for your internet provider and government firewall. Your VPN connection will be detected by your provider as regular web traffic to particular web site.\n\nYou SHOULD set this web site address to some foreign web site which is updatesnot blocked by your internet provider. Other words you need to type below some foreign web site address which is accessible without VPN.\n\nPlease note, this protocol still does not support export connection profile to mobile devices. Keep for updates.")
|
anchors.right: root.right
|
||||||
}
|
anchors.rightMargin: 30
|
||||||
LabelType {
|
|
||||||
x: 30
|
contentHeight: content.height
|
||||||
y: 400
|
clip: true
|
||||||
width: 321
|
|
||||||
height: 71
|
ColumnLayout {
|
||||||
text: qsTr("OpenVPN over Cloak (VPN obfuscation) profile will be installed")
|
id: content
|
||||||
}
|
enabled: logic.pageEnabled
|
||||||
LabelType {
|
anchors.top: parent.top
|
||||||
x: 30
|
anchors.left: parent.left
|
||||||
y: 330
|
anchors.right: parent.right
|
||||||
width: 291
|
|
||||||
height: 21
|
LabelType {
|
||||||
text: qsTr("Type web site address for mask")
|
Layout.fillWidth: true
|
||||||
}
|
verticalAlignment: Text.AlignTop
|
||||||
TextFieldType {
|
text: qsTr("AmneziaVPN will install a VPN protocol which is not visible to your internet provider and government firewall. Your VPN connection will be seen by your internet provider as regular web traffic to a particular website.
|
||||||
id: website_masking
|
|
||||||
x: 30
|
You SHOULD set this website address to some foreign website which is not blocked by your internet provider. In other words, you need to type some foreign website address which is accessible to you without a VPN.")
|
||||||
y: 360
|
}
|
||||||
width: 301
|
|
||||||
height: 41
|
LabelType {
|
||||||
text: WizardLogic.lineEditHighWebsiteMaskingText
|
Layout.fillWidth: true
|
||||||
onEditingFinished: {
|
Layout.topMargin: 15
|
||||||
let _text = website_masking.text
|
verticalAlignment: Text.AlignTop
|
||||||
_text.replace("http://", "");
|
text: qsTr("Type another web site address for masking or keep it by default. Your internet provider will think you working on this web site when you connected to VPN.")
|
||||||
_text.replace("https://", "");
|
}
|
||||||
if (!_text) {
|
|
||||||
return
|
TextFieldType {
|
||||||
|
id: website_masking
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: WizardLogic.lineEditHighWebsiteMaskingText
|
||||||
|
onEditingFinished: {
|
||||||
|
let _text = website_masking.text
|
||||||
|
_text.replace("http://", "");
|
||||||
|
_text.replace("https://", "");
|
||||||
|
if (!_text) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_text = _text.split("/").first();
|
||||||
|
WizardLogic.lineEditHighWebsiteMaskingText = _text
|
||||||
}
|
}
|
||||||
_text = _text.split("/").first();
|
onAccepted: {
|
||||||
WizardLogic.lineEditHighWebsiteMaskingText = _text
|
next_button.clicked()
|
||||||
}
|
|
||||||
onAccepted: {
|
|
||||||
next_button.clicked()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BlueButtonType {
|
|
||||||
id: next_button
|
|
||||||
x: 30
|
|
||||||
y: 490
|
|
||||||
width: 301
|
|
||||||
height: 40
|
|
||||||
text: qsTr("Next")
|
|
||||||
onClicked: {
|
|
||||||
let domain = website_masking.text;
|
|
||||||
if (!domain || !domain.includes(".")) {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
UiLogic.goToPage(PageEnum.WizardVpnMode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 15
|
||||||
|
verticalAlignment: Text.AlignTop
|
||||||
|
text: qsTr("OpenVPN and ShadowSocks over Cloak (VPN obfuscation) profiles will be installed.
|
||||||
|
|
||||||
|
This protocol support exporting connection profiles to mobile devices by exporting ShadowSocks and Cloak configs (you should launch the 3rd party open source VPN client - ShadowSocks VPN and install Cloak plugin).")
|
||||||
|
}
|
||||||
|
BlueButtonType {
|
||||||
|
id: next_button
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 15
|
||||||
|
Layout.preferredHeight: 41
|
||||||
|
text: qsTr("Next")
|
||||||
|
onClicked: {
|
||||||
|
let domain = website_masking.text;
|
||||||
|
if (!domain || !domain.includes(".")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
UiLogic.goToPage(PageEnum.WizardVpnMode, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
import PageEnum 1.0
|
import PageEnum 1.0
|
||||||
import "./"
|
import "./"
|
||||||
import "../Controls"
|
import "../Controls"
|
||||||
|
@ -14,38 +15,56 @@ PageBase {
|
||||||
id: back_from_setup_wizard
|
id: back_from_setup_wizard
|
||||||
}
|
}
|
||||||
Caption {
|
Caption {
|
||||||
|
id: caption
|
||||||
text: qsTr("Setup Wizard")
|
text: qsTr("Setup Wizard")
|
||||||
}
|
}
|
||||||
Item {
|
|
||||||
x: 10
|
Flickable {
|
||||||
y: 70
|
id: fl
|
||||||
width: 361
|
width: root.width
|
||||||
height: 561
|
anchors.top: caption.bottom
|
||||||
LabelType {
|
anchors.topMargin: 20
|
||||||
x: 30
|
anchors.bottom: root.bottom
|
||||||
y: 10
|
anchors.bottomMargin: 20
|
||||||
width: 321
|
anchors.left: root.left
|
||||||
height: 341
|
anchors.leftMargin: 30
|
||||||
verticalAlignment: Text.AlignTop
|
anchors.right: root.right
|
||||||
text: qsTr('AmneziaVPN will install OpenVPN protocol with public/private key pairs generated on server and client sides. You can also configure connection on your mobile device by copying exported ".ovpn" file to your device and setting up official OpenVPN client. We recommend do not use messengers for sending connection profile - it contains VPN private keys.')
|
anchors.rightMargin: 30
|
||||||
}
|
|
||||||
LabelType {
|
contentHeight: content.height
|
||||||
x: 30
|
clip: true
|
||||||
y: 400
|
|
||||||
width: 321
|
ColumnLayout {
|
||||||
height: 71
|
id: content
|
||||||
text: qsTr('OpenVPN profile will be installed')
|
enabled: logic.pageEnabled
|
||||||
verticalAlignment: Text.AlignBottom
|
anchors.top: parent.top
|
||||||
}
|
anchors.left: parent.left
|
||||||
BlueButtonType {
|
anchors.right: parent.right
|
||||||
id: next_button
|
|
||||||
x: 30
|
LabelType {
|
||||||
y: 490
|
Layout.fillWidth: true
|
||||||
width: 301
|
verticalAlignment: Text.AlignTop
|
||||||
height: 40
|
text: qsTr('AmneziaVPN will install the OpenVPN protocol with public/private key pairs generated on both server and client sides.
|
||||||
text: qsTr("Start configuring")
|
|
||||||
onClicked: {
|
You can also configure the connection on your mobile device by copying the exported ".ovpn" file to your device, and setting up the official OpenVPN client.
|
||||||
WizardLogic.onPushButtonLowFinishClicked()
|
|
||||||
|
We recommend not to use messaging applications for sending the connection profile - it contains VPN private keys.')
|
||||||
|
}
|
||||||
|
LabelType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 15
|
||||||
|
text: qsTr('OpenVPN profile will be installed')
|
||||||
|
verticalAlignment: Text.AlignBottom
|
||||||
|
}
|
||||||
|
BlueButtonType {
|
||||||
|
id: next_button
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 15
|
||||||
|
Layout.preferredHeight: 41
|
||||||
|
text: qsTr("Start configuring")
|
||||||
|
onClicked: {
|
||||||
|
WizardLogic.onPushButtonLowFinishClicked()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
import PageEnum 1.0
|
import PageEnum 1.0
|
||||||
import "./"
|
import "./"
|
||||||
import "../Controls"
|
import "../Controls"
|
||||||
|
@ -14,38 +15,51 @@ PageBase {
|
||||||
id: back_from_setup_wizard
|
id: back_from_setup_wizard
|
||||||
}
|
}
|
||||||
Caption {
|
Caption {
|
||||||
|
id: caption
|
||||||
text: qsTr("Setup Wizard")
|
text: qsTr("Setup Wizard")
|
||||||
}
|
}
|
||||||
Item {
|
|
||||||
x: 10
|
Flickable {
|
||||||
y: 70
|
id: fl
|
||||||
width: 361
|
width: root.width
|
||||||
height: 561
|
anchors.top: caption.bottom
|
||||||
LabelType {
|
anchors.topMargin: 20
|
||||||
x: 30
|
anchors.bottom: root.bottom
|
||||||
y: 10
|
anchors.bottomMargin: 20
|
||||||
width: 321
|
anchors.left: root.left
|
||||||
height: 341
|
anchors.leftMargin: 30
|
||||||
verticalAlignment: Text.AlignTop
|
anchors.right: root.right
|
||||||
text: qsTr('AmneziaVPN will install VPN protocol which is difficult to detect by your internet provider and government firewall (but possible). In most cases, this is the most suitable protocol. This protocol is faster compared to the VPN protocols with "web traffic masking".\n\nThis protocol support export connection profile to mobile devices using QR code (you should launch 3rd party opensource VPN client - ShadowSocks VPN).')
|
anchors.rightMargin: 30
|
||||||
}
|
|
||||||
LabelType {
|
contentHeight: content.height
|
||||||
x: 30
|
clip: true
|
||||||
y: 400
|
|
||||||
width: 321
|
ColumnLayout {
|
||||||
height: 71
|
id: content
|
||||||
text: qsTr('OpenVPN over ShadowSocks profile will be installed')
|
enabled: logic.pageEnabled
|
||||||
verticalAlignment: Text.AlignBottom
|
anchors.top: parent.top
|
||||||
}
|
anchors.left: parent.left
|
||||||
BlueButtonType {
|
anchors.right: parent.right
|
||||||
id: next_button
|
|
||||||
x: 30
|
LabelType {
|
||||||
y: 490
|
Layout.fillWidth: true
|
||||||
width: 301
|
verticalAlignment: Text.AlignTop
|
||||||
height: 40
|
text: qsTr('AmneziaVPN will install a VPN protocol which is difficult to detect by your internet provider and government firewall (but possible). In most cases, this is the most suitable protocol. This protocol is faster compared to the VPN protocols with "VPN masking".\n\nThis protocol supports exporting connection profiles to mobile devices by using QR codes (you should launch the 3rd party open source VPN client - ShadowSocks VPN).')
|
||||||
text: qsTr("Next")
|
}
|
||||||
onClicked: {
|
LabelType {
|
||||||
UiLogic.goToPage(PageEnum.WizardVpnMode)
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 15
|
||||||
|
text: qsTr('OpenVPN over ShadowSocks profile will be installed')
|
||||||
|
}
|
||||||
|
BlueButtonType {
|
||||||
|
id: next_button
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 15
|
||||||
|
Layout.preferredHeight: 41
|
||||||
|
text: qsTr("Next")
|
||||||
|
onClicked: {
|
||||||
|
UiLogic.goToPage(PageEnum.WizardVpnMode, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
import PageEnum 1.0
|
import PageEnum 1.0
|
||||||
import "./"
|
import "./"
|
||||||
import "../Controls"
|
import "../Controls"
|
||||||
|
@ -14,40 +15,57 @@ PageBase {
|
||||||
id: back_from_setup_wizard
|
id: back_from_setup_wizard
|
||||||
}
|
}
|
||||||
Caption {
|
Caption {
|
||||||
|
id: caption
|
||||||
text: qsTr("Setup Wizard")
|
text: qsTr("Setup Wizard")
|
||||||
}
|
}
|
||||||
Item {
|
|
||||||
x: 10
|
Flickable {
|
||||||
y: 70
|
id: fl
|
||||||
width: 361
|
width: root.width
|
||||||
height: 561
|
anchors.top: caption.bottom
|
||||||
CheckBoxType {
|
anchors.topMargin: 20
|
||||||
x: 30
|
anchors.bottom: root.bottom
|
||||||
y: 350
|
anchors.bottomMargin: 20
|
||||||
width: 301
|
anchors.left: root.left
|
||||||
height: 71
|
anchors.leftMargin: 30
|
||||||
text: qsTr('Turn on mode "VPN for selected sites"')
|
anchors.right: root.right
|
||||||
checked: WizardLogic.checkBoxVpnModeChecked
|
anchors.rightMargin: 30
|
||||||
onCheckedChanged: {
|
|
||||||
WizardLogic.checkBoxVpnModeChecked = checked
|
contentHeight: content.height
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
|
enabled: logic.pageEnabled
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
verticalAlignment: Text.AlignTop
|
||||||
|
text: qsTr('Optional.\n
|
||||||
|
You can enable VPN mode "For selected sites" and add blocked sites you need to visit manually. If you will choose this option, you will need add every bloked site you want to visit to the access list. You may switch between modes later.\n\nPlease note, you should add addresses to the list after VPN connection established. You may add any domain, URL or IP address, it will be resolved to IP address.')
|
||||||
}
|
}
|
||||||
}
|
|
||||||
LabelType {
|
CheckBoxType {
|
||||||
x: 30
|
Layout.fillWidth: true
|
||||||
y: 10
|
text: qsTr('Turn on mode "VPN for selected sites"')
|
||||||
width: 321
|
checked: WizardLogic.checkBoxVpnModeChecked
|
||||||
height: 341
|
onCheckedChanged: {
|
||||||
text: qsTr('Optional.\n\nWe recommend to enable VPN mode "For selected sites" and add blocked sites you need to visit manually. If you will choose this option, you will need add every bloked site you want to visit to the access list. You may switch between modes later.\n\nPlease note, you should add addresses to the list after VPN connection established. You may add any domain, URL or IP address, it will be resolved to IP address.')
|
WizardLogic.checkBoxVpnModeChecked = checked
|
||||||
}
|
}
|
||||||
BlueButtonType {
|
}
|
||||||
id: vpn_mode_finish
|
|
||||||
x: 30
|
BlueButtonType {
|
||||||
y: 490
|
id: vpn_mode_finish
|
||||||
width: 301
|
Layout.fillWidth: true
|
||||||
height: 40
|
Layout.topMargin: 15
|
||||||
text: qsTr("Start configuring")
|
Layout.preferredHeight: 41
|
||||||
onClicked: {
|
text: qsTr("Start configuring")
|
||||||
WizardLogic.onPushButtonVpnModeFinishClicked()
|
onClicked: {
|
||||||
|
WizardLogic.onPushButtonVpnModeFinishClicked()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQml.Models 2.15
|
||||||
import Qt.labs.platform 1.0
|
import Qt.labs.platform 1.0
|
||||||
import QtQuick.Dialogs 1.0
|
import QtQuick.Dialogs 1.0
|
||||||
import PageEnum 1.0
|
import PageEnum 1.0
|
||||||
|
@ -12,6 +13,8 @@ PageBase {
|
||||||
page: PageEnum.Sites
|
page: PageEnum.Sites
|
||||||
logic: SitesLogic
|
logic: SitesLogic
|
||||||
|
|
||||||
|
property int lastIndex: 0
|
||||||
|
|
||||||
BackButton {
|
BackButton {
|
||||||
id: back
|
id: back
|
||||||
}
|
}
|
||||||
|
@ -104,20 +107,32 @@ PageBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
DelegateModel {
|
||||||
id: tb
|
id: visualModel
|
||||||
x: 20
|
|
||||||
anchors.top: sites_add.bottom
|
|
||||||
anchors.topMargin: 10
|
|
||||||
width: parent.width - 40
|
|
||||||
anchors.bottom: sites_delete.top
|
|
||||||
anchors.bottomMargin: 10
|
|
||||||
spacing: 1
|
|
||||||
clip: true
|
|
||||||
property int currentRow: -1
|
|
||||||
model: SitesLogic.tableViewSitesModel
|
model: SitesLogic.tableViewSitesModel
|
||||||
|
groups: [
|
||||||
delegate: Item {
|
DelegateModelGroup {
|
||||||
|
id : delegateModelGroup
|
||||||
|
name: "multiSelect"
|
||||||
|
function removeAll(){
|
||||||
|
var count = delegateModelGroup.count;
|
||||||
|
if (count !== 0){
|
||||||
|
delegateModelGroup.remove(0,count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function selectAll(){
|
||||||
|
for(var i = 0; i < visualModel.count; i++){
|
||||||
|
visualModel.items.get(i).inMultiSelect = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
delegate: Rectangle {
|
||||||
|
id: item
|
||||||
|
focus: true
|
||||||
|
height: 25
|
||||||
|
width: root.width
|
||||||
|
color: item.DelegateModel.inMultiSelect ? '#63b4fb' : 'transparent'
|
||||||
implicitWidth: 170 * 2
|
implicitWidth: 170 * 2
|
||||||
implicitHeight: 30
|
implicitHeight: 30
|
||||||
Item {
|
Item {
|
||||||
|
@ -171,23 +186,130 @@ PageBase {
|
||||||
}
|
}
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
tb.currentRow = index
|
onClicked:{
|
||||||
|
tb.focus = true
|
||||||
|
if(mouse.button === Qt.RightButton){
|
||||||
|
//copyPasteMenu.popup()
|
||||||
|
console.log("RightButton")
|
||||||
|
}
|
||||||
|
if(mouse.button === Qt.LeftButton){
|
||||||
|
switch(mouse.modifiers){
|
||||||
|
case Qt.ControlModifier :
|
||||||
|
item.DelegateModel.inMultiSelect = !item.DelegateModel.inMultiSelect
|
||||||
|
break;
|
||||||
|
case Qt.ShiftModifier :
|
||||||
|
delegateModelGroup.removeAll();
|
||||||
|
var start = lastIndex <= index? lastIndex: index;
|
||||||
|
var end = lastIndex >= index? lastIndex: index;
|
||||||
|
for(var i = start;i <= end;i++){
|
||||||
|
visualModel.items.get(i).inMultiSelect = true
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
delegateModelGroup.removeAll();
|
||||||
|
item.DelegateModel.inMultiSelect = true
|
||||||
|
lastIndex = index
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: tb
|
||||||
|
x: 20
|
||||||
|
anchors.top: sites_add.bottom
|
||||||
|
anchors.topMargin: 10
|
||||||
|
width: parent.width - 40
|
||||||
|
anchors.bottom: sites_delete.top
|
||||||
|
anchors.bottomMargin: 10
|
||||||
|
spacing: 1
|
||||||
|
clip: true
|
||||||
|
focus: true
|
||||||
|
activeFocusOnTab: true
|
||||||
|
keyNavigationEnabled: true
|
||||||
|
property int currentRow: -1
|
||||||
|
model: visualModel
|
||||||
|
|
||||||
|
Keys.onPressed: {
|
||||||
|
if (event.key === Qt.Key_PageUp) {
|
||||||
|
let idx = tb.indexAt(1, tb.contentY)
|
||||||
|
tb.positionViewAtIndex(idx-20, ListView.Beginning)
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
else if (event.key === Qt.Key_PageDown) {
|
||||||
|
let idx = tb.indexAt(1, tb.contentY)
|
||||||
|
tb.positionViewAtIndex(idx+20, ListView.Beginning)
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
else if (event.key === Qt.Key_Home) {
|
||||||
|
tb.positionViewAtBeginning()
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
else if (event.key === Qt.Key_End) {
|
||||||
|
tb.positionViewAtEnd()
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
else if (event.key === Qt.Key_Delete) {
|
||||||
|
let items = []
|
||||||
|
for(let i = 0; i < visualModel.count; i++){
|
||||||
|
if (visualModel.items.get(i).inMultiSelect) items.push(i)
|
||||||
|
}
|
||||||
|
SitesLogic.onPushButtonSitesDeleteClicked(items)
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
else if (event.key === Qt.Key_A) {
|
||||||
|
delegateModelGroup.selectAll()
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
BlueButtonType {
|
BlueButtonType {
|
||||||
id: sites_delete
|
id: sites_delete
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: select_all.top
|
||||||
anchors.bottomMargin: 20
|
anchors.bottomMargin: 10
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
height: 31
|
height: 31
|
||||||
font.pixelSize: 16
|
font.pixelSize: 16
|
||||||
text: qsTr("Delete selected")
|
text: qsTr("Delete selected")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
SitesLogic.onPushButtonSitesDeleteClicked(tb.currentRow)
|
let items = []
|
||||||
|
for(let i = 0; i < visualModel.count; i++){
|
||||||
|
if (visualModel.items.get(i).inMultiSelect) items.push(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
SitesLogic.onPushButtonSitesDeleteClicked(items)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlueButtonType {
|
||||||
|
id: select_all
|
||||||
|
anchors.bottom: sites_export.top
|
||||||
|
anchors.bottomMargin: 10
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
height: 31
|
||||||
|
font.pixelSize: 16
|
||||||
|
text: qsTr("Select all")
|
||||||
|
onClicked: {
|
||||||
|
delegateModelGroup.selectAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlueButtonType {
|
||||||
|
id: sites_export
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 20
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
height: 31
|
||||||
|
font.pixelSize: 16
|
||||||
|
text: qsTr("Export all")
|
||||||
|
onClicked: {
|
||||||
|
SitesLogic.onPushButtonSitesExportClicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,8 +240,7 @@ PageBase {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
height: 71
|
height: 71
|
||||||
echoMode: TextInput.Password
|
font.pixelSize: 10
|
||||||
font.pixelSize: 9
|
|
||||||
verticalAlignment: Text.AlignTop
|
verticalAlignment: Text.AlignTop
|
||||||
text: StartPageLogic.textEditSshKeyText
|
text: StartPageLogic.textEditSshKeyText
|
||||||
onEditingFinished: {
|
onEditingFinished: {
|
||||||
|
@ -264,6 +263,7 @@ PageBase {
|
||||||
id: new_sever_connect
|
id: new_sever_connect
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.top: new_server_ssh_key.bottom
|
anchors.top: new_server_ssh_key.bottom
|
||||||
|
anchors.topMargin: 10
|
||||||
|
|
||||||
text: StartPageLogic.pushButtonConnectText
|
text: StartPageLogic.pushButtonConnectText
|
||||||
visible: StartPageLogic.pushButtonConnectVisible
|
visible: StartPageLogic.pushButtonConnectVisible
|
||||||
|
|
|
@ -22,7 +22,7 @@ PageBase {
|
||||||
|
|
||||||
LabelType {
|
LabelType {
|
||||||
x: 10
|
x: 10
|
||||||
y: 5
|
y: 10
|
||||||
width: 100
|
width: 100
|
||||||
height: 21
|
height: 21
|
||||||
text: VpnLogic.labelVersionText
|
text: VpnLogic.labelVersionText
|
||||||
|
@ -30,6 +30,33 @@ PageBase {
|
||||||
font.pixelSize: 12
|
font.pixelSize: 12
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BasicButtonType {
|
||||||
|
y: 10
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
height: 21
|
||||||
|
background: Item {}
|
||||||
|
|
||||||
|
|
||||||
|
contentItem: Text {
|
||||||
|
anchors.fill: parent
|
||||||
|
font.family: "Lato"
|
||||||
|
font.styleName: "normal"
|
||||||
|
font.pixelSize: 18
|
||||||
|
font.underline: true
|
||||||
|
|
||||||
|
text: qsTr("Donate")
|
||||||
|
color: "#D4D4D4"
|
||||||
|
|
||||||
|
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
UiLogic.goToPage(PageEnum.About)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImageButtonType {
|
ImageButtonType {
|
||||||
x: parent.width - 40
|
x: parent.width - 40
|
||||||
y: 10
|
y: 10
|
||||||
|
@ -108,7 +135,7 @@ PageBase {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
height: 21
|
height: 21
|
||||||
background: Item {}
|
background: Item {}
|
||||||
text: VpnLogic.labelCurrentServer
|
text: VpnLogic.labelCurrentServer + " →"
|
||||||
font.family: "Lato"
|
font.family: "Lato"
|
||||||
font.styleName: "normal"
|
font.styleName: "normal"
|
||||||
font.pixelSize: 16
|
font.pixelSize: 16
|
||||||
|
@ -129,14 +156,14 @@ PageBase {
|
||||||
LabelType {
|
LabelType {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
height: 21
|
height: 21
|
||||||
text: qsTr("Service") + ": "
|
text: qsTr("Proto") + ": "
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
height: 21
|
height: 21
|
||||||
background: Item {}
|
background: Item {}
|
||||||
text: VpnLogic.labelCurrentService
|
text: VpnLogic.labelCurrentService + " →"
|
||||||
font.family: "Lato"
|
font.family: "Lato"
|
||||||
font.styleName: "normal"
|
font.styleName: "normal"
|
||||||
font.pixelSize: 16
|
font.pixelSize: 16
|
||||||
|
@ -146,10 +173,46 @@ PageBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: layout3
|
||||||
|
anchors.top: layout2.bottom
|
||||||
|
anchors.topMargin: 5
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
height: 21
|
||||||
|
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
height: 21
|
||||||
|
text: qsTr("DNS") + ": "
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicButtonType {
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
height: 21
|
||||||
|
implicitWidth: implicitContentWidth > root.width * 0.6 ? root.width * 0.6 : implicitContentWidth + leftPadding + rightPadding
|
||||||
|
background: Item {}
|
||||||
|
text: VpnLogic.labelCurrentDns + " →"
|
||||||
|
font.family: "Lato"
|
||||||
|
font.styleName: "normal"
|
||||||
|
font.pixelSize: 16
|
||||||
|
onClicked: {
|
||||||
|
UiLogic.goToPage(PageEnum.NetworkSettings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SvgImageType {
|
||||||
|
svg.source: VpnLogic.amneziaDnsEnabled ? "qrc:/images/svg/gpp_good_black_24dp.svg" : "qrc:/images/svg/gpp_maybe_black_24dp.svg"
|
||||||
|
color: VpnLogic.amneziaDnsEnabled ? "#22aa33" : "orange"
|
||||||
|
width: 25
|
||||||
|
height: 25
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
LabelType {
|
LabelType {
|
||||||
id: error_text
|
id: error_text
|
||||||
anchors.top: layout2.bottom
|
anchors.top: layout3.bottom
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.topMargin: 20
|
anchors.topMargin: 20
|
||||||
width: parent.width - 20
|
width: parent.width - 20
|
||||||
|
@ -259,6 +322,7 @@ PageBase {
|
||||||
onClicked: VpnLogic.onRadioButtonVpnModeAllSitesClicked(true)
|
onClicked: VpnLogic.onRadioButtonVpnModeAllSitesClicked(true)
|
||||||
}
|
}
|
||||||
RadioButtonType {
|
RadioButtonType {
|
||||||
|
enabled: VpnLogic.isCustomRoutesSupported
|
||||||
x: 0
|
x: 0
|
||||||
y: 60
|
y: 60
|
||||||
width: 341
|
width: 341
|
||||||
|
@ -268,6 +332,7 @@ PageBase {
|
||||||
onClicked: VpnLogic.onRadioButtonVpnModeExceptSitesClicked(true)
|
onClicked: VpnLogic.onRadioButtonVpnModeExceptSitesClicked(true)
|
||||||
}
|
}
|
||||||
RadioButtonType {
|
RadioButtonType {
|
||||||
|
enabled: VpnLogic.isCustomRoutesSupported
|
||||||
x: 0
|
x: 0
|
||||||
y: 30
|
y: 30
|
||||||
width: 341
|
width: 341
|
||||||
|
|
|
@ -16,7 +16,7 @@ PageProtocolBase {
|
||||||
|
|
||||||
Caption {
|
Caption {
|
||||||
id: caption
|
id: caption
|
||||||
text: qsTr("SFTF settings")
|
text: qsTr("SFTP settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
@ -77,40 +77,42 @@ PageProtocolBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelType {
|
RichLabelType {
|
||||||
anchors.bottom: check_persist.top
|
anchors.bottom: check_persist.top
|
||||||
anchors.bottomMargin: 10
|
anchors.bottomMargin: 10
|
||||||
width: parent.width - 60
|
width: parent.width - 60
|
||||||
x: 30
|
x: 30
|
||||||
font.pixelSize: 14
|
font.pixelSize: 14
|
||||||
textFormat: Text.RichText
|
|
||||||
|
|
||||||
MouseArea {
|
readonly property string windows_text: "In order to mount remote SFTP folder as local drive, perform following steps:
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
||||||
acceptedButtons: Qt.NoButton
|
|
||||||
}
|
|
||||||
|
|
||||||
// text: "In order to mount remote SFTP folder as local drive, perform following steps:
|
|
||||||
//- Install the latest version of WinFsp [https://github.com/billziss-gh/winfsp/releases/latest].
|
|
||||||
//- Install the latest version of SSHFS-Win. Choose the x64 or x86 installer according to your computer's architecture [https://github.com/billziss-gh/sshfs-win/releases]"
|
|
||||||
onLinkActivated: Qt.openUrlExternally(link)
|
|
||||||
|
|
||||||
text:"In order to mount remote SFTP folder as local drive, perform following steps:
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Install the latest version of <a href=\"https://github.com/billziss-gh/winfsp/releases/latest\">WinFsp</a>.</li>
|
<li>Install the latest version of <a href=\"https://github.com/billziss-gh/winfsp/releases/latest\">WinFsp</a>.</li>
|
||||||
<li>Install the latest version of <a href=\"https://github.com/billziss-gh/sshfs-win/releases\">SSHFS-Win</a>. Choose the x64 or x86 installer according to your computer's architecture.</li>
|
<li>Install the latest version of <a href=\"https://github.com/billziss-gh/sshfs-win/releases\">SSHFS-Win</a>. Choose the x64 or x86 installer according to your computer's architecture.</li>
|
||||||
</ul>"
|
</ul>"
|
||||||
|
|
||||||
|
readonly property string macos_text: "In order to mount remote SFTP folder as local folder, perform following steps:
|
||||||
|
<ul>
|
||||||
|
<li>Install the latest version of <a href=\"https://osxfuse.github.io/\">macFUSE</a>.</li>
|
||||||
|
<li>Install the latest version of <a href=\"https://osxfuse.github.io/\">SSHFS</a>.</li>
|
||||||
|
</ul>"
|
||||||
|
|
||||||
|
text: {
|
||||||
|
if (Qt.platform.os == "windows") return windows_text
|
||||||
|
else if (Qt.platform.os == "osx") return macos_text
|
||||||
|
else if (Qt.platform.os == "linux") return ""
|
||||||
|
else return ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckBoxType {
|
CheckBoxType {
|
||||||
id: check_persist
|
id: check_persist
|
||||||
|
visible: false
|
||||||
anchors.bottom: pb_mount.top
|
anchors.bottom: pb_mount.top
|
||||||
anchors.bottomMargin: 10
|
anchors.bottomMargin: 10
|
||||||
x: 30
|
x: 30
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 21
|
height: 21
|
||||||
text: qsTr("Restore drive after restart")
|
text: qsTr("Restore drive when client starts")
|
||||||
checked: logic.checkBoxSftpRestoreChecked
|
checked: logic.checkBoxSftpRestoreChecked
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
logic.checkBoxSftpRestoreChecked = checked
|
logic.checkBoxSftpRestoreChecked = checked
|
||||||
|
@ -122,6 +124,7 @@ PageProtocolBase {
|
||||||
|
|
||||||
BlueButtonType {
|
BlueButtonType {
|
||||||
id: pb_mount
|
id: pb_mount
|
||||||
|
visible: GC.isDesktop()
|
||||||
enabled: logic.pushButtonSftpMountEnabled
|
enabled: logic.pushButtonSftpMountEnabled
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
|
|
@ -11,7 +11,6 @@ PageProtocolBase {
|
||||||
protocol: ProtocolEnum.ShadowSocks
|
protocol: ProtocolEnum.ShadowSocks
|
||||||
logic: UiLogic.protocolLogic(protocol)
|
logic: UiLogic.protocolLogic(protocol)
|
||||||
|
|
||||||
enabled: logic.pageEnabled
|
|
||||||
BackButton {
|
BackButton {
|
||||||
id: back
|
id: back
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import QtQuick 2.14
|
import QtQuick 2.14
|
||||||
import QtQuick.Window 2.14
|
import QtQuick.Window 2.14
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
import QtQuick.Controls.Material 2.12
|
import QtQuick.Controls.Material 2.12
|
||||||
import PageEnum 1.0
|
import PageEnum 1.0
|
||||||
import PageType 1.0
|
import PageType 1.0
|
||||||
|
@ -8,6 +9,7 @@ import Qt.labs.platform 1.1
|
||||||
import Qt.labs.folderlistmodel 2.12
|
import Qt.labs.folderlistmodel 2.12
|
||||||
import QtQuick.Dialogs 1.1
|
import QtQuick.Dialogs 1.1
|
||||||
import "./"
|
import "./"
|
||||||
|
import "Controls"
|
||||||
import "Pages"
|
import "Pages"
|
||||||
import "Pages/Protocols"
|
import "Pages/Protocols"
|
||||||
import "Pages/Share"
|
import "Pages/Share"
|
||||||
|
@ -22,6 +24,8 @@ Window {
|
||||||
visible: true
|
visible: true
|
||||||
width: GC.screenWidth
|
width: GC.screenWidth
|
||||||
height: GC.isDesktop() ? GC.screenHeight + titleBar.height : GC.screenHeight
|
height: GC.isDesktop() ? GC.screenHeight + titleBar.height : GC.screenHeight
|
||||||
|
minimumWidth: 360
|
||||||
|
minimumHeight: GC.isDesktop() ? 640 : 0
|
||||||
Keys.enabled: true
|
Keys.enabled: true
|
||||||
onClosing: {
|
onClosing: {
|
||||||
console.debug("QML onClosing signal")
|
console.debug("QML onClosing signal")
|
||||||
|
@ -38,7 +42,7 @@ Window {
|
||||||
else if (type === PageType.ShareProto) p_obj = sharePages[page]
|
else if (type === PageType.ShareProto) p_obj = sharePages[page]
|
||||||
else return
|
else return
|
||||||
|
|
||||||
console.debug("QML gotoPage " + type + " " + page + " " + p_obj)
|
//console.debug("QML gotoPage " + type + " " + page + " " + p_obj)
|
||||||
|
|
||||||
if (pageLoader.depth > 0) {
|
if (pageLoader.depth > 0) {
|
||||||
pageLoader.currentItem.deactivated()
|
pageLoader.currentItem.deactivated()
|
||||||
|
@ -118,7 +122,7 @@ Window {
|
||||||
focus: true
|
focus: true
|
||||||
|
|
||||||
onCurrentItemChanged: {
|
onCurrentItemChanged: {
|
||||||
console.debug("QML onCurrentItemChanged " + pageLoader.currentItem)
|
//console.debug("QML onCurrentItemChanged " + pageLoader.currentItem)
|
||||||
UiLogic.currentPageValue = currentItem.page
|
UiLogic.currentPageValue = currentItem.page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,15 +217,15 @@ Window {
|
||||||
Connections {
|
Connections {
|
||||||
target: UiLogic
|
target: UiLogic
|
||||||
function onGoToPage(page, reset, slide) {
|
function onGoToPage(page, reset, slide) {
|
||||||
console.debug("Qml Connections onGoToPage " + page);
|
//console.debug("Qml Connections onGoToPage " + page);
|
||||||
root.gotoPage(PageType.Basic, page, reset, slide)
|
root.gotoPage(PageType.Basic, page, reset, slide)
|
||||||
}
|
}
|
||||||
function onGoToProtocolPage(protocol, reset, slide) {
|
function onGoToProtocolPage(protocol, reset, slide) {
|
||||||
console.debug("Qml Connections onGoToProtocolPage " + protocol);
|
//console.debug("Qml Connections onGoToProtocolPage " + protocol);
|
||||||
root.gotoPage(PageType.Proto, protocol, reset, slide)
|
root.gotoPage(PageType.Proto, protocol, reset, slide)
|
||||||
}
|
}
|
||||||
function onGoToShareProtocolPage(protocol, reset, slide) {
|
function onGoToShareProtocolPage(protocol, reset, slide) {
|
||||||
console.debug("Qml Connections onGoToShareProtocolPage " + protocol);
|
//console.debug("Qml Connections onGoToShareProtocolPage " + protocol);
|
||||||
root.gotoPage(PageType.ShareProto, protocol, reset, slide)
|
root.gotoPage(PageType.ShareProto, protocol, reset, slide)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,6 +253,9 @@ Window {
|
||||||
root.raise()
|
root.raise()
|
||||||
root.requestActivate()
|
root.requestActivate()
|
||||||
}
|
}
|
||||||
|
function onToggleLogPanel() {
|
||||||
|
drawer_log.visible = !drawer_log.visible
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageDialog {
|
MessageDialog {
|
||||||
|
@ -275,4 +282,114 @@ Window {
|
||||||
text: UiLogic.dialogConnectErrorText
|
text: UiLogic.dialogConnectErrorText
|
||||||
visible: false
|
visible: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Drawer {
|
||||||
|
id: drawer_log
|
||||||
|
|
||||||
|
z: -3
|
||||||
|
y: 0
|
||||||
|
x: 0
|
||||||
|
edge: Qt.BottomEdge
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height * 0.85
|
||||||
|
|
||||||
|
modal: true
|
||||||
|
//interactive: activeFocus
|
||||||
|
|
||||||
|
onAboutToHide: {
|
||||||
|
pageLoader.focus = true
|
||||||
|
}
|
||||||
|
onAboutToShow: {
|
||||||
|
tfSshLog.focus = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: itemLog
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Keys.onPressed: {
|
||||||
|
UiLogic.keyPressEvent(event.key)
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
RadioButtonType {
|
||||||
|
id: rbSshLog
|
||||||
|
focus: false
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 2
|
||||||
|
|
||||||
|
height: 25
|
||||||
|
text: qsTr("Ssh log")
|
||||||
|
}
|
||||||
|
RadioButtonType {
|
||||||
|
id: rbAllLog
|
||||||
|
focus: false
|
||||||
|
checked: true
|
||||||
|
anchors.left: rbSshLog.right
|
||||||
|
anchors.bottom: rbSshLog.bottom
|
||||||
|
anchors.top: rbSshLog.top
|
||||||
|
height: rbSshLog.height
|
||||||
|
text: qsTr("App log")
|
||||||
|
}
|
||||||
|
CheckBoxType {
|
||||||
|
id: cbLogWrap
|
||||||
|
text: qsTr("Wrap words")
|
||||||
|
checked: true
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: rbAllLog.bottom
|
||||||
|
anchors.top: rbAllLog.top
|
||||||
|
height: 15
|
||||||
|
imageHeight: 15
|
||||||
|
imageWidth: 15
|
||||||
|
|
||||||
|
onCheckedChanged: {
|
||||||
|
tfSshLog
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextAreaType {
|
||||||
|
id: tfSshLog
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: rbSshLog.top
|
||||||
|
|
||||||
|
flickableDirection: Flickable.AutoFlickIfNeeded
|
||||||
|
|
||||||
|
textArea.readOnly: true
|
||||||
|
textArea.selectByMouse: true
|
||||||
|
|
||||||
|
textArea.verticalAlignment: Text.AlignTop
|
||||||
|
textArea.text: {
|
||||||
|
if (!drawer_log.visible) return ""
|
||||||
|
else if (rbSshLog.checked ) return Debug.sshLog
|
||||||
|
else return Debug.allLog
|
||||||
|
}
|
||||||
|
textArea.wrapMode: cbLogWrap.checked ? TextEdit.WordWrap: TextEdit.NoWrap
|
||||||
|
|
||||||
|
Keys.onPressed: {
|
||||||
|
UiLogic.keyPressEvent(event.key)
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
textArea.onTextChanged: {
|
||||||
|
textArea.cursorPosition = textArea.length-1
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: GC.isDesktop()
|
||||||
|
acceptedButtons: Qt.RightButton
|
||||||
|
onClicked: contextMenu.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextMenu {
|
||||||
|
id: contextMenu
|
||||||
|
textObj: tfSshLog.textArea
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,7 @@ UiLogic::~UiLogic()
|
||||||
{
|
{
|
||||||
emit hide();
|
emit hide();
|
||||||
|
|
||||||
|
#ifdef AMNEZIA_DESKTOP
|
||||||
if (m_vpnConnection->connectionState() != VpnProtocol::VpnConnectionState::Disconnected) {
|
if (m_vpnConnection->connectionState() != VpnProtocol::VpnConnectionState::Disconnected) {
|
||||||
m_vpnConnection->disconnectFromVpn();
|
m_vpnConnection->disconnectFromVpn();
|
||||||
for (int i = 0; i < 50; i++) {
|
for (int i = 0; i < 50; i++) {
|
||||||
|
@ -122,6 +123,7 @@ UiLogic::~UiLogic()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
m_vpnConnection->deleteLater();
|
m_vpnConnection->deleteLater();
|
||||||
m_vpnConnectionThread.quit();
|
m_vpnConnectionThread.quit();
|
||||||
|
@ -149,24 +151,6 @@ void UiLogic::initalizeUiLogic()
|
||||||
connect(m_notificationHandler, &NotificationHandler::connectRequested, vpnLogic(), &VpnLogic::onConnect);
|
connect(m_notificationHandler, &NotificationHandler::connectRequested, vpnLogic(), &VpnLogic::onConnect);
|
||||||
connect(m_notificationHandler, &NotificationHandler::disconnectRequested, vpnLogic(), &VpnLogic::onDisconnect);
|
connect(m_notificationHandler, &NotificationHandler::disconnectRequested, vpnLogic(), &VpnLogic::onDisconnect);
|
||||||
|
|
||||||
// if (QOperatingSystemVersion::current() <= QOperatingSystemVersion::Windows7) {
|
|
||||||
// needToHideCustomTitlebar = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
//#if defined Q_OS_MAC
|
|
||||||
// fixWidget(this);
|
|
||||||
// needToHideCustomTitlebar = true;
|
|
||||||
//#endif
|
|
||||||
|
|
||||||
// if (needToHideCustomTitlebar) {
|
|
||||||
// ui->widget_tittlebar->hide();
|
|
||||||
// resize(width(), 640);
|
|
||||||
// ui->stackedWidget_main->move(0,0);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Post initialization
|
|
||||||
//emit goToPage(Page::Start, true, false);
|
|
||||||
|
|
||||||
if (m_settings.serversCount() > 0) {
|
if (m_settings.serversCount() > 0) {
|
||||||
if (m_settings.defaultServerIndex() < 0) m_settings.setDefaultServer(0);
|
if (m_settings.defaultServerIndex() < 0) m_settings.setDefaultServer(0);
|
||||||
emit goToPage(Page::Vpn, true, false);
|
emit goToPage(Page::Vpn, true, false);
|
||||||
|
@ -176,37 +160,9 @@ void UiLogic::initalizeUiLogic()
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedServerIndex = m_settings.defaultServerIndex();
|
selectedServerIndex = m_settings.defaultServerIndex();
|
||||||
//goToPage(Page::ServerContainers, true, false);
|
|
||||||
//goToPage(Page::NewServerProtocols, true, false);
|
|
||||||
//onGotoProtocolPage(Proto::OpenVpn);
|
|
||||||
|
|
||||||
|
|
||||||
//ui->pushButton_general_settings_exit->hide();
|
|
||||||
|
|
||||||
|
|
||||||
qInfo().noquote() << QString("Started %1 version %2").arg(APPLICATION_NAME).arg(APP_VERSION);
|
qInfo().noquote() << QString("Started %1 version %2").arg(APPLICATION_NAME).arg(APP_VERSION);
|
||||||
qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName()).arg(QSysInfo::currentCpuArchitecture());
|
qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName()).arg(QSysInfo::currentCpuArchitecture());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
vpnLogic()->onConnectionStateChanged(VpnProtocol::Disconnected);
|
|
||||||
|
|
||||||
|
|
||||||
// m_ipAddressValidator.setRegExp(Utils::ipAddressRegExp());
|
|
||||||
// m_ipAddressPortValidator.setRegExp(Utils::ipAddressPortRegExp());
|
|
||||||
// m_ipNetwok24Validator.setRegExp(Utils::ipNetwork24RegExp());
|
|
||||||
// m_ipPortValidator.setRegExp(Utils::ipPortRegExp());
|
|
||||||
|
|
||||||
// ui->lineEdit_new_server_ip->setValidator(&m_ipAddressPortValidator);
|
|
||||||
// ui->lineEdit_network_settings_dns1->setValidator(&m_ipAddressValidator);
|
|
||||||
// ui->lineEdit_network_settings_dns2->setValidator(&m_ipAddressValidator);
|
|
||||||
|
|
||||||
// ui->lineEdit_proto_openvpn_subnet->setValidator(&m_ipNetwok24Validator);
|
|
||||||
|
|
||||||
// ui->lineEdit_proto_openvpn_port->setValidator(&m_ipPortValidator);
|
|
||||||
// ui->lineEdit_proto_shadowsocks_port->setValidator(&m_ipPortValidator);
|
|
||||||
// ui->lineEdit_proto_cloak_port->setValidator(&m_ipPortValidator);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString UiLogic::getDialogConnectErrorText() const
|
QString UiLogic::getDialogConnectErrorText() const
|
||||||
|
@ -225,10 +181,13 @@ void UiLogic::setDialogConnectErrorText(const QString &dialogConnectErrorText)
|
||||||
void UiLogic::showOnStartup()
|
void UiLogic::showOnStartup()
|
||||||
{
|
{
|
||||||
if (! m_settings.isStartMinimized()) {
|
if (! m_settings.isStartMinimized()) {
|
||||||
show();
|
emit show();
|
||||||
} else {
|
}
|
||||||
#if defined Q_OS_MACX
|
else {
|
||||||
setDockIconVisible(false);
|
#ifdef Q_OS_WIN
|
||||||
|
emit hide();
|
||||||
|
#elif defined Q_OS_MACX
|
||||||
|
// TODO: fix: setDockIconVisible(false);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,6 +216,9 @@ void UiLogic::onUpdateAllPages()
|
||||||
void UiLogic::keyPressEvent(Qt::Key key)
|
void UiLogic::keyPressEvent(Qt::Key key)
|
||||||
{
|
{
|
||||||
switch (key) {
|
switch (key) {
|
||||||
|
case Qt::Key_AsciiTilde:
|
||||||
|
case Qt::Key_QuoteLeft: emit toggleLogPanel();
|
||||||
|
break;
|
||||||
case Qt::Key_L: Debug::openLogsFolder();
|
case Qt::Key_L: Debug::openLogsFolder();
|
||||||
break;
|
break;
|
||||||
case Qt::Key_K: Debug::openServiceLogsFolder();
|
case Qt::Key_K: Debug::openServiceLogsFolder();
|
||||||
|
@ -302,7 +264,6 @@ void UiLogic::keyPressEvent(Qt::Key key)
|
||||||
// break;
|
// break;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
//if (! ui->stackedWidget_main->isAnimationRunning() && ui->stackedWidget_main->currentWidget()->isEnabled()) {
|
|
||||||
emit closePage();
|
emit closePage();
|
||||||
//}
|
//}
|
||||||
default:
|
default:
|
||||||
|
@ -644,12 +605,14 @@ PageEnumNS::Page UiLogic::currentPage()
|
||||||
return static_cast<PageEnumNS::Page>(currentPageValue());
|
return static_cast<PageEnumNS::Page>(currentPageValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void UiLogic::saveTextFile(const QString& desc, const QString& ext, const QString& data)
|
void UiLogic::saveTextFile(const QString& desc, QString ext, const QString& data)
|
||||||
{
|
{
|
||||||
|
ext.replace("*", "");
|
||||||
QString fileName = QFileDialog::getSaveFileName(nullptr, desc,
|
QString fileName = QFileDialog::getSaveFileName(nullptr, desc,
|
||||||
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), ext);
|
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*" + ext);
|
||||||
|
|
||||||
if (fileName.isEmpty()) return;
|
if (fileName.isEmpty()) return;
|
||||||
|
if (!fileName.endsWith(ext)) fileName.append(ext);
|
||||||
|
|
||||||
QFile save(fileName);
|
QFile save(fileName);
|
||||||
save.open(QIODevice::WriteOnly);
|
save.open(QIODevice::WriteOnly);
|
||||||
|
@ -660,12 +623,14 @@ void UiLogic::saveTextFile(const QString& desc, const QString& ext, const QStrin
|
||||||
QDesktopServices::openUrl(fi.absoluteDir().absolutePath());
|
QDesktopServices::openUrl(fi.absoluteDir().absolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
void UiLogic::saveBinaryFile(const QString &desc, const QString &ext, const QString &data)
|
void UiLogic::saveBinaryFile(const QString &desc, QString ext, const QString &data)
|
||||||
{
|
{
|
||||||
|
ext.replace("*", "");
|
||||||
QString fileName = QFileDialog::getSaveFileName(nullptr, desc,
|
QString fileName = QFileDialog::getSaveFileName(nullptr, desc,
|
||||||
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), ext);
|
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*" + ext);
|
||||||
|
|
||||||
if (fileName.isEmpty()) return;
|
if (fileName.isEmpty()) return;
|
||||||
|
if (!fileName.endsWith(ext)) fileName.append(ext);
|
||||||
|
|
||||||
QFile save(fileName);
|
QFile save(fileName);
|
||||||
save.open(QIODevice::WriteOnly);
|
save.open(QIODevice::WriteOnly);
|
||||||
|
|
|
@ -100,8 +100,8 @@ public:
|
||||||
|
|
||||||
Q_INVOKABLE void keyPressEvent(Qt::Key key);
|
Q_INVOKABLE void keyPressEvent(Qt::Key key);
|
||||||
|
|
||||||
Q_INVOKABLE void saveTextFile(const QString& desc, const QString& ext, const QString& data);
|
Q_INVOKABLE void saveTextFile(const QString& desc, QString ext, const QString& data);
|
||||||
Q_INVOKABLE void saveBinaryFile(const QString& desc, const QString& ext, const QString& data);
|
Q_INVOKABLE void saveBinaryFile(const QString& desc, QString ext, const QString& data);
|
||||||
Q_INVOKABLE void copyToClipboard(const QString& text);
|
Q_INVOKABLE void copyToClipboard(const QString& text);
|
||||||
|
|
||||||
QString getDialogConnectErrorText() const;
|
QString getDialogConnectErrorText() const;
|
||||||
|
@ -121,6 +121,7 @@ signals:
|
||||||
void show();
|
void show();
|
||||||
void hide();
|
void hide();
|
||||||
void raise();
|
void raise();
|
||||||
|
void toggleLogPanel();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_dialogConnectErrorText;
|
QString m_dialogConnectErrorText;
|
||||||
|
@ -213,20 +214,6 @@ private:
|
||||||
|
|
||||||
NotificationHandler* m_notificationHandler;
|
NotificationHandler* m_notificationHandler;
|
||||||
|
|
||||||
|
|
||||||
// QRegExpValidator m_ipAddressValidator;
|
|
||||||
// QRegExpValidator m_ipAddressPortValidator;
|
|
||||||
// QRegExpValidator m_ipNetwok24Validator;
|
|
||||||
// QRegExpValidator m_ipPortValidator;
|
|
||||||
|
|
||||||
// QPoint offset;
|
|
||||||
// bool needToHideCustomTitlebar = false;
|
|
||||||
|
|
||||||
// void showEvent(QShowEvent *event) override;
|
|
||||||
// void hideEvent(QHideEvent *event) override;
|
|
||||||
|
|
||||||
|
|
||||||
// QStack<Page> pagesStack;
|
|
||||||
int selectedServerIndex = -1; // server index to use when proto settings page opened
|
int selectedServerIndex = -1; // server index to use when proto settings page opened
|
||||||
DockerContainer selectedDockerContainer; // same
|
DockerContainer selectedDockerContainer; // same
|
||||||
ServerCredentials installCredentials; // used to save cred between pages new_server and new_server_protocols and wizard
|
ServerCredentials installCredentials; // used to save cred between pages new_server and new_server_protocols and wizard
|
||||||
|
|
|
@ -35,6 +35,7 @@ public:
|
||||||
|
|
||||||
static QRegExp ipPortRegExp() { return QRegExp("^()([1-9]|[1-5]?[0-9]{2,4}|6[1-4][0-9]{3}|65[1-4][0-9]{2}|655[1-2][0-9]|6553[1-5])$"); }
|
static QRegExp ipPortRegExp() { return QRegExp("^()([1-9]|[1-5]?[0-9]{2,4}|6[1-4][0-9]{3}|65[1-4][0-9]{2}|655[1-2][0-9]|6553[1-5])$"); }
|
||||||
|
|
||||||
|
static QRegExp domainRegExp() { return QRegExp("(((?!\\-))(xn\\-\\-)?[a-z0-9\\-_]{0,61}[a-z0-9]{1,1}\\.)*(xn\\-\\-)?([a-z0-9\\-]{1,61}|[a-z0-9\\-]{1,30})\\.[a-z]{2,}"); }
|
||||||
static bool processIsRunning(const QString& fileName);
|
static bool processIsRunning(const QString& fileName);
|
||||||
static void killProcessByName(const QString &name);
|
static void killProcessByName(const QString &name);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QHostInfo>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
|
||||||
#include <configurators/openvpn_configurator.h>
|
#include <configurators/openvpn_configurator.h>
|
||||||
|
@ -56,21 +57,27 @@ void VpnConnection::onConnectionStateChanged(VpnProtocol::VpnConnectionState sta
|
||||||
IpcClient::Interface()->routeDeleteList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0");
|
IpcClient::Interface()->routeDeleteList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0");
|
||||||
//qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size();
|
//qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size();
|
||||||
}
|
}
|
||||||
|
QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString();
|
||||||
|
QString dns2 = m_vpnConfiguration.value(config_key::dns1).toString();
|
||||||
|
|
||||||
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(),
|
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(),
|
||||||
QStringList() << m_settings.primaryDns() << m_settings.secondaryDns());
|
QStringList() << dns1 << dns2);
|
||||||
|
|
||||||
|
|
||||||
if (m_settings.routeMode() == Settings::VpnOnlyForwardSites) {
|
if (m_settings.routeMode() == Settings::VpnOnlyForwardSites) {
|
||||||
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), m_settings.getVpnIps(Settings::VpnOnlyForwardSites));
|
QTimer::singleShot(1000, m_vpnProtocol.data(), [this](){
|
||||||
|
addSitesRoutes(m_vpnProtocol->vpnGateway(), m_settings.routeMode());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else if (m_settings.routeMode() == Settings::VpnAllExceptSites) {
|
else if (m_settings.routeMode() == Settings::VpnAllExceptSites) {
|
||||||
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0/1");
|
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0/1");
|
||||||
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "128.0.0.0/1");
|
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "128.0.0.0/1");
|
||||||
|
|
||||||
IpcClient::Interface()->routeAddList(m_vpnProtocol->routeGateway(), QStringList() << remoteAddress());
|
IpcClient::Interface()->routeAddList(m_vpnProtocol->routeGateway(), QStringList() << remoteAddress());
|
||||||
IpcClient::Interface()->routeAddList(m_vpnProtocol->routeGateway(), m_settings.getVpnIps(Settings::VpnAllExceptSites));
|
addSitesRoutes(m_vpnProtocol->routeGateway(), m_settings.routeMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (state == VpnProtocol::Error) {
|
else if (state == VpnProtocol::Error) {
|
||||||
IpcClient::Interface()->flushDns();
|
IpcClient::Interface()->flushDns();
|
||||||
|
@ -89,6 +96,49 @@ const QString &VpnConnection::remoteAddress() const
|
||||||
return m_remoteAddress;
|
return m_remoteAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VpnConnection::addSitesRoutes(const QString &gw, Settings::RouteMode mode)
|
||||||
|
{
|
||||||
|
QStringList ips;
|
||||||
|
QStringList sites;
|
||||||
|
const QVariantMap &m = m_settings.vpnSites(mode);
|
||||||
|
for (auto i = m.constBegin(); i != m.constEnd(); ++i) {
|
||||||
|
if (Utils::checkIpSubnetFormat(i.key())) {
|
||||||
|
ips.append(i.key());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (Utils::checkIpSubnetFormat(i.value().toString())) {
|
||||||
|
ips.append(i.value().toString());
|
||||||
|
}
|
||||||
|
sites.append(i.key());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ips.removeDuplicates();
|
||||||
|
|
||||||
|
// add all IPs immediately
|
||||||
|
IpcClient::Interface()->routeAddList(gw, ips);
|
||||||
|
|
||||||
|
// re-resolve domains
|
||||||
|
for (const QString &site: sites) {
|
||||||
|
const auto &cbResolv = [this, site, gw, mode, ips](const QHostInfo &hostInfo){
|
||||||
|
const QList<QHostAddress> &addresses = hostInfo.addresses();
|
||||||
|
QString ipv4Addr;
|
||||||
|
for (const QHostAddress &addr: hostInfo.addresses()) {
|
||||||
|
if (addr.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol) {
|
||||||
|
const QString &ip = addr.toString();
|
||||||
|
//qDebug() << "VpnConnection::addSitesRoutes updating site" << site << ip;
|
||||||
|
if (!ips.contains(ip)) {
|
||||||
|
IpcClient::Interface()->routeAddList(gw, QStringList() << ip);
|
||||||
|
m_settings.addVpnSite(mode, site, ip);
|
||||||
|
}
|
||||||
|
flushDns();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
QHostInfo::lookupHost(site, this, cbResolv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QSharedPointer<VpnProtocol> VpnConnection::vpnProtocol() const
|
QSharedPointer<VpnProtocol> VpnConnection::vpnProtocol() const
|
||||||
{
|
{
|
||||||
return m_vpnProtocol;
|
return m_vpnProtocol;
|
||||||
|
@ -149,12 +199,12 @@ QString VpnConnection::createVpnConfigurationForProto(int serverIndex,
|
||||||
ErrorCode *errorCode)
|
ErrorCode *errorCode)
|
||||||
{
|
{
|
||||||
ErrorCode e = ErrorCode::NoError;
|
ErrorCode e = ErrorCode::NoError;
|
||||||
auto lastVpnConfig = getLastVpnConfig(containerConfig);
|
QMap<Proto, QString> lastVpnConfig = getLastVpnConfig(containerConfig);
|
||||||
|
|
||||||
QString configData;
|
QString configData;
|
||||||
if (lastVpnConfig.contains(proto)) {
|
if (lastVpnConfig.contains(proto)) {
|
||||||
configData = lastVpnConfig.value(proto);
|
configData = lastVpnConfig.value(proto);
|
||||||
configData = VpnConfigurator::processConfigWithLocalSettings(container, proto, configData);
|
configData = VpnConfigurator::processConfigWithLocalSettings(serverIndex, container, proto, configData);
|
||||||
|
|
||||||
qDebug() << "VpnConnection::createVpnConfiguration: using saved config for" << ProtocolProps::protoToString(proto);
|
qDebug() << "VpnConnection::createVpnConfiguration: using saved config for" << ProtocolProps::protoToString(proto);
|
||||||
}
|
}
|
||||||
|
@ -165,7 +215,7 @@ QString VpnConnection::createVpnConfigurationForProto(int serverIndex,
|
||||||
|
|
||||||
QString configDataBeforeLocalProcessing = configData;
|
QString configDataBeforeLocalProcessing = configData;
|
||||||
|
|
||||||
configData = VpnConfigurator::processConfigWithLocalSettings(container, proto, configData);
|
configData = VpnConfigurator::processConfigWithLocalSettings(serverIndex, container, proto, configData);
|
||||||
|
|
||||||
|
|
||||||
if (errorCode && e) {
|
if (errorCode && e) {
|
||||||
|
@ -211,6 +261,11 @@ QJsonObject VpnConnection::createVpnConfiguration(int serverIndex,
|
||||||
Proto proto = ContainerProps::defaultProtocol(container);
|
Proto proto = ContainerProps::defaultProtocol(container);
|
||||||
vpnConfiguration[config_key::vpnproto] = ProtocolProps::protoToString(proto);
|
vpnConfiguration[config_key::vpnproto] = ProtocolProps::protoToString(proto);
|
||||||
|
|
||||||
|
auto dns = VpnConfigurator::getDnsForConfig(serverIndex);
|
||||||
|
|
||||||
|
vpnConfiguration[config_key::dns1] = dns.first;
|
||||||
|
vpnConfiguration[config_key::dns2] = dns.second;
|
||||||
|
|
||||||
return vpnConfiguration;
|
return vpnConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,11 +295,8 @@ void VpnConnection::connectToVpn(int serverIndex,
|
||||||
|
|
||||||
if (m_vpnProtocol) {
|
if (m_vpnProtocol) {
|
||||||
disconnect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
|
disconnect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
|
||||||
//qDebug() << "VpnConnection::connectToVpn 1";
|
|
||||||
m_vpnProtocol->stop();
|
m_vpnProtocol->stop();
|
||||||
//qDebug() << "VpnConnection::connectToVpn 2";
|
|
||||||
m_vpnProtocol.reset();
|
m_vpnProtocol.reset();
|
||||||
//qDebug() << "VpnConnection::connectToVpn 3";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode e = ErrorCode::NoError;
|
ErrorCode e = ErrorCode::NoError;
|
||||||
|
|
|
@ -47,6 +47,7 @@ public:
|
||||||
void flushDns();
|
void flushDns();
|
||||||
|
|
||||||
const QString &remoteAddress() const;
|
const QString &remoteAddress() const;
|
||||||
|
void addSitesRoutes(const QString &gw, Settings::RouteMode mode);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void connectToVpn(int serverIndex,
|
void connectToVpn(int serverIndex,
|
||||||
|
|
|
@ -64,7 +64,7 @@ echo "Packaging ..."
|
||||||
|
|
||||||
#cd $DEPLOY_DIR
|
#cd $DEPLOY_DIR
|
||||||
|
|
||||||
$QT_BIN_DIR/macdeployqt $OUT_APP_DIR/$APP_FILENAME -always-overwrite
|
$QT_BIN_DIR/macdeployqt $OUT_APP_DIR/$APP_FILENAME -always-overwrite -qmldir=$PROJECT_DIR
|
||||||
cp -av $BUILD_DIR/service/server/$APP_NAME-service.app/Contents/macOS/$APP_NAME-service $BUNDLE_DIR/Contents/macOS
|
cp -av $BUILD_DIR/service/server/$APP_NAME-service.app/Contents/macOS/$APP_NAME-service $BUNDLE_DIR/Contents/macOS
|
||||||
cp -Rv $PROJECT_DIR/deploy/data/macos/* $BUNDLE_DIR/Contents/macOS
|
cp -Rv $PROJECT_DIR/deploy/data/macos/* $BUNDLE_DIR/Contents/macOS
|
||||||
rm -f $BUNDLE_DIR/Contents/macOS/post_install.sh $BUNDLE_DIR/Contents/macOS/post_uninstall.sh
|
rm -f $BUNDLE_DIR/Contents/macOS/post_install.sh $BUNDLE_DIR/Contents/macOS/post_uninstall.sh
|
||||||
|
|
|
@ -70,7 +70,6 @@ rem if not exist "%OUT_APP_DIR:"=%\%APP_FILENAME:"=%" break
|
||||||
echo "Deploying..."
|
echo "Deploying..."
|
||||||
|
|
||||||
copy "%WORK_DIR:"=%\service\server\release\%APP_NAME:"=%-service.exe" %OUT_APP_DIR%
|
copy "%WORK_DIR:"=%\service\server\release\%APP_NAME:"=%-service.exe" %OUT_APP_DIR%
|
||||||
copy "%WORK_DIR:"=%\platform\post-uninstall\release\post-uninstall.exe" %OUT_APP_DIR%
|
|
||||||
|
|
||||||
echo "Signing exe"
|
echo "Signing exe"
|
||||||
cd %OUT_APP_DIR%
|
cd %OUT_APP_DIR%
|
||||||
|
@ -84,8 +83,6 @@ echo "Copying deploy data..."
|
||||||
xcopy %DEPLOY_DATA_DIR% %OUT_APP_DIR% /s /e /y /i /f
|
xcopy %DEPLOY_DATA_DIR% %OUT_APP_DIR% /s /e /y /i /f
|
||||||
copy "%WORK_DIR:"=%\service\wireguard-service\release\wireguard-service.exe" %OUT_APP_DIR%\wireguard\
|
copy "%WORK_DIR:"=%\service\wireguard-service\release\wireguard-service.exe" %OUT_APP_DIR%\wireguard\
|
||||||
|
|
||||||
del %OUT_APP_DIR%\botand.dll
|
|
||||||
|
|
||||||
cd %SCRIPT_DIR%
|
cd %SCRIPT_DIR%
|
||||||
xcopy %SCRIPT_DIR:"=%\installer %RELEASE_DIR:"=%\installer /s /e /y /i /f
|
xcopy %SCRIPT_DIR:"=%\installer %RELEASE_DIR:"=%\installer /s /e /y /i /f
|
||||||
mkdir %INSTALLER_DATA_DIR%
|
mkdir %INSTALLER_DATA_DIR%
|
||||||
|
|