From 505c9c62183739b9a3d475f278cd915855bac523 Mon Sep 17 00:00:00 2001 From: pokamest Date: Wed, 9 Feb 2022 15:23:20 +0300 Subject: [PATCH] Various fixes --- .../android/src/org/amnezia/vpn/VPNService.kt | 594 +++++++++--------- client/client.pro | 2 +- client/ui/pages_logic/QrDecoderLogic.cpp | 2 - client/ui/qml/Controls/SettingButtonType.qml | 1 + client/ui/uilogic.cpp | 2 + 5 files changed, 306 insertions(+), 295 deletions(-) diff --git a/client/android/src/org/amnezia/vpn/VPNService.kt b/client/android/src/org/amnezia/vpn/VPNService.kt index d6efc732..89d2c451 100644 --- a/client/android/src/org/amnezia/vpn/VPNService.kt +++ b/client/android/src/org/amnezia/vpn/VPNService.kt @@ -37,12 +37,13 @@ class VPNService : android.net.VpnService() { SharedLibraryLoader.loadSharedLibrary(this, "ovpn3") Log.i(tag, "Loaded libs") Log.e(tag, "Wireguard Version ${wgVersion()}") - mOpenVPNThreadv3 = OpenVPNThreadv3 (this) + mOpenVPNThreadv3 = OpenVPNThreadv3(this) mAlreadyInitialised = true } override fun onUnbind(intent: Intent?): Boolean { + Log.v(tag, "Got Unbind request") if (!isUp) { // If the Qt Client got closed while we were not connected // 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 - * calles bindService. Returns the [VPNServiceBinder] so QT can send Requests to it. - */ + * EntryPoint for the Service, gets Called when AndroidController.cpp + * calles bindService. Returns the [VPNServiceBinder] so QT can send Requests to it. + */ override fun onBind(intent: Intent?): IBinder? { Log.v(tag, "Got Bind request") init() @@ -62,10 +63,10 @@ class VPNService : android.net.VpnService() { } /** - * Might be the entryPoint if the Service gets Started via an - * Service Intent: Might be from Always-On-Vpn from Settings - * or from Booting the device and having "connect on boot" enabled. - */ + * Might be the entryPoint if the Service gets Started via an + * Service Intent: Might be from Always-On-Vpn from Settings + * or from Booting the device and having "connect on boot" enabled. + */ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { init() intent?.let { @@ -84,28 +85,28 @@ class VPNService : android.net.VpnService() { Log.e( tag, "VPN service was triggered without defining a Server or having a tunnel" - ) - return super.onStartCommand(intent, flags, startId) - } - this.mConfig = JSONObject(lastConfString) + ) + return super.onStartCommand(intent, flags, startId) } - - return super.onStartCommand(intent, flags, startId) + this.mConfig = JSONObject(lastConfString) } - // 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() - } + return super.onStartCommand(intent, flags, startId) + } - 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() { return mConnectionTime } - var isUp: Boolean + var isUp: Boolean get() { return currentTunnelHandle >= 0 } @@ -118,7 +119,7 @@ class VPNService : android.net.VpnService() { mBinder.dispatchEvent(VPNServiceBinder.EVENTS.disconnected, "") mConnectionTime = 0 } - val status: JSONObject + val status: JSONObject get() { val deviceIpv4: String = "" return JSONObject().apply { @@ -128,299 +129,308 @@ class VPNService : android.net.VpnService() { 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()) { - Log.e(tag, "turn on was called without no permissions present!") - isUp = false - 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 - } - } - 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) + /* + * 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 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) - + fun turnOn(json: JSONObject?): Int { + if (!checkPermissions()) { + Log.e(tag, "turn on was called without no permissions present!") 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 + } + } + 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() { - mOpenVPNThreadv3?.stop() - mOpenVPNThreadv3 = null - Log.e(tag, "mOpenVPNThreadv3 stop!") - } - /** - * Configures an Android VPN Service Tunnel - * with a given Wireguard Config - */ - private fun setupBuilder(config: Config, builder: Builder) { - // Setup Split tunnel - for (excludedApplication in config.`interface`.excludedApplications) + private fun ovpnTurnOff() { + mOpenVPNThreadv3?.stop() + mOpenVPNThreadv3 = null + Log.e(tag, "mOpenVPNThreadv3 stop!") + } + + /** + * Configures an Android VPN Service Tunnel + * with a given Wireguard Config + */ + private fun setupBuilder(config: Config, builder: Builder) { + // Setup Split tunnel + for (excludedApplication in config.`interface`.excludedApplications) builder.addDisallowedApplication(excludedApplication) - // Device IP - for (addr in config.`interface`.addresses) builder.addAddress(addr.address, addr.mask) - // DNS - for (addr in config.`interface`.dnsServers) builder.addDnsServer(addr.hostAddress) - // Add All routes the VPN may route tos - for (peer in config.peers) { - for (addr in peer.allowedIps) { - builder.addRoute(addr.address, addr.mask) - } + // Device IP + for (addr in config.`interface`.addresses) builder.addAddress(addr.address, addr.mask) + // DNS + for (addr in config.`interface`.dnsServers) builder.addDnsServer(addr.hostAddress) + // Add All routes the VPN may route tos + for (peer in config.peers) { + for (addr in peer.allowedIps) { + 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)) - /** - * Gets config value for {key} from the Current - * running Wireguard tunnel - */ - private fun getConfigValue(key: String): String? { - if (!isUp) { - 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 - } - } + 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) + } + + /** + * Gets config value for {key} from the Current + * running Wireguard tunnel + */ + private fun getConfigValue(key: String): String? { + if (!isUp) { 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> { - val parseData = mutableMapOf>() - var currentSection: Pair>? = null - data.lines().forEach { line -> - if (line.isNotEmpty()) { - if (line.startsWith('[')) { - currentSection?.let { - 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()) + private fun parseConfigData(data: String): Map> { + val parseData = mutableMapOf>() + var currentSection: Pair>? = null + data.lines().forEach { line -> + if (line.isNotEmpty()) { + if (line.startsWith('[')) { + currentSection?.let { + 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?.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() + + 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) + }) } - /** - * 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) - } - } - peerBuilder.setEndpoint(InetEndpoint.parse(peerConfig["Endpoint"])) - peerConfig["PersistentKeepalive"]?.let { - peerBuilder.setPersistentKeepalive(it.toInt()) - } - confBuilder.addPeer(peerBuilder.build()) + @JvmStatic + private external fun wgGetConfig(handle: Int): String? - 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()) + @JvmStatic + private external fun wgGetSocketV4(handle: Int): Int - return confBuilder.build() - } + @JvmStatic + private external fun wgGetSocketV6(handle: Int): Int - fun getVpnConfig(): JSONObject { - return mConfig!! - } + @JvmStatic + private external fun wgTurnOff(handle: Int) - private fun startOpenVpn() { - mOpenVPNThreadv3 = OpenVPNThreadv3 (this) - Thread ({ - mOpenVPNThreadv3?.run() - }).start() - } + @JvmStatic + private external fun wgTurnOn(ifName: String, tunFd: Int, settings: String): Int - 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() - - 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? - } - } + @JvmStatic + private external fun wgVersion(): String? + } +} diff --git a/client/client.pro b/client/client.pro index 28f11c42..fe501f31 100644 --- a/client/client.pro +++ b/client/client.pro @@ -1,4 +1,4 @@ -QT += widgets core gui network xml remoteobjects quick +QT += widgets core gui network xml remoteobjects quick svg TARGET = AmneziaVPN TEMPLATE = app diff --git a/client/ui/pages_logic/QrDecoderLogic.cpp b/client/ui/pages_logic/QrDecoderLogic.cpp index 5933a9e6..01c607f7 100644 --- a/client/ui/pages_logic/QrDecoderLogic.cpp +++ b/client/ui/pages_logic/QrDecoderLogic.cpp @@ -38,8 +38,6 @@ void QrDecoderLogic::onDetectedQrCode(const QString &code) if (magic == amnezia::qrMagicCode) { - qDebug() << "QrDecoderLogic::onDetectedQrCode magic code detected" << magic << ba.size(); - quint8 chunksCount; s >> chunksCount; if (totalChunksCount() != chunksCount) { m_chunks.clear(); diff --git a/client/ui/qml/Controls/SettingButtonType.qml b/client/ui/qml/Controls/SettingButtonType.qml index 2efee56c..5569011d 100644 --- a/client/ui/qml/Controls/SettingButtonType.qml +++ b/client/ui/qml/Controls/SettingButtonType.qml @@ -12,6 +12,7 @@ BasicButtonType { anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter svg.source: root.icon.source + color: "#100A44" width: 25 height: 25 } diff --git a/client/ui/uilogic.cpp b/client/ui/uilogic.cpp index 64310f41..bd23102d 100644 --- a/client/ui/uilogic.cpp +++ b/client/ui/uilogic.cpp @@ -112,6 +112,7 @@ UiLogic::~UiLogic() { emit hide(); +#ifdef AMNEZIA_DESKTOP if (m_vpnConnection->connectionState() != VpnProtocol::VpnConnectionState::Disconnected) { m_vpnConnection->disconnectFromVpn(); for (int i = 0; i < 50; i++) { @@ -122,6 +123,7 @@ UiLogic::~UiLogic() } } } +#endif m_vpnConnection->deleteLater(); m_vpnConnectionThread.quit();