From a2bfd75dee9b4952771a66c555ba1d920a072ae9 Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Tue, 11 Jul 2017 14:49:08 +0200 Subject: Implement create and join network dialogs + refactoring --- .../org/pacien/tincapp/activities/BaseActivity.kt | 9 ++- .../pacien/tincapp/activities/ConfigureActivity.kt | 61 ++++++++++++++++- .../org/pacien/tincapp/activities/StartActivity.kt | 2 +- .../pacien/tincapp/activities/StatusActivity.kt | 12 ++-- .../java/org/pacien/tincapp/commands/Executor.kt | 7 +- .../java/org/pacien/tincapp/commands/TincApp.kt | 29 ++++++++ .../java/org/pacien/tincapp/context/AppPaths.kt | 4 ++ .../java/org/pacien/tincapp/data/CidrAddress.kt | 18 +++++ .../tincapp/data/VpnInterfaceConfiguration.kt | 78 ++++++++++++++++++++++ .../java/org/pacien/tincapp/extensions/Android.kt | 24 +++++++ .../tincapp/extensions/ApacheConfiguration.kt | 16 +++++ .../java/org/pacien/tincapp/extensions/Java.kt | 18 +++++ .../pacien/tincapp/extensions/VpnServiceBuilder.kt | 51 ++++++++++++++ .../org/pacien/tincapp/service/TincVpnService.kt | 9 +-- .../tincapp/service/VpnInterfaceConfiguraton.kt | 62 ----------------- .../tincapp/service/VpnServiceBuilderExtensions.kt | 46 ------------- .../org/pacien/tincapp/utils/AndroidExtensions.kt | 23 ------- .../java/org/pacien/tincapp/utils/Functions.kt | 11 --- app/src/main/res/layout/page_configure.xml | 4 +- app/src/main/res/values/strings.xml | 10 +++ 20 files changed, 335 insertions(+), 159 deletions(-) create mode 100644 app/src/main/java/org/pacien/tincapp/commands/TincApp.kt create mode 100644 app/src/main/java/org/pacien/tincapp/data/CidrAddress.kt create mode 100644 app/src/main/java/org/pacien/tincapp/data/VpnInterfaceConfiguration.kt create mode 100644 app/src/main/java/org/pacien/tincapp/extensions/Android.kt create mode 100644 app/src/main/java/org/pacien/tincapp/extensions/ApacheConfiguration.kt create mode 100644 app/src/main/java/org/pacien/tincapp/extensions/Java.kt create mode 100644 app/src/main/java/org/pacien/tincapp/extensions/VpnServiceBuilder.kt delete mode 100644 app/src/main/java/org/pacien/tincapp/service/VpnInterfaceConfiguraton.kt delete mode 100644 app/src/main/java/org/pacien/tincapp/service/VpnServiceBuilderExtensions.kt delete mode 100644 app/src/main/java/org/pacien/tincapp/utils/AndroidExtensions.kt delete mode 100644 app/src/main/java/org/pacien/tincapp/utils/Functions.kt diff --git a/app/src/main/java/org/pacien/tincapp/activities/BaseActivity.kt b/app/src/main/java/org/pacien/tincapp/activities/BaseActivity.kt index 274e1ba..000320c 100644 --- a/app/src/main/java/org/pacien/tincapp/activities/BaseActivity.kt +++ b/app/src/main/java/org/pacien/tincapp/activities/BaseActivity.kt @@ -1,5 +1,6 @@ package org.pacien.tincapp.activities +import android.app.ProgressDialog import android.content.Intent import android.net.Uri import android.os.Bundle @@ -38,12 +39,18 @@ abstract class BaseActivity : AppCompatActivity() { resources.getString(R.string.app_license) + "\n\n" + AppInfo.all()) .setNeutralButton(R.string.action_open_project_website) { _, _ -> openWebsite(R.string.app_website_url) } - .setPositiveButton(R.string.action_close) { _, _ -> /* nop */ } + .setPositiveButton(R.string.action_close, dismiss) .show() } protected fun openWebsite(@StringRes url: Int) = startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(resources.getString(url)))) protected fun notify(@StringRes msg: Int) = Snackbar.make(activity_base, msg, Snackbar.LENGTH_LONG).show() protected fun notify(msg: String) = Snackbar.make(activity_base, msg, Snackbar.LENGTH_LONG).show() + protected fun showProgressDialog(@StringRes msg: Int): ProgressDialog = ProgressDialog.show(this, null, getString(msg), true, false) + protected fun showErrorDialog(msg: String): AlertDialog = AlertDialog.Builder(this) + .setTitle(R.string.title_error).setMessage(msg) + .setPositiveButton(R.string.action_close, dismiss).show() + + protected val dismiss = { _: Any, _: Any -> /* nop */ } } diff --git a/app/src/main/java/org/pacien/tincapp/activities/ConfigureActivity.kt b/app/src/main/java/org/pacien/tincapp/activities/ConfigureActivity.kt index 3590f99..d11809c 100644 --- a/app/src/main/java/org/pacien/tincapp/activities/ConfigureActivity.kt +++ b/app/src/main/java/org/pacien/tincapp/activities/ConfigureActivity.kt @@ -1,11 +1,20 @@ package org.pacien.tincapp.activities import android.os.Bundle +import android.support.annotation.StringRes +import android.support.v7.app.AlertDialog import android.view.View +import android.view.ViewGroup +import android.widget.EditText +import android.widget.FrameLayout +import java8.util.concurrent.CompletableFuture import kotlinx.android.synthetic.main.base.* import kotlinx.android.synthetic.main.page_configure.* import org.pacien.tincapp.R +import org.pacien.tincapp.commands.Tinc +import org.pacien.tincapp.commands.TincApp import org.pacien.tincapp.context.AppPaths +import org.pacien.tincapp.extensions.Java.exceptionallyAccept /** * @author pacien @@ -19,13 +28,61 @@ class ConfigureActivity : BaseActivity() { writeContent() } + fun openGenerateConfDialog(@Suppress("UNUSED_PARAMETER") v: View) { + val netNameField = EditText(this) + netNameField.layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT) + netNameField.setHint(R.string.field_net_name) + + val dialogFrame = layoutInflater.inflate(R.layout.dialog_frame, main_content, false) as ViewGroup + dialogFrame.addView(netNameField) + + AlertDialog.Builder(this).setTitle(R.string.title_new_network).setView(dialogFrame) + .setPositiveButton(R.string.action_create) { _, _ -> generateConf(netNameField.text.toString()) } + .setNegativeButton(R.string.action_cancel, dismiss).show() + } + + fun openJoinNetworkDialog(@Suppress("UNUSED_PARAMETER") v: View) { + val netNameField = EditText(this) + netNameField.layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT) + netNameField.setHint(R.string.field_net_name) + + val joinUrlField = EditText(this) + joinUrlField.layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT) + joinUrlField.setHint(R.string.field_invitation_url) + + val dialogFrame = layoutInflater.inflate(R.layout.dialog_frame, main_content, false) as ViewGroup + dialogFrame.addView(netNameField) + dialogFrame.addView(joinUrlField) + + AlertDialog.Builder(this).setTitle(R.string.title_join_network).setView(dialogFrame) + .setPositiveButton(R.string.action_join) { _, _ -> joinNetwork(netNameField.text.toString(), joinUrlField.text.toString()) } + .setNegativeButton(R.string.action_cancel, dismiss).show() + } + private fun writeContent() { text_configuration_directory.text = AppPaths.confDir().absolutePath text_log_directory.text = AppPaths.cacheDir().absolutePath text_tinc_binary.text = AppPaths.tinc().absolutePath } - fun generateConf(@Suppress("UNUSED_PARAMETER") v: View) = notify("Not implemented yet") - fun joinNetwork(@Suppress("UNUSED_PARAMETER") v: View) = notify("Not implemented yet") + private fun generateConf(netName: String) = execAction( + R.string.message_generating_configuration, + Tinc.init(netName) + .thenCompose { TincApp.removeScripts(netName) }) + + private fun joinNetwork(netName: String, url: String) = execAction( + R.string.message_joining_network, + Tinc.join(netName, url) + .thenCompose { TincApp.removeScripts(netName) } + .thenCompose { TincApp.generateIfaceCfg(netName) }) + + private fun execAction(@StringRes label: Int, action: CompletableFuture) { + showProgressDialog(label).let { progressDialog -> + action + .whenComplete { _, _ -> progressDialog.dismiss() } + .thenAccept { notify(R.string.message_network_configuration_created) } + .exceptionallyAccept { runOnUiThread { showErrorDialog(it.cause!!.localizedMessage) } } + } + } } diff --git a/app/src/main/java/org/pacien/tincapp/activities/StartActivity.kt b/app/src/main/java/org/pacien/tincapp/activities/StartActivity.kt index cecd474..6bd845d 100644 --- a/app/src/main/java/org/pacien/tincapp/activities/StartActivity.kt +++ b/app/src/main/java/org/pacien/tincapp/activities/StartActivity.kt @@ -13,9 +13,9 @@ import kotlinx.android.synthetic.main.base.* import kotlinx.android.synthetic.main.page_start.* import org.pacien.tincapp.R import org.pacien.tincapp.context.AppPaths +import org.pacien.tincapp.extensions.Android.setElements import org.pacien.tincapp.service.TincVpnService import org.pacien.tincapp.utils.FileObserver -import org.pacien.tincapp.utils.setElements /** * @author pacien diff --git a/app/src/main/java/org/pacien/tincapp/activities/StatusActivity.kt b/app/src/main/java/org/pacien/tincapp/activities/StatusActivity.kt index fb6ab73..a2c39dd 100644 --- a/app/src/main/java/org/pacien/tincapp/activities/StatusActivity.kt +++ b/app/src/main/java/org/pacien/tincapp/activities/StatusActivity.kt @@ -17,10 +17,10 @@ import kotlinx.android.synthetic.main.fragment_network_status_header.* import kotlinx.android.synthetic.main.page_status.* import org.pacien.tincapp.R import org.pacien.tincapp.commands.Tinc +import org.pacien.tincapp.data.VpnInterfaceConfiguration +import org.pacien.tincapp.extensions.Android.setElements +import org.pacien.tincapp.extensions.Android.setText import org.pacien.tincapp.service.TincVpnService -import org.pacien.tincapp.service.VpnInterfaceConfiguration -import org.pacien.tincapp.utils.setElements -import org.pacien.tincapp.utils.setText import java.util.* import kotlin.concurrent.timerTask @@ -105,8 +105,8 @@ class StatusActivity : BaseActivity(), AdapterView.OnItemClickListener, SwipeRef fun writeNetworkInfo(cfg: VpnInterfaceConfiguration) { text_network_name.text = TincVpnService.getCurrentNetName() ?: getString(R.string.value_none) - text_network_ip_addresses.setText(cfg.addresses.map { it.toString() }) - text_network_routes.setText(cfg.routes.map { it.toString() }) + text_network_ip_addresses.setText(cfg.addresses.map { it.toSlashSeparated() }) + text_network_routes.setText(cfg.routes.map { it.toSlashSeparated() }) text_network_dns_servers.setText(cfg.dnsServers) text_network_search_domains.setText(cfg.searchDomains) text_network_allow_bypass.text = getString(if (cfg.allowBypass) R.string.value_yes else R.string.value_no) @@ -135,7 +135,7 @@ class StatusActivity : BaseActivity(), AdapterView.OnItemClickListener, SwipeRef private val REFRESH_RATE = 5000L fun getNodeNames(): CompletableFuture> = when (TincVpnService.isConnected()) { - true -> Tinc.dumpNodes(TincVpnService.getCurrentNetName()!!).thenApply> { it.map { it.substringBefore(" ") } } + true -> Tinc.dumpNodes(TincVpnService.getCurrentNetName()!!).thenApply> { it.map { it.substringBefore(' ') } } false -> CompletableFuture.supplyAsync> { emptyList() } } } diff --git a/app/src/main/java/org/pacien/tincapp/commands/Executor.kt b/app/src/main/java/org/pacien/tincapp/commands/Executor.kt index eccd2f9..38c2cb5 100644 --- a/app/src/main/java/org/pacien/tincapp/commands/Executor.kt +++ b/app/src/main/java/org/pacien/tincapp/commands/Executor.kt @@ -1,6 +1,8 @@ package org.pacien.tincapp.commands +import android.os.AsyncTask import java8.util.concurrent.CompletableFuture +import java8.util.function.Supplier import java.io.BufferedReader import java.io.IOException import java.io.InputStream @@ -35,10 +37,13 @@ internal object Executor { throw CommandExecutionException(e.message ?: "Could not start process.") } - return CompletableFuture.supplyAsync> { + return supplyAsyncTask> { if (proc.waitFor() == 0) read(proc.inputStream) else throw CommandExecutionException(read(proc.errorStream).lastOrNull() ?: "Non-zero exit status.") } } + fun runAsyncTask(r: () -> Unit) = CompletableFuture.runAsync(Runnable(r), AsyncTask.THREAD_POOL_EXECUTOR)!! + fun supplyAsyncTask(s: () -> U) = CompletableFuture.supplyAsync(Supplier(s), AsyncTask.THREAD_POOL_EXECUTOR)!! + } diff --git a/app/src/main/java/org/pacien/tincapp/commands/TincApp.kt b/app/src/main/java/org/pacien/tincapp/commands/TincApp.kt new file mode 100644 index 0000000..108b27d --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/commands/TincApp.kt @@ -0,0 +1,29 @@ +package org.pacien.tincapp.commands + +import java8.util.concurrent.CompletableFuture +import org.pacien.tincapp.commands.Executor.runAsyncTask +import org.pacien.tincapp.context.AppPaths +import org.pacien.tincapp.data.VpnInterfaceConfiguration + +/** + * @author pacien + */ +object TincApp { + + private val SCRIPT_SUFFIXES = listOf("-up", "-down", "-created", "-accepted") + private val STATIC_SCRIPTS = listOf("tinc", "host", "subnet", "invitation").flatMap { s -> SCRIPT_SUFFIXES.map { s + it } } + + private fun listScripts(netName: String) = AppPaths.confDir(netName).listFiles { f -> f.name in STATIC_SCRIPTS } + + AppPaths.hostsDir(netName).listFiles { f -> SCRIPT_SUFFIXES.none { f.name.endsWith(it) } } + + fun removeScripts(netName: String): CompletableFuture = runAsyncTask { + listScripts(netName).forEach { it.delete() } + } + + fun generateIfaceCfg(netName: String): CompletableFuture = runAsyncTask { + VpnInterfaceConfiguration + .fromInvitation(AppPaths.invitationFile(netName)) + .write(AppPaths.netConfFile(netName)) + } + +} diff --git a/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt b/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt index 078a81d..c745d4d 100644 --- a/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt +++ b/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt @@ -16,13 +16,17 @@ object AppPaths { private val PIDFILE_FORMAT = "tinc.%s.pid" private val NET_CONF_FILE = "network.conf" + private val NET_HOSTS_DIR = "hosts" + private val NET_INVITATION_FILE = "invitation-data" fun cacheDir() = App.getContext().externalCacheDir!! fun confDir() = App.getContext().getExternalFilesDir(null)!! fun binDir() = File(App.getContext().applicationInfo.nativeLibraryDir) fun confDir(netName: String) = File(confDir(), netName) + fun hostsDir(netName: String) = File(confDir(netName), NET_HOSTS_DIR) fun netConfFile(netName: String) = File(confDir(netName), NET_CONF_FILE) + fun invitationFile(netName: String) = File(confDir(netName), NET_INVITATION_FILE) fun logFile(netName: String) = File(cacheDir(), String.format(LOGFILE_FORMAT, netName)) fun pidFile(netName: String) = File(App.getContext().cacheDir, String.format(PIDFILE_FORMAT, netName)) diff --git a/app/src/main/java/org/pacien/tincapp/data/CidrAddress.kt b/app/src/main/java/org/pacien/tincapp/data/CidrAddress.kt new file mode 100644 index 0000000..bce9894 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/data/CidrAddress.kt @@ -0,0 +1,18 @@ +package org.pacien.tincapp.data + +/** + * @author pacien + */ +data class CidrAddress(val address: String, val prefix: Int) { + + companion object { + + private val SEPARATOR = "/" + + fun fromSlashSeparated(s: String) = CidrAddress(s.substringBefore(SEPARATOR), Integer.parseInt(s.substringAfter(SEPARATOR))) + + } + + fun toSlashSeparated() = address + SEPARATOR + prefix + +} diff --git a/app/src/main/java/org/pacien/tincapp/data/VpnInterfaceConfiguration.kt b/app/src/main/java/org/pacien/tincapp/data/VpnInterfaceConfiguration.kt new file mode 100644 index 0000000..70c8b96 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/data/VpnInterfaceConfiguration.kt @@ -0,0 +1,78 @@ +package org.pacien.tincapp.data + +import org.apache.commons.configuration2.Configuration +import org.apache.commons.configuration2.FileBasedConfiguration +import org.apache.commons.configuration2.PropertiesConfiguration +import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder +import org.apache.commons.configuration2.builder.fluent.Configurations +import org.apache.commons.configuration2.builder.fluent.Parameters +import org.pacien.tincapp.extensions.ApacheConfiguration.getCidrList +import org.pacien.tincapp.extensions.ApacheConfiguration.getIntList +import org.pacien.tincapp.extensions.ApacheConfiguration.getStringList +import org.pacien.tincapp.extensions.Java.applyIgnoringException +import java.io.File + +/** + * @author pacien + */ +data class VpnInterfaceConfiguration(val addresses: List = emptyList(), + val routes: List = emptyList(), + val dnsServers: List = emptyList(), + val searchDomains: List = emptyList(), + val allowedApplications: List = emptyList(), + val disallowedApplications: List = emptyList(), + val allowedFamilies: List = emptyList(), + val allowBypass: Boolean = false, + val blocking: Boolean = false, + val mtu: Int? = null) { + + companion object { + + private val KEY_ADDRESSES = "Address" + private val KEY_ROUTES = "Route" + private val KEY_DNS_SERVERS = "DNSServer" + private val KEY_SEARCH_DOMAINS = "SearchDomain" + private val KEY_ALLOWED_APPLICATIONS = "AllowApplication" + private val KEY_DISALLOWED_APPLICATIONS = "DisallowApplication" + private val KEY_ALLOWED_FAMILIES = "AllowFamily" + private val KEY_ALLOW_BYPASS = "AllowBypass" + private val KEY_BLOCKING = "Blocking" + private val KEY_MTU = "MTU" + + private val INVITATION_KEY_ADDRESSES = "Ifconfig" + private val INVITATION_KEY_ROUTES = "Route" + + fun fromIfaceConfiguration(f: File) = fromIfaceConfiguration(Configurations().properties(f)) + fun fromIfaceConfiguration(c: Configuration) = VpnInterfaceConfiguration( + c.getCidrList(KEY_ADDRESSES), + c.getCidrList(KEY_ROUTES), + c.getStringList(KEY_DNS_SERVERS), + c.getStringList(KEY_SEARCH_DOMAINS), + c.getStringList(KEY_ALLOWED_APPLICATIONS), + c.getStringList(KEY_DISALLOWED_APPLICATIONS), + c.getIntList(KEY_ALLOWED_FAMILIES), + c.getBoolean(KEY_ALLOW_BYPASS, false), + c.getBoolean(KEY_BLOCKING, false), + c.getInteger(KEY_MTU, null)) + + fun fromInvitation(f: File) = fromInvitation(Configurations().properties(f)) + fun fromInvitation(c: Configuration) = VpnInterfaceConfiguration( + c.getStringList(INVITATION_KEY_ADDRESSES) + .map { applyIgnoringException(CidrAddress.Companion::fromSlashSeparated, it) } + .filterNotNull(), + c.getStringList(INVITATION_KEY_ROUTES) + .map { it.substringBefore(' ') } + .map { CidrAddress.fromSlashSeparated(it) }) + + } + + fun write(f: File) = FileBasedConfigurationBuilder(PropertiesConfiguration::class.java) + .configure(Parameters().properties().setFile(f.apply { createNewFile() })).let { builder -> + builder.configuration.let { cfg -> + addresses.forEach { cfg.addProperty(KEY_ADDRESSES, it.toSlashSeparated()) } + routes.forEach { cfg.addProperty(KEY_ROUTES, it.toSlashSeparated()) } + } + builder.save() + } + +} diff --git a/app/src/main/java/org/pacien/tincapp/extensions/Android.kt b/app/src/main/java/org/pacien/tincapp/extensions/Android.kt new file mode 100644 index 0000000..f10c4c7 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/extensions/Android.kt @@ -0,0 +1,24 @@ +package org.pacien.tincapp.extensions + +import android.widget.ArrayAdapter +import android.widget.TextView +import org.pacien.tincapp.R +import org.pacien.tincapp.context.App + +/** + * @author pacien + */ +object Android { + + fun ArrayAdapter.setElements(elems: Collection) { + setNotifyOnChange(false) + clear() + addAll(elems) + notifyDataSetChanged() + setNotifyOnChange(true) + } + + fun TextView.setText(list: List) = + if (list.isNotEmpty()) text = list.joinToString("\n") else text = App.getContext().getString(R.string.value_none) + +} diff --git a/app/src/main/java/org/pacien/tincapp/extensions/ApacheConfiguration.kt b/app/src/main/java/org/pacien/tincapp/extensions/ApacheConfiguration.kt new file mode 100644 index 0000000..eb4bb47 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/extensions/ApacheConfiguration.kt @@ -0,0 +1,16 @@ +package org.pacien.tincapp.extensions + + +import org.apache.commons.configuration2.Configuration +import org.pacien.tincapp.data.CidrAddress + +/** + * @author pacien + */ +object ApacheConfiguration { + + fun Configuration.getStringList(key: String): List = getList(String::class.java, key, emptyList()) + fun Configuration.getCidrList(key: String): List = getStringList(key).map { CidrAddress.fromSlashSeparated(it) } + fun Configuration.getIntList(key: String): List = getList(Int::class.java, key, emptyList()) + +} diff --git a/app/src/main/java/org/pacien/tincapp/extensions/Java.kt b/app/src/main/java/org/pacien/tincapp/extensions/Java.kt new file mode 100644 index 0000000..af3c1af --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/extensions/Java.kt @@ -0,0 +1,18 @@ +package org.pacien.tincapp.extensions + +import java8.util.concurrent.CompletableFuture + +/** + * @author pacien + */ +object Java { + + fun CompletableFuture.exceptionallyAccept(fn: (Throwable) -> Unit) = exceptionally { fn(it); null }!! + + fun applyIgnoringException(f: (A) -> R, x: A, alt: R? = null) = try { + f(x) + } catch (_: Exception) { + alt + } + +} diff --git a/app/src/main/java/org/pacien/tincapp/extensions/VpnServiceBuilder.kt b/app/src/main/java/org/pacien/tincapp/extensions/VpnServiceBuilder.kt new file mode 100644 index 0000000..870668a --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/extensions/VpnServiceBuilder.kt @@ -0,0 +1,51 @@ +package org.pacien.tincapp.extensions + +import android.net.VpnService +import org.pacien.tincapp.data.CidrAddress +import org.pacien.tincapp.data.VpnInterfaceConfiguration +import org.pacien.tincapp.extensions.Java.applyIgnoringException + +/** + * @author pacien + */ +object VpnServiceBuilder { + + fun VpnService.Builder.addAddress(cidr: CidrAddress): VpnService.Builder = addAddress(cidr.address, cidr.prefix) + fun VpnService.Builder.addRoute(cidr: CidrAddress): VpnService.Builder = addRoute(cidr.address, cidr.prefix) + fun VpnService.Builder.allowBypass(allow: Boolean): VpnService.Builder = if (allow) allowBypass() else this + fun VpnService.Builder.overrideMtu(mtu: Int?): VpnService.Builder = if (mtu != null) setMtu(mtu) else this + + fun VpnService.Builder.addAddresses(cidrList: List): VpnService.Builder = + cidrList.fold(this, { net, cidr -> net.addAddress(cidr) }) + + fun VpnService.Builder.addRoutes(cidrList: List): VpnService.Builder = + cidrList.fold(this, { net, cidr -> net.addRoute(cidr) }) + + fun VpnService.Builder.addDnsServers(dnsList: List): VpnService.Builder = + dnsList.fold(this, { net, dns -> net.addDnsServer(dns) }) + + fun VpnService.Builder.addSearchDomains(domainList: List): VpnService.Builder = + domainList.fold(this, { net, domain -> net.addSearchDomain(domain) }) + + fun VpnService.Builder.allowFamilies(familyList: List): VpnService.Builder = + familyList.fold(this, { net, family -> net.allowFamily(family) }) + + fun VpnService.Builder.addAllowedApplications(apps: List): VpnService.Builder = + apps.fold(this, { net, app -> applyIgnoringException(net::addAllowedApplication, app, net)!! }) + + fun VpnService.Builder.addDisallowedApplications(apps: List): VpnService.Builder = + apps.fold(this, { net, app -> applyIgnoringException(net::addDisallowedApplication, app, net)!! }) + + fun VpnService.Builder.applyCfg(cfg: VpnInterfaceConfiguration): VpnService.Builder = this + .addAddresses(cfg.addresses) + .addRoutes(cfg.routes) + .addDnsServers(cfg.dnsServers) + .addSearchDomains(cfg.searchDomains) + .addAllowedApplications(cfg.allowedApplications) + .addDisallowedApplications(cfg.disallowedApplications) + .allowFamilies(cfg.allowedFamilies) + .allowBypass(cfg.allowBypass) + .setBlocking(cfg.blocking) + .overrideMtu(cfg.mtu) + +} diff --git a/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt b/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt index 45f901b..e2eae00 100644 --- a/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt +++ b/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt @@ -9,10 +9,11 @@ import org.pacien.tincapp.commands.Tinc import org.pacien.tincapp.commands.Tincd import org.pacien.tincapp.context.App import org.pacien.tincapp.context.AppPaths -import org.pacien.tincapp.utils.applyIgnoringException +import org.pacien.tincapp.data.VpnInterfaceConfiguration +import org.pacien.tincapp.extensions.Java.applyIgnoringException +import org.pacien.tincapp.extensions.VpnServiceBuilder.applyCfg import java.io.IOException - /** * @author pacien */ @@ -46,9 +47,9 @@ class TincVpnService : VpnService() { private fun startVpn(netName: String) { if (isConnected()) onDestroy() TincVpnService.netName = netName - TincVpnService.interfaceCfg = VpnInterfaceConfiguration(AppPaths.netConfFile(netName)) + TincVpnService.interfaceCfg = VpnInterfaceConfiguration.fromIfaceConfiguration(AppPaths.netConfFile(netName)) - val net = Builder().setSession(netName).apply(TincVpnService.interfaceCfg!!) + val net = Builder().setSession(netName).applyCfg(TincVpnService.interfaceCfg!!) applyIgnoringException(net::addDisallowedApplication, BuildConfig.APPLICATION_ID) try { diff --git a/app/src/main/java/org/pacien/tincapp/service/VpnInterfaceConfiguraton.kt b/app/src/main/java/org/pacien/tincapp/service/VpnInterfaceConfiguraton.kt deleted file mode 100644 index 50ccb20..0000000 --- a/app/src/main/java/org/pacien/tincapp/service/VpnInterfaceConfiguraton.kt +++ /dev/null @@ -1,62 +0,0 @@ -package org.pacien.tincapp.service - -/** - * @author pacien - */ - -import org.apache.commons.configuration2.Configuration -import org.apache.commons.configuration2.builder.fluent.Configurations -import java.io.File - -private val KEY_ADDRESSES = "Address" -private val KEY_ROUTES = "Route" -private val KEY_DNS_SERVERS = "DNSServer" -private val KEY_SEARCH_DOMAINS = "SearchDomain" -private val KEY_ALLOWED_APPLICATIONS = "AllowApplication" -private val KEY_DISALLOWED_APPLICATIONS = "DisallowApplication" -private val KEY_ALLOWED_FAMILIES = "AllowFamily" -private val KEY_ALLOW_BYPASS = "AllowBypass" -private val KEY_BLOCKING = "Blocking" -private val KEY_MTU = "MTU" - -private fun Configuration.getStringList(key: String): List = getList(String::class.java, key, emptyList()) -private fun Configuration.getCidrList(key: String): List = getStringList(key).map { CidrAddress(it) } -private fun Configuration.getIntList(key: String): List = getList(Int::class.java, key, emptyList()) - -data class CidrAddress(val address: String, val prefix: Int) { - constructor(slashSeparated: String) : - this(slashSeparated.substringBefore(SEPARATOR), Integer.parseInt(slashSeparated.substringAfter(SEPARATOR))) - - override fun toString() = address + SEPARATOR + prefix - - companion object { - private val SEPARATOR = "/" - } -} - -data class VpnInterfaceConfiguration(val addresses: List = emptyList(), - val routes: List = emptyList(), - val dnsServers: List = emptyList(), - val searchDomains: List = emptyList(), - val allowedApplications: List = emptyList(), - val disallowedApplications: List = emptyList(), - val allowedFamilies: List = emptyList(), - val allowBypass: Boolean = false, - val blocking: Boolean = false, - val mtu: Int? = null) { - - constructor(cfg: Configuration) : this( - cfg.getCidrList(KEY_ADDRESSES), - cfg.getCidrList(KEY_ROUTES), - cfg.getStringList(KEY_DNS_SERVERS), - cfg.getStringList(KEY_SEARCH_DOMAINS), - cfg.getStringList(KEY_ALLOWED_APPLICATIONS), - cfg.getStringList(KEY_DISALLOWED_APPLICATIONS), - cfg.getIntList(KEY_ALLOWED_FAMILIES), - cfg.getBoolean(KEY_ALLOW_BYPASS, false), - cfg.getBoolean(KEY_BLOCKING, false), - cfg.getInteger(KEY_MTU, null)) - - constructor(cfgFile: File) : this(Configurations().properties(cfgFile)) - -} diff --git a/app/src/main/java/org/pacien/tincapp/service/VpnServiceBuilderExtensions.kt b/app/src/main/java/org/pacien/tincapp/service/VpnServiceBuilderExtensions.kt deleted file mode 100644 index 22edff9..0000000 --- a/app/src/main/java/org/pacien/tincapp/service/VpnServiceBuilderExtensions.kt +++ /dev/null @@ -1,46 +0,0 @@ -package org.pacien.tincapp.service - -/** - * @author pacien - */ - -import android.net.VpnService -import org.pacien.tincapp.utils.applyIgnoringException - -fun VpnService.Builder.addAddress(cidr: CidrAddress): VpnService.Builder = addAddress(cidr.address, cidr.prefix) -fun VpnService.Builder.addRoute(cidr: CidrAddress): VpnService.Builder = addRoute(cidr.address, cidr.prefix) -fun VpnService.Builder.allowBypass(allow: Boolean): VpnService.Builder = if (allow) allowBypass() else this -fun VpnService.Builder.overrideMtu(mtu: Int?): VpnService.Builder = if (mtu != null) setMtu(mtu) else this - -fun VpnService.Builder.addAddresses(cidrList: List): VpnService.Builder = - cidrList.fold(this, { net, cidr -> net.addAddress(cidr) }) - -fun VpnService.Builder.addRoutes(cidrList: List): VpnService.Builder = - cidrList.fold(this, { net, cidr -> net.addRoute(cidr) }) - -fun VpnService.Builder.addDnsServers(dnsList: List): VpnService.Builder = - dnsList.fold(this, { net, dns -> net.addDnsServer(dns) }) - -fun VpnService.Builder.addSearchDomains(domainList: List): VpnService.Builder = - domainList.fold(this, { net, domain -> net.addSearchDomain(domain) }) - -fun VpnService.Builder.allowFamilies(familyList: List): VpnService.Builder = - familyList.fold(this, { net, family -> net.allowFamily(family) }) - -fun VpnService.Builder.addAllowedApplications(apps: List): VpnService.Builder = - apps.fold(this, { net, app -> applyIgnoringException(net::addAllowedApplication, app, net)!! }) - -fun VpnService.Builder.addDisallowedApplications(apps: List): VpnService.Builder = - apps.fold(this, { net, app -> applyIgnoringException(net::addDisallowedApplication, app, net)!! }) - -fun VpnService.Builder.apply(cfg: VpnInterfaceConfiguration): VpnService.Builder = this - .addAddresses(cfg.addresses) - .addRoutes(cfg.routes) - .addDnsServers(cfg.dnsServers) - .addSearchDomains(cfg.searchDomains) - .addAllowedApplications(cfg.allowedApplications) - .addDisallowedApplications(cfg.disallowedApplications) - .allowFamilies(cfg.allowedFamilies) - .allowBypass(cfg.allowBypass) - .setBlocking(cfg.blocking) - .overrideMtu(cfg.mtu) diff --git a/app/src/main/java/org/pacien/tincapp/utils/AndroidExtensions.kt b/app/src/main/java/org/pacien/tincapp/utils/AndroidExtensions.kt deleted file mode 100644 index 732b5b2..0000000 --- a/app/src/main/java/org/pacien/tincapp/utils/AndroidExtensions.kt +++ /dev/null @@ -1,23 +0,0 @@ -package org.pacien.tincapp.utils - -import android.widget.ArrayAdapter -import android.widget.TextView -import org.pacien.tincapp.R -import org.pacien.tincapp.context.App - -/** - * @author pacien - */ - -fun ArrayAdapter.setElements(list: Collection) { - setNotifyOnChange(false) - clear() - addAll(list) - notifyDataSetChanged() - setNotifyOnChange(true) -} - -fun TextView.setText(list: List) { - if (list.isNotEmpty()) text = list.joinToString("\n") - else text = App.getContext().getString(R.string.value_none) -} diff --git a/app/src/main/java/org/pacien/tincapp/utils/Functions.kt b/app/src/main/java/org/pacien/tincapp/utils/Functions.kt deleted file mode 100644 index 6ed77ce..0000000 --- a/app/src/main/java/org/pacien/tincapp/utils/Functions.kt +++ /dev/null @@ -1,11 +0,0 @@ -package org.pacien.tincapp.utils - -/** - * @author pacien - */ - -fun applyIgnoringException(f: (A) -> R, x: A, alt: R? = null) = try { - f(x) -} catch (_: Exception) { - alt -} diff --git a/app/src/main/res/layout/page_configure.xml b/app/src/main/res/layout/page_configure.xml index 35a7416..ab1033e 100644 --- a/app/src/main/res/layout/page_configure.xml +++ b/app/src/main/res/layout/page_configure.xml @@ -63,7 +63,7 @@ + android:onClick="openGenerateConfDialog"> + android:onClick="openJoinNetworkDialog"> Disconnect Tinc network name + Invitation URL Connect to network Request VPN permissions @@ -34,14 +35,23 @@ Disallowed applications Allow bypass Nodes + Error + New network + Join network Close + Cancel Project website Generate node configuration and keys Join network via invitation URL + Create + Join No network configuration has been found. No known node + Generating node configuration… + Joining network… + Network configuration successfully created. none yes -- cgit v1.2.3