Merge remote-tracking branch 'origin/dev' into ios-wireguard

This commit is contained in:
Alex Kh 2022-02-15 10:55:45 +04:00
commit e1293c2c74
118 changed files with 2135 additions and 981 deletions

View file

@ -2,5 +2,5 @@ TEMPLATE = subdirs
SUBDIRS = client SUBDIRS = client
!ios:!android { !ios:!android {
SUBDIRS += service platform SUBDIRS += service
} }

View file

@ -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>

View file

@ -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 {

View file

@ -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?
}
}

View file

@ -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 \

View file

@ -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;
} }

View file

@ -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,

View file

@ -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) {

View file

@ -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;

View file

@ -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"

View file

@ -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();
}

View file

@ -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;

View 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 454 B

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View file

@ -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();
} }

View file

@ -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);

View file

@ -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";

View file

@ -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;
} }

View file

@ -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>

View file

@ -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

View file

@ -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

View file

@ -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'

View file

@ -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

View file

@ -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

View file

@ -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'

View file

@ -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

View file

@ -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

View file

@ -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'

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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() {

View file

@ -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();
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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;

View file

@ -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();

View file

@ -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)

View file

@ -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())

View file

@ -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);

View file

@ -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);
}

View file

@ -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);

View file

@ -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."));

View file

@ -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)

View file

@ -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
} }

View file

@ -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

View file

@ -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"
} }

View file

@ -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
} }

View 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
}
}

View file

@ -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

View 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
}
}

View 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
}
}

View 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
}
}

View file

@ -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
} }
} }

View file

@ -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()

View file

@ -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()
} }
} }

View file

@ -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)

View file

@ -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)

View file

@ -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 {

View file

@ -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

View file

@ -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);
}
}
} }
} }
} }

View file

@ -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)
}
}
} }
} }
} }

View file

@ -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()
}
} }
} }
} }

View file

@ -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)
}
} }
} }
} }

View file

@ -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()
}
} }
} }
} }

View file

@ -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()
} }
} }
} }

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
} }

View file

@ -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
}
}
}
}
} }

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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,

View file

@ -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

View file

@ -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%

Binary file not shown.

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