aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpacien2020-12-08 16:16:08 +0100
committerpacien2020-12-08 16:16:08 +0100
commit20ecd9840f1e237dba79674b71e49b43b074902e (patch)
tree4594947f1faf96e9851d95c9cfc7bfad0c9ff923
parentb1f98caca2f6960f7abb3ef7f7c27b903e1ef929 (diff)
downloadtincapp-20ecd9840f1e237dba79674b71e49b43b074902e.tar.gz
app: add configuration FTP server
This is a ridiculous workaround to make the configuration (and other files) accessible to the user necessary after the new storage access restriction enforced in Android 11 which prevent other applications from accessing the supposedly public application's directory. The app's internal private storage directory is now exposed to the user through an embedded FTP server that the user can turn on and off from the configuration activity. The user can then play with the configuration and retrieve logs through a remote or local FTP client application of their choice. GitHub: closes #103
-rw-r--r--app/build.gradle9
-rw-r--r--app/proguard-rules.pro4
-rw-r--r--app/src/main/AndroidManifest.xml4
-rw-r--r--app/src/main/java/org/pacien/tincapp/activities/configure/ConfigurationFtpServerFragment.kt72
-rw-r--r--app/src/main/java/org/pacien/tincapp/service/ConfigurationFtpService.kt128
-rw-r--r--app/src/main/res/layout/configure_activity.xml24
-rw-r--r--app/src/main/res/layout/configure_tools_configuration_ftp_server_fragment.xml86
-rw-r--r--app/src/main/res/values-nb-rNO/strings.xml1
-rw-r--r--app/src/main/res/values-zh-rCN/strings.xml1
-rw-r--r--app/src/main/res/values-zh-rHK/strings.xml1
-rw-r--r--app/src/main/res/values-zh-rTW/strings.xml1
-rw-r--r--app/src/main/res/values/strings.xml8
-rw-r--r--readme.md1
13 files changed, 327 insertions, 13 deletions
diff --git a/app/build.gradle b/app/build.gradle
index 99e9226..41c75ef 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -70,6 +70,14 @@ android {
70 buildFeatures { 70 buildFeatures {
71 dataBinding = true 71 dataBinding = true
72 } 72 }
73
74 packagingOptions {
75 // clashing in dependencies (Apache Mina FtpServer)
76 exclude 'META-INF/DEPENDENCIES'
77 exclude 'META-INF/spring.schemas'
78 exclude 'META-INF/spring.handlers'
79 exclude 'META-INF/license.txt'
80 }
73} 81}
74 82
75dependencies { 83dependencies {
@@ -88,6 +96,7 @@ dependencies {
88 implementation('org.apache.commons:commons-configuration2:2.3') { exclude group: 'commons-logging', module: 'commons-logging' } 96 implementation('org.apache.commons:commons-configuration2:2.3') { exclude group: 'commons-logging', module: 'commons-logging' }
89 implementation('commons-beanutils:commons-beanutils:1.9.3') { exclude group: 'commons-logging', module: 'commons-logging' } 97 implementation('commons-beanutils:commons-beanutils:1.9.3') { exclude group: 'commons-logging', module: 'commons-logging' }
90 implementation('commons-io:commons-io:2.6') { exclude group: 'commons-logging', module: 'commons-logging' } 98 implementation('commons-io:commons-io:2.6') { exclude group: 'commons-logging', module: 'commons-logging' }
99 implementation('org.apache.ftpserver:ftpserver:1.1.1') { exclude group: 'org.slf4j', module: 'slf4j-log4j12' }
91} 100}
92 101
93repositories { 102repositories {
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 5100915..ffbb3e3 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -1,5 +1,5 @@
1# Tinc App, an Android binding and user interface for the tinc mesh VPN daemon 1# Tinc App, an Android binding and user interface for the tinc mesh VPN daemon
2# Copyright (C) 2017-2019 Pacien TRAN-GIRARD 2# Copyright (C) 2017-2020 Pacien TRAN-GIRARD
3# 3#
4# This program is free software: you can redistribute it and/or modify 4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by 5# it under the terms of the GNU General Public License as published by
@@ -15,6 +15,8 @@
15# along with this program. If not, see <https://www.gnu.org/licenses/>. 15# along with this program. If not, see <https://www.gnu.org/licenses/>.
16 16
17-keep class org.apache.commons.** { *; } 17-keep class org.apache.commons.** { *; }
18-keep class org.apache.mina.** { *; }
19-keep class org.apache.ftpserver.** { *; }
18-keep class org.bouncycastle.** 20-keep class org.bouncycastle.**
19-keep class ch.qos.** { *; } 21-keep class ch.qos.** { *; }
20-keep class org.slf4j.** { *; } 22-keep class org.slf4j.** { *; }
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c86ec8f..020d62d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -81,6 +81,10 @@
81 </intent-filter> 81 </intent-filter>
82 </service> 82 </service>
83 83
84 <service
85 android:name="org.pacien.tincapp.service.ConfigurationFtpService">
86 </service>
87
84 </application> 88 </application>
85 89
86</manifest> 90</manifest>
diff --git a/app/src/main/java/org/pacien/tincapp/activities/configure/ConfigurationFtpServerFragment.kt b/app/src/main/java/org/pacien/tincapp/activities/configure/ConfigurationFtpServerFragment.kt
new file mode 100644
index 0000000..b97a15e
--- /dev/null
+++ b/app/src/main/java/org/pacien/tincapp/activities/configure/ConfigurationFtpServerFragment.kt
@@ -0,0 +1,72 @@
1/*
2 * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon
3 * Copyright (C) 2017-2020 Pacien TRAN-GIRARD
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19package org.pacien.tincapp.activities.configure
20
21import android.content.Intent
22import android.os.Bundle
23import android.view.LayoutInflater
24import android.view.View
25import android.view.ViewGroup
26import androidx.databinding.Observable
27import androidx.databinding.ObservableBoolean
28import org.pacien.tincapp.activities.BaseFragment
29import org.pacien.tincapp.databinding.ConfigureToolsConfigurationFtpServerFragmentBinding
30import org.pacien.tincapp.service.ConfigurationFtpService
31
32/**
33 * @author pacien
34 */
35class ConfigurationFtpServerFragment : BaseFragment() {
36 private val ftpServerStartListener = object : Observable.OnPropertyChangedCallback() {
37 override fun onPropertyChanged(sender: Observable, propertyId: Int) {
38 binding.ftpEnabled = (sender as ObservableBoolean).get()
39 }
40 }
41
42 private lateinit var binding: ConfigureToolsConfigurationFtpServerFragmentBinding
43
44 override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
45 binding = ConfigureToolsConfigurationFtpServerFragmentBinding.inflate(inflater, container, false)
46 binding.ftpUsername = ConfigurationFtpService.FTP_USERNAME
47 binding.ftpPassword = ConfigurationFtpService.FTP_PASSWORD
48 binding.ftpPort = ConfigurationFtpService.FTP_PORT
49 binding.toggleFtpState = { toggleServer() }
50 return binding.root
51 }
52
53 override fun onResume() {
54 super.onResume()
55 ConfigurationFtpService.runningState.addOnPropertyChangedCallback(ftpServerStartListener)
56 binding.ftpEnabled = ConfigurationFtpService.runningState.get()
57 }
58
59 override fun onPause() {
60 ConfigurationFtpService.runningState.removeOnPropertyChangedCallback(ftpServerStartListener)
61 super.onPause()
62 }
63
64 private fun toggleServer() {
65 val targetServiceIntent = Intent(requireContext(), ConfigurationFtpService::class.java)
66
67 if (binding.ftpEnabled)
68 requireContext().stopService(targetServiceIntent)
69 else
70 requireContext().startService(targetServiceIntent)
71 }
72}
diff --git a/app/src/main/java/org/pacien/tincapp/service/ConfigurationFtpService.kt b/app/src/main/java/org/pacien/tincapp/service/ConfigurationFtpService.kt
new file mode 100644
index 0000000..c562768
--- /dev/null
+++ b/app/src/main/java/org/pacien/tincapp/service/ConfigurationFtpService.kt
@@ -0,0 +1,128 @@
1/*
2 * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon
3 * Copyright (C) 2017-2020 Pacien TRAN-GIRARD
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19package org.pacien.tincapp.service
20
21import android.app.Service
22import android.content.Intent
23import android.os.IBinder
24import androidx.databinding.ObservableBoolean
25import org.apache.ftpserver.FtpServer
26import org.apache.ftpserver.FtpServerFactory
27import org.apache.ftpserver.ftplet.*
28import org.apache.ftpserver.listener.ListenerFactory
29import org.apache.ftpserver.usermanager.UsernamePasswordAuthentication
30import org.apache.ftpserver.usermanager.impl.WritePermission
31import org.pacien.tincapp.R
32import org.pacien.tincapp.context.App
33import org.pacien.tincapp.extensions.Java.defaultMessage
34import org.slf4j.LoggerFactory
35import java.io.IOException
36
37/**
38 * FTP server service allowing a remote and local user to access and modify configuration files in
39 * the application's context.
40 *
41 * @author pacien
42 */
43class ConfigurationFtpService : Service() {
44 companion object {
45 const val FTP_PORT = 65521 // tinc port `concat` FTP port
46 const val FTP_USERNAME = "tincapp"
47 val FTP_HOME_DIR = App.getContext().applicationInfo.dataDir!!
48 val FTP_PASSWORD = generateRandomString(8)
49
50 val runningState = ObservableBoolean(false)
51
52 private fun generateRandomString(length: Int): String {
53 val alphabet = ('a'..'z') + ('A'..'Z') + ('0'..'9')
54 return List(length) { alphabet.random() }.joinToString("")
55 }
56 }
57
58 private val log by lazy { LoggerFactory.getLogger(this.javaClass)!! }
59 private var sftpServer: FtpServer? = null
60
61 override fun onBind(intent: Intent): IBinder? = null // non-bindable service
62
63 override fun onDestroy() {
64 sftpServer?.stop()
65 sftpServer = null
66 runningState.set(false)
67 log.info("Stopped FTP server")
68 super.onDestroy()
69 }
70
71 override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
72 val ftpUser = StaticFtpUser(FTP_USERNAME, FTP_PASSWORD, FTP_HOME_DIR, listOf(WritePermission()))
73 sftpServer = setupSingleUserServer(ftpUser).also {
74 try {
75 it.start()
76 runningState.set(true)
77 log.info("Started FTP server on port {}", FTP_PORT)
78 } catch (e: IOException) {
79 log.error("Could not start FTP server", e)
80 App.alert(R.string.notification_error_title_unable_to_start_ftp_server, e.defaultMessage())
81 }
82 }
83
84 return START_NOT_STICKY
85 }
86
87 private fun setupSingleUserServer(ftpUser: User): FtpServer {
88 return FtpServerFactory()
89 .apply { addListener("default", ListenerFactory().apply { port = FTP_PORT }.createListener()) }
90 .apply { userManager = StaticFtpUserManager(listOf(ftpUser)) }
91 .createServer()
92 }
93
94 private class StaticFtpUserManager(users: List<User>) : UserManager {
95 private val userMap: Map<String, User> = users.map { it.name to it }.toMap()
96 override fun getUserByName(username: String?): User? = userMap[username]
97 override fun getAllUserNames(): Array<String> = userMap.keys.toTypedArray()
98 override fun doesExist(username: String?): Boolean = username in userMap
99 override fun delete(username: String?) = throw UnsupportedOperationException()
100 override fun save(user: User?) = throw UnsupportedOperationException()
101 override fun getAdminName(): String = throw UnsupportedOperationException()
102 override fun isAdmin(username: String?): Boolean = throw UnsupportedOperationException()
103 override fun authenticate(authentication: Authentication?): User = when (authentication) {
104 is UsernamePasswordAuthentication -> getUserByName(authentication.username).let {
105 if (it != null && authentication.password == it.password) it
106 else throw AuthenticationFailedException()
107 }
108 else -> throw IllegalArgumentException()
109 }
110 }
111
112 private data class StaticFtpUser(
113 private val name: String,
114 private val password: String,
115 private val homeDirectory: String,
116 private val authorities: List<Authority>
117 ) : User {
118 override fun getName(): String = name
119 override fun getPassword(): String = password
120 override fun getAuthorities(): List<Authority> = authorities
121 override fun getAuthorities(clazz: Class<out Authority>): List<Authority> = authorities.filter(clazz::isInstance)
122 override fun getMaxIdleTime(): Int = 0 // unlimited
123 override fun getEnabled(): Boolean = true
124 override fun getHomeDirectory(): String = homeDirectory
125 override fun authorize(request: AuthorizationRequest?): AuthorizationRequest? =
126 authorities.filter { it.canAuthorize(request) }.fold(request) { req, auth -> auth.authorize(req) }
127 }
128}
diff --git a/app/src/main/res/layout/configure_activity.xml b/app/src/main/res/layout/configure_activity.xml
index 6097384..2fde4a2 100644
--- a/app/src/main/res/layout/configure_activity.xml
+++ b/app/src/main/res/layout/configure_activity.xml
@@ -2,7 +2,7 @@
2 2
3<!-- 3<!--
4 * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon 4 * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon
5 * Copyright (C) 2017-2018 Pacien TRAN-GIRARD 5 * Copyright (C) 2017-2020 Pacien TRAN-GIRARD
6 * 6 *
7 * This program is free software: you can redistribute it and/or modify 7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by 8 * it under the terms of the GNU General Public License as published by
@@ -28,23 +28,33 @@
28 android:layout_height="wrap_content" 28 android:layout_height="wrap_content"
29 android:orientation="vertical"> 29 android:orientation="vertical">
30 30
31 <TextView
32 style="@style/AppTheme.SectionTitle"
33 android:text="@string/configure_activity_title_configuration_server"/>
34
35 <fragment
36 android:id="@+id/configure_activity_configuration_sftp_server_fragment"
37 android:name="org.pacien.tincapp.activities.configure.ConfigurationFtpServerFragment"
38 android:layout_width="match_parent"
39 android:layout_height="wrap_content"/>
40
31 <TextView 41 <TextView
32 style="@style/AppTheme.SectionTitle" 42 style="@style/AppTheme.SectionTitle"
33 android:text="@string/configure_activity_title_path_info"/> 43 android:text="@string/configure_activity_title_tools"/>
34 44
35 <fragment 45 <fragment
36 android:id="@+id/configure_activity_path_info_fragment" 46 android:id="@+id/configure_activity_tools_fragment"
37 android:name="org.pacien.tincapp.activities.configure.PathInfoFragment" 47 android:name="org.pacien.tincapp.activities.configure.ToolsFragment"
38 android:layout_width="match_parent" 48 android:layout_width="match_parent"
39 android:layout_height="wrap_content"/> 49 android:layout_height="wrap_content"/>
40 50
41 <TextView 51 <TextView
42 style="@style/AppTheme.SectionTitle" 52 style="@style/AppTheme.SectionTitle"
43 android:text="@string/configure_activity_title_tools"/> 53 android:text="@string/configure_activity_title_internal_paths_info"/>
44 54
45 <fragment 55 <fragment
46 android:id="@+id/configure_activity_tools_fragment" 56 android:id="@+id/configure_activity_path_info_fragment"
47 android:name="org.pacien.tincapp.activities.configure.ToolsFragment" 57 android:name="org.pacien.tincapp.activities.configure.PathInfoFragment"
48 android:layout_width="match_parent" 58 android:layout_width="match_parent"
49 android:layout_height="wrap_content"/> 59 android:layout_height="wrap_content"/>
50 60
diff --git a/app/src/main/res/layout/configure_tools_configuration_ftp_server_fragment.xml b/app/src/main/res/layout/configure_tools_configuration_ftp_server_fragment.xml
new file mode 100644
index 0000000..24f3c36
--- /dev/null
+++ b/app/src/main/res/layout/configure_tools_configuration_ftp_server_fragment.xml
@@ -0,0 +1,86 @@
1<?xml version="1.0" encoding="utf-8"?>
2
3<!--
4 * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon
5 * Copyright (C) 2017-2020 Pacien TRAN-GIRARD
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19-->
20
21<layout xmlns:android="http://schemas.android.com/apk/res/android">
22
23 <data>
24
25 <variable
26 name="ftpEnabled"
27 type="boolean" />
28
29 <variable
30 name="ftpUsername"
31 type="String" />
32
33 <variable
34 name="ftpPassword"
35 type="String" />
36
37 <variable
38 name="ftpPort"
39 type="int" />
40
41 <variable
42 name="toggleFtpState"
43 type="kotlin.jvm.functions.Function0&lt;kotlin.Unit>" />
44
45 </data>
46
47 <LinearLayout style="@style/AppTheme.List">
48
49 <LinearLayout
50 style="@style/AppTheme.ListBlock.Clickable"
51 android:onClick="@{() -> toggleFtpState.invoke()}"
52 android:orientation="horizontal">
53
54 <LinearLayout
55 android:layout_width="wrap_content"
56 android:layout_height="wrap_content"
57 android:layout_weight="1"
58 android:orientation="vertical">
59
60 <TextView
61 style="@style/AppTheme.BlockTitle"
62 android:layout_width="wrap_content"
63 android:layout_height="wrap_content"
64 android:text="@string/configure_configuration_server_ftp_access" />
65
66 <TextView
67 style="@style/AppTheme.BlockContent"
68 android:layout_width="wrap_content"
69 android:layout_height="wrap_content"
70 android:text="@{ftpEnabled ? @string/configure_configuration_server_ftp_access_endpoint_format(ftpUsername, ftpPassword, ftpPort) : @string/configure_configuration_server_ftp_access_not_active}"
71 android:textIsSelectable="@{ftpEnabled}" />
72
73 </LinearLayout>
74
75 <androidx.appcompat.widget.SwitchCompat
76 android:layout_width="wrap_content"
77 android:layout_height="match_parent"
78 android:layout_weight="0"
79 android:checked="@{ftpEnabled}"
80 android:onClick="@{() -> toggleFtpState.invoke()}" />
81
82 </LinearLayout>
83
84 </LinearLayout>
85
86</layout>
diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml
index 9c330a7..346a2d1 100644
--- a/app/src/main/res/values-nb-rNO/strings.xml
+++ b/app/src/main/res/values-nb-rNO/strings.xml
@@ -80,7 +80,6 @@
80 <string name="configure_tools_message_invalid_network_name">Ugyldig nettverksnavn.</string> 80 <string name="configure_tools_message_invalid_network_name">Ugyldig nettverksnavn.</string>
81 <string name="configure_tools_message_network_configuration_written">Nettverksoppsett skrevet.</string> 81 <string name="configure_tools_message_network_configuration_written">Nettverksoppsett skrevet.</string>
82 82
83 <string name="configure_activity_title_path_info">Sti-info</string>
84 <string name="configure_path_info_title_configuration_directory">Oppsettsmappe</string> 83 <string name="configure_path_info_title_configuration_directory">Oppsettsmappe</string>
85 <string name="configure_path_info_title_log_directory">Loggingsmappe</string> 84 <string name="configure_path_info_title_log_directory">Loggingsmappe</string>
86 <string name="configure_path_info_title_tinc_binary">tinc-binærfil</string> 85 <string name="configure_path_info_title_tinc_binary">tinc-binærfil</string>
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index b5d12b1..536403e 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -80,7 +80,6 @@
80 <string name="configure_tools_message_invalid_network_name">网络名称无效。</string> 80 <string name="configure_tools_message_invalid_network_name">网络名称无效。</string>
81 <string name="configure_tools_message_network_configuration_written">网络配置写入成功。</string> 81 <string name="configure_tools_message_network_configuration_written">网络配置写入成功。</string>
82 82
83 <string name="configure_activity_title_path_info">路径信息</string>
84 <string name="configure_path_info_title_configuration_directory">配置目录</string> 83 <string name="configure_path_info_title_configuration_directory">配置目录</string>
85 <string name="configure_path_info_title_log_directory">日志目录</string> 84 <string name="configure_path_info_title_log_directory">日志目录</string>
86 <string name="configure_path_info_title_tinc_binary">Tinc 二进制文件</string> 85 <string name="configure_path_info_title_tinc_binary">Tinc 二进制文件</string>
diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml
index bddb004..e9c48d2 100644
--- a/app/src/main/res/values-zh-rHK/strings.xml
+++ b/app/src/main/res/values-zh-rHK/strings.xml
@@ -80,7 +80,6 @@
80 <string name="configure_tools_message_invalid_network_name">網絡名稱無效。</string> 80 <string name="configure_tools_message_invalid_network_name">網絡名稱無效。</string>
81 <string name="configure_tools_message_network_configuration_written">網絡配置寫入成功。</string> 81 <string name="configure_tools_message_network_configuration_written">網絡配置寫入成功。</string>
82 82
83 <string name="configure_activity_title_path_info">路徑信息</string>
84 <string name="configure_path_info_title_configuration_directory">配置目錄</string> 83 <string name="configure_path_info_title_configuration_directory">配置目錄</string>
85 <string name="configure_path_info_title_log_directory">日誌目錄</string> 84 <string name="configure_path_info_title_log_directory">日誌目錄</string>
86 <string name="configure_path_info_title_tinc_binary">Tinc 二進制文件</string> 85 <string name="configure_path_info_title_tinc_binary">Tinc 二進制文件</string>
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index c423b15..82eec1d 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -80,7 +80,6 @@
80 <string name="configure_tools_message_invalid_network_name">網路名稱無效。</string> 80 <string name="configure_tools_message_invalid_network_name">網路名稱無效。</string>
81 <string name="configure_tools_message_network_configuration_written">網路配置寫入成功。</string> 81 <string name="configure_tools_message_network_configuration_written">網路配置寫入成功。</string>
82 82
83 <string name="configure_activity_title_path_info">路徑資訊</string>
84 <string name="configure_path_info_title_configuration_directory">配置目錄</string> 83 <string name="configure_path_info_title_configuration_directory">配置目錄</string>
85 <string name="configure_path_info_title_log_directory">日誌目錄</string> 84 <string name="configure_path_info_title_log_directory">日誌目錄</string>
86 <string name="configure_path_info_title_tinc_binary">Tinc 二進位制檔案</string> 85 <string name="configure_path_info_title_tinc_binary">Tinc 二進位制檔案</string>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 770f61c..cae7fe5 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -62,6 +62,7 @@
62 <string name="notification_error_channel_name">Errors</string> 62 <string name="notification_error_channel_name">Errors</string>
63 <string name="notification_error_action_open_manual">Open manual</string> 63 <string name="notification_error_action_open_manual">Open manual</string>
64 <string name="notification_error_title_unable_to_start_tinc">Could not start tinc</string> 64 <string name="notification_error_title_unable_to_start_tinc">Could not start tinc</string>
65 <string name="notification_error_title_unable_to_start_ftp_server">Could not start FTP server</string>
65 <string name="notification_error_message_could_not_read_private_key_format">Could not read private tinc keys:\n%1$s</string> 66 <string name="notification_error_message_could_not_read_private_key_format">Could not read private tinc keys:\n%1$s</string>
66 <string name="notification_error_message_could_not_read_network_configuration_format">Could not read network interface configuration:\n%1$s</string> 67 <string name="notification_error_message_could_not_read_network_configuration_format">Could not read network interface configuration:\n%1$s</string>
67 <string name="notification_error_message_could_not_bind_iface">Could not bind network interface. Is another VPN running?</string> 68 <string name="notification_error_message_could_not_bind_iface">Could not bind network interface. Is another VPN running?</string>
@@ -84,7 +85,12 @@
84 <string name="configure_tools_message_invalid_network_name">Invalid network name.</string> 85 <string name="configure_tools_message_invalid_network_name">Invalid network name.</string>
85 <string name="configure_tools_message_network_configuration_written">Network configuration written.</string> 86 <string name="configure_tools_message_network_configuration_written">Network configuration written.</string>
86 87
87 <string name="configure_activity_title_path_info">Path info</string> 88 <string name="configure_activity_title_configuration_server">Configuration server</string>
89 <string name="configure_configuration_server_ftp_access">FTP access</string>
90 <string name="configure_configuration_server_ftp_access_endpoint_format">User: %1$s, password: %2$s, port: %3$d</string>
91 <string name="configure_configuration_server_ftp_access_not_active">Not active</string>
92
93 <string name="configure_activity_title_internal_paths_info">Internal paths info</string>
88 <string name="configure_path_info_title_configuration_directory">Configuration directory</string> 94 <string name="configure_path_info_title_configuration_directory">Configuration directory</string>
89 <string name="configure_path_info_title_log_directory">Log directory</string> 95 <string name="configure_path_info_title_log_directory">Log directory</string>
90 <string name="configure_path_info_title_tinc_binary">tinc binary</string> 96 <string name="configure_path_info_title_tinc_binary">tinc binary</string>
diff --git a/readme.md b/readme.md
index 070023f..5df80d0 100644
--- a/readme.md
+++ b/readme.md
@@ -57,6 +57,7 @@ Builds of this software embed and make use of the following libraries:
57* logback-android, licensed under the GNU Lesser General Public License v2.1 57* logback-android, licensed under the GNU Lesser General Public License v2.1
58* Apache Commons Configuration, licensed under the Apache v2.0 License 58* Apache Commons Configuration, licensed under the Apache v2.0 License
59* Apache Commons BeanUtils, licensed under the Apache v2.0 License 59* Apache Commons BeanUtils, licensed under the Apache v2.0 License
60* Apache Mina FtpServer, licensed under the Apache v2.0 License
60* LZO, licensed under the GNU General Public License v2.0 61* LZO, licensed under the GNU General Public License v2.0
61* LibreSSL libcrypto, licensed under the OpenSSL License, ISC License, public domain 62* LibreSSL libcrypto, licensed under the OpenSSL License, ISC License, public domain
62* tinc, licensed under the GNU General Public License v2.0 63* tinc, licensed under the GNU General Public License v2.0