fix: fdroid build and ssid comma

Fixes bug where commas in SSID names were splitting into multiple SSIDs due to database type converters.
Closes #48

Fixes bug where F-Droid pipeline was failing to build due to lack of proguard rule. Closes #47

Fixes bug where crashes could happen if config QR code or file has improperly configured data.

Bump versions.
This commit is contained in:
Zane Schepke 2023-10-26 22:05:20 -04:00
parent 513d08998b
commit b70ecbdfff
9 changed files with 62 additions and 49 deletions

View File

@ -14,10 +14,8 @@ android {
applicationId = "com.zaneschepke.wireguardautotunnel" applicationId = "com.zaneschepke.wireguardautotunnel"
minSdk = 26 minSdk = 26
targetSdk = 34 targetSdk = 34
versionCode = 31700 versionCode = 31900
versionName = "3.1.7" versionName = "3.1.9"
multiDexEnabled = true
ksp { ksp {
arg("room.schemaLocation", "$projectDir/schemas") arg("room.schemaLocation", "$projectDir/schemas")
@ -49,6 +47,7 @@ android {
productFlavors { productFlavors {
create("fdroid") { create("fdroid") {
dimension = "type" dimension = "type"
proguardFile("fdroid-rules.pro")
} }
create("general") { create("general") {
dimension = "type" dimension = "type"

1
app/fdroid-rules.pro Normal file
View File

@ -0,0 +1 @@
-dontwarn com.google.errorprone.annotations.**

View File

@ -1,15 +1,17 @@
package com.zaneschepke.wireguardautotunnel.repository package com.zaneschepke.wireguardautotunnel.repository
import androidx.room.TypeConverter import androidx.room.TypeConverter
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
class DatabaseListConverters { class DatabaseListConverters {
@TypeConverter @TypeConverter
fun listToString(value: MutableList<String>): String { fun listToString(value: MutableList<String>): String {
return value.joinToString(",") return Json.encodeToString(value)
} }
@TypeConverter @TypeConverter
fun stringToList(value: String): MutableList<String> { fun stringToList(value: String): MutableList<String> {
if(value.isEmpty()) return mutableListOf() if(value.isEmpty()) return mutableListOf()
return value.split(",").toMutableList() return Json.decodeFromString<MutableList<String>>(value)
} }
} }

View File

@ -1,10 +1,12 @@
package com.zaneschepke.wireguardautotunnel.ui.common.prompt package com.zaneschepke.wireguardautotunnel.ui.common.prompt
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Info import androidx.compose.material.icons.rounded.Info
@ -42,14 +44,15 @@ fun CustomSnackBar(
if (isRtl) LayoutDirection.Rtl else LayoutDirection.Ltr if (isRtl) LayoutDirection.Rtl else LayoutDirection.Ltr
) { ) {
Row( Row(
modifier = Modifier.fillMaxSize(), modifier = Modifier.width(IntrinsicSize.Max).height(IntrinsicSize.Min),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceEvenly horizontalArrangement = Arrangement.Start
) { ) {
Icon( Icon(
Icons.Rounded.Info, Icons.Rounded.Info,
contentDescription = stringResource(R.string.info), contentDescription = stringResource(R.string.info),
tint = Color.White tint = Color.White,
modifier = Modifier.padding(end = 10.dp)
) )
Text(message, color = Color.White, modifier = Modifier.padding(end = 5.dp)) Text(message, color = Color.White, modifier = Modifier.padding(end = 5.dp))
} }

View File

@ -168,12 +168,14 @@ fun MainScreen(
val scanLauncher = rememberLauncherForActivityResult( val scanLauncher = rememberLauncherForActivityResult(
contract = ScanContract(), contract = ScanContract(),
onResult = { onResult = {
scope.launch {
try { try {
viewModel.onTunnelQrResult(it.contents) viewModel.onTunnelQrResult(it.contents)
} catch (e: Exception) { } catch (e: Exception) {
showSnackbarMessage(context.getString(R.string.qr_result_failed)) showSnackbarMessage(context.getString(R.string.qr_result_failed))
} }
} }
}
) )
if(showPrimaryChangeAlertDialog) { if(showPrimaryChangeAlertDialog) {

View File

@ -28,6 +28,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.InputStream import java.io.InputStream
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import javax.inject.Inject import javax.inject.Inject
@ -117,32 +118,26 @@ class MainViewModel @Inject constructor(
} }
private fun validateConfigString(config: String) { private fun validateConfigString(config: String) {
if (!config.contains(application.getString(R.string.config_validation))) { TunnelConfig.configFromQuick(config)
throw WgTunnelException(application.getString(R.string.config_validation))
}
} }
fun onTunnelQrResult(result: String) { suspend fun onTunnelQrResult(result: String) {
viewModelScope.launch(Dispatchers.IO) {
try { try {
validateConfigString(result) validateConfigString(result)
val tunnelConfig = val tunnelConfig =
TunnelConfig(name = NumberUtils.generateRandomTunnelName(), wgQuick = result) TunnelConfig(name = NumberUtils.generateRandomTunnelName(), wgQuick = result)
addTunnel(tunnelConfig) addTunnel(tunnelConfig)
} catch (e: WgTunnelException) { } catch (e : Exception) {
throw WgTunnelException( throw WgTunnelException(e)
e.message ?: application.getString(R.string.unknown_error_message)
)
}
} }
} }
private fun saveTunnelConfigFromStream(stream: InputStream, fileName: String) { private suspend fun saveTunnelConfigFromStream(stream: InputStream, fileName: String) {
viewModelScope.launch(Dispatchers.IO) {
val bufferReader = stream.bufferedReader(charset = Charsets.UTF_8) val bufferReader = stream.bufferedReader(charset = Charsets.UTF_8)
val config = Config.parse(bufferReader) val config = Config.parse(bufferReader)
val tunnelName = getNameFromFileName(fileName) val tunnelName = getNameFromFileName(fileName)
addTunnel(TunnelConfig(name = tunnelName, wgQuick = config.toWgQuickString())) addTunnel(TunnelConfig(name = tunnelName, wgQuick = config.toWgQuickString()))
withContext(Dispatchers.IO) {
stream.close() stream.close()
} }
} }
@ -161,9 +156,8 @@ class MainViewModel @Inject constructor(
Constants.ZIP_FILE_EXTENSION -> saveTunnelsFromZipUri(uri) Constants.ZIP_FILE_EXTENSION -> saveTunnelsFromZipUri(uri)
else -> throw WgTunnelException(application.getString(R.string.file_extension_message)) else -> throw WgTunnelException(application.getString(R.string.file_extension_message))
} }
} catch (e: Exception) { } catch (e: Exception) {
throw WgTunnelException(e.message ?: "Error importing file") throw WgTunnelException(e)
} }
} }
@ -182,7 +176,7 @@ class MainViewModel @Inject constructor(
} }
} }
private fun saveTunnelFromConfUri(name : String, uri: Uri) { private suspend fun saveTunnelFromConfUri(name : String, uri: Uri) {
val stream = getInputStreamFromUri(uri) val stream = getInputStreamFromUri(uri)
saveTunnelConfigFromStream(stream, name) saveTunnelConfigFromStream(stream, name)
} }

View File

@ -1,3 +1,15 @@
package com.zaneschepke.wireguardautotunnel.util package com.zaneschepke.wireguardautotunnel.util
class WgTunnelException(message: String) : Exception(message) import com.wireguard.config.BadConfigException
class WgTunnelException(e: Exception) : Exception() {
constructor(message : String) : this(Exception(message))
override val message: String = generateExceptionMessage(e)
private fun generateExceptionMessage(e : Exception) : String {
return when(e) {
is BadConfigException -> "${e.section.name} ${e.location.name} ${e.reason.name}"
else -> e.message ?: "Unknown error occurred"
}
}
}

View File

@ -14,20 +14,20 @@ hiltNavigationCompose = "1.0.0"
junit = "4.13.2" junit = "4.13.2"
kotlinx-serialization-json = "1.5.1" kotlinx-serialization-json = "1.5.1"
lifecycle-runtime-compose = "2.6.2" lifecycle-runtime-compose = "2.6.2"
material-icons-extended = "1.5.3" material-icons-extended = "1.5.4"
material3 = "1.1.2" material3 = "1.1.2"
navigationCompose = "2.7.4" navigationCompose = "2.7.4"
roomVersion = "2.6.0-rc01" roomVersion = "2.6.0"
timber = "5.0.1" timber = "5.0.1"
tunnel = "1.0.20230706" tunnel = "1.0.20230706"
androidGradlePlugin = "8.3.0-alpha06" androidGradlePlugin = "8.3.0-alpha06"
kotlin="1.9.10" kotlin="1.9.10"
ksp="1.9.10-1.0.13" ksp="1.9.10-1.0.13"
composeBom="2023.10.00" composeBom="2023.10.01"
firebaseBom="32.3.1" firebaseBom="32.4.0"
compose="1.5.3" compose="1.5.4"
crashlytics="18.4.3" crashlytics="18.5.0"
analytics="21.3.0" analytics="21.4.0"
composeCompiler="1.5.3" composeCompiler="1.5.3"
zxingAndroidEmbedded = "4.3.0" zxingAndroidEmbedded = "4.3.0"
zxingCore = "3.4.1" zxingCore = "3.4.1"