Skip to content
Snippets Groups Projects
Verified Commit 50f1d3dd authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Implement cleaner code for backend configuration

parent 2a320461
Branches
Tags
No related merge requests found
Showing
with 388 additions and 47 deletions
......@@ -119,15 +119,14 @@ class EndToEndTest {
HandshakeMessage.ClientInit(
clientVersion = "Quasseldroid test",
buildDate = "Never",
clientFeatures = connectionFeatureSet.legacyFeatures(),
featureList = connectionFeatureSet.featureList()
featureSet = connectionFeatureSet
),
connectionFeatureSet
)
}
println("Reading clientInit response")
read {
HandshakeMessageSerializer.deserialize(it, connectionFeatureSet)
println(HandshakeMessageSerializer.deserialize(it, connectionFeatureSet))
}
println("Writing invalid core init")
write {
......@@ -168,7 +167,7 @@ class EndToEndTest {
}
println("Reading valid clientInit response")
read {
HandshakeMessageSerializer.deserialize(it, connectionFeatureSet)
println(HandshakeMessageSerializer.deserialize(it, connectionFeatureSet))
}
println("Writing invalid clientLogin")
write {
......@@ -206,11 +205,11 @@ class EndToEndTest {
}
println("Reading valid clientLogin response")
read {
HandshakeMessageSerializer.deserialize(it, connectionFeatureSet)
println(HandshakeMessageSerializer.deserialize(it, connectionFeatureSet))
}
println("Reading valid session init")
read {
HandshakeMessageSerializer.deserialize(it, connectionFeatureSet)
println(HandshakeMessageSerializer.deserialize(it, connectionFeatureSet))
}
}
......
......@@ -16,7 +16,7 @@ import de.justjanne.bitflags.of
* Model representing the set of negotiated or supported features for a
* connection or library
*/
class FeatureSet internal constructor(
data class FeatureSet internal constructor(
private val features: Set<QuasselFeature>,
private val additional: Set<QuasselFeatureName> = emptySet()
) {
......
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.protocol.models
data class BackendInfo(
val entries: List<SetupEntry>,
val isDefault: Boolean,
val displayName: String,
val description: String,
val backendId: String,
)
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.protocol.models
import de.justjanne.libquassel.protocol.models.types.QtType
import de.justjanne.libquassel.protocol.util.triples
import de.justjanne.libquassel.protocol.variant.QVariantList
import de.justjanne.libquassel.protocol.variant.QVariantMap
import de.justjanne.libquassel.protocol.variant.QVariant_
import de.justjanne.libquassel.protocol.variant.into
import de.justjanne.libquassel.protocol.variant.qVariant
object BackendInfoSerializer {
fun serialize(data: BackendInfo): QVariantMap = mapOf(
"SetupKeys" to qVariant<QStringList>(
data.entries.map(SetupEntry::key),
QtType.QStringList
),
"SetupDefaults" to qVariant<QVariantMap>(
data.entries.map { it.key to it.defaultValue }.toMap<String, QVariant_>(),
QtType.QVariantMap
),
"SetupData" to qVariant<QVariantList>(
data.entries.flatMap {
listOf<QVariant_>(
qVariant(it.key, QtType.QString),
qVariant(it.displayName, QtType.QString),
it.defaultValue
)
},
QtType.QVariantList
),
"IsDefault" to qVariant(
data.isDefault,
QtType.Bool
),
"DisplayName" to qVariant(
data.displayName,
QtType.QString
),
"Description" to qVariant(
data.description,
QtType.QString
),
"BackendId" to qVariant(
data.backendId,
QtType.QString
),
)
private fun parseSetupEntries(
data: QVariantList?,
defaults: QVariantMap
): List<SetupEntry> = data?.triples { key, displayName, defaultValue ->
SetupEntry(key.into(""), displayName.into(""), defaultValue)
} ?: defaults.map { (key, value) -> SetupEntry(key, key, value) }
fun deserialize(data: QVariantMap) = BackendInfo(
entries = parseSetupEntries(
data["SetupData"].into<QVariantList>(),
data["SetupDefaults"].into<QVariantMap>().orEmpty()
),
isDefault = data["IsDefault"].into<Boolean>(false),
displayName = data["DisplayName"].into<String>(""),
description = data["Description"].into<String>(""),
backendId = data["BackendId"].into<String>("")
)
}
......@@ -10,8 +10,7 @@
package de.justjanne.libquassel.protocol.models
import de.justjanne.libquassel.protocol.features.LegacyFeatures
import de.justjanne.libquassel.protocol.features.QuasselFeatureName
import de.justjanne.libquassel.protocol.features.FeatureSet
import de.justjanne.libquassel.protocol.variant.QVariantList
import de.justjanne.libquassel.protocol.variant.QVariantMap
......@@ -35,14 +34,9 @@ sealed class HandshakeMessage {
*/
val buildDate: String?,
/**
* Set of legacy features supported
* Enabled client features for this connection
*/
val clientFeatures: LegacyFeatures,
/**
* List of supported features. If a feature can also be represented as
* legacy feature, it is included in both.
*/
val featureList: List<QuasselFeatureName>
val featureSet: FeatureSet,
) : HandshakeMessage()
/**
......@@ -60,21 +54,16 @@ sealed class HandshakeMessage {
* If the core needs to be configured, this contains metadata about the
* supported storage backends
*/
val backendInfo: QVariantList,
val backendInfo: List<BackendInfo>,
/**
* If the core needs to be configured, this contains metadata about the
* supported authentication backends
*/
val authenticatorInfo: QVariantList,
/**
* Set of legacy features supported
*/
val coreFeatures: LegacyFeatures,
val authenticatorInfo: List<BackendInfo>,
/**
* List of supported features. If a feature can also be represented as
* legacy feature, it is included in both.
* Enabled features for this connection
*/
val featureList: List<QuasselFeatureName>
val featureSet: FeatureSet,
) : HandshakeMessage()
/**
......
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.protocol.models
import de.justjanne.libquassel.protocol.variant.QVariant_
data class SetupEntry(
val key: String,
val displayName: String,
val defaultValue: QVariant_,
)
......@@ -12,12 +12,15 @@ package de.justjanne.libquassel.protocol.serializers.handshake
import de.justjanne.bitflags.of
import de.justjanne.bitflags.toBits
import de.justjanne.libquassel.protocol.features.FeatureSet
import de.justjanne.libquassel.protocol.features.LegacyFeature
import de.justjanne.libquassel.protocol.features.QuasselFeatureName
import de.justjanne.libquassel.protocol.models.BackendInfoSerializer
import de.justjanne.libquassel.protocol.models.HandshakeMessage
import de.justjanne.libquassel.protocol.models.QStringList
import de.justjanne.libquassel.protocol.models.types.QtType
import de.justjanne.libquassel.protocol.serializers.HandshakeSerializer
import de.justjanne.libquassel.protocol.variant.QVariantList
import de.justjanne.libquassel.protocol.variant.QVariantMap
import de.justjanne.libquassel.protocol.variant.into
import de.justjanne.libquassel.protocol.variant.qVariant
......@@ -30,20 +33,45 @@ object ClientInitAckSerializer : HandshakeSerializer<HandshakeMessage.ClientInit
override fun serialize(data: HandshakeMessage.ClientInitAck) = mapOf(
"MsgType" to qVariant(type, QtType.QString),
"CoreFeatures" to qVariant(data.coreFeatures.toBits(), QtType.UInt),
"StorageBackends" to qVariant(data.backendInfo, QtType.QVariantList),
"Authenticator" to qVariant(data.authenticatorInfo, QtType.QVariantList),
"CoreFeatures" to qVariant(data.featureSet.legacyFeatures().toBits(), QtType.UInt),
"StorageBackends" to qVariant<QVariantList>(
data.backendInfo.map {
qVariant<QVariantMap>(
BackendInfoSerializer.serialize(it),
QtType.QVariantMap
)
},
QtType.QVariantList
),
"Authenticator" to qVariant<QVariantList>(
data.authenticatorInfo.map {
qVariant<QVariantMap>(
BackendInfoSerializer.serialize(it),
QtType.QVariantMap
)
},
QtType.QVariantList
),
"Configured" to qVariant(data.coreConfigured, QtType.Bool),
"FeatureList" to qVariant(data.featureList, QtType.QStringList)
"FeatureList" to qVariant(
data.featureSet.featureList().map(QuasselFeatureName::name),
QtType.QStringList
)
)
override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientInitAck(
coreConfigured = data["Configured"].into(),
backendInfo = data["StorageBackends"].into(emptyList()),
authenticatorInfo = data["Authenticators"].into(emptyList()),
coreFeatures = LegacyFeature.of(data["CoreFeatures"].into<UInt>()),
featureList = data["FeatureList"].into<QStringList>(emptyList())
.filterNotNull()
.map(::QuasselFeatureName),
backendInfo = data["StorageBackends"].into<QVariantList>()?.mapNotNull {
it.into<QVariantMap>()
}?.map(BackendInfoSerializer::deserialize).orEmpty(),
authenticatorInfo = data["Authenticator"].into<QVariantList>()?.mapNotNull {
it.into<QVariantMap>()
}?.map(BackendInfoSerializer::deserialize).orEmpty(),
featureSet = FeatureSet.build(
LegacyFeature.of(data["CoreFeatures"].into<UInt>()),
data["FeatureList"].into<QStringList>(emptyList())
.filterNotNull()
.map(::QuasselFeatureName)
)
)
}
......@@ -12,9 +12,11 @@ package de.justjanne.libquassel.protocol.serializers.handshake
import de.justjanne.bitflags.of
import de.justjanne.bitflags.toBits
import de.justjanne.libquassel.protocol.features.FeatureSet
import de.justjanne.libquassel.protocol.features.LegacyFeature
import de.justjanne.libquassel.protocol.features.QuasselFeatureName
import de.justjanne.libquassel.protocol.models.HandshakeMessage
import de.justjanne.libquassel.protocol.models.QStringList
import de.justjanne.libquassel.protocol.models.types.QtType
import de.justjanne.libquassel.protocol.serializers.HandshakeSerializer
import de.justjanne.libquassel.protocol.variant.QVariantMap
......@@ -31,9 +33,9 @@ object ClientInitSerializer : HandshakeSerializer<HandshakeMessage.ClientInit> {
"MsgType" to qVariant(type, QtType.QString),
"ClientVersion" to qVariant(data.clientVersion, QtType.QString),
"ClientDate" to qVariant(data.buildDate, QtType.QString),
"Features" to qVariant(data.clientFeatures.toBits(), QtType.UInt),
"Features" to qVariant(data.featureSet.legacyFeatures().toBits(), QtType.UInt),
"FeatureList" to qVariant(
data.featureList.map(QuasselFeatureName::name),
data.featureSet.featureList().map(QuasselFeatureName::name),
QtType.QStringList
),
)
......@@ -41,7 +43,11 @@ object ClientInitSerializer : HandshakeSerializer<HandshakeMessage.ClientInit> {
override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientInit(
clientVersion = data["ClientVersion"].into(),
buildDate = data["ClientDate"].into(),
clientFeatures = LegacyFeature.of(data["Features"].into<UInt>()),
featureList = data["FeatureList"].into(emptyList<String>()).map(::QuasselFeatureName),
featureSet = FeatureSet.build(
LegacyFeature.of(data["Features"].into<UInt>()),
data["FeatureList"].into<QStringList>(emptyList())
.filterNotNull()
.map(::QuasselFeatureName)
)
)
}
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.protocol.util
fun <T> Iterable<T>.pairs(): List<Pair<T, T>> {
return pairs { a, b -> Pair(a, b) }
}
inline fun <T, R> Iterable<T>.pairs(transform: (a: T, b: T) -> R): List<R> {
val iterator = iterator()
val result = mutableListOf<R>()
while (true) {
if (!iterator.hasNext()) return result
val first = iterator.next()
if (!iterator.hasNext()) return result
val second = iterator.next()
result.add(transform(first, second))
}
}
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.protocol.util
fun <T> Iterable<T>.triples(): List<Triple<T, T, T>> {
return triples { a, b, c -> Triple(a, b, c) }
}
inline fun <T, R> Iterable<T>.triples(transform: (a: T, b: T, c: T) -> R): List<R> {
val iterator = iterator()
val result = mutableListOf<R>()
while (true) {
if (!iterator.hasNext()) return result
val first = iterator.next()
if (!iterator.hasNext()) return result
val second = iterator.next()
if (!iterator.hasNext()) return result
val third = iterator.next()
result.add(transform(first, second, third))
}
}
......@@ -10,6 +10,8 @@
package de.justjanne.libquassel.protocol.variant
import de.justjanne.libquassel.protocol.util.pairs
/**
* Simple alias for a generic QVariantList type
*/
......@@ -19,6 +21,6 @@ typealias QVariantList = List<QVariant_>
* Transform a QVariantList of interleaved keys and values into a QVariantMap
*/
fun QVariantList.toVariantMap(): QVariantMap =
zipWithNext { key, value ->
pairs { key, value ->
Pair(key.into(""), value)
}.toMap()
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.justjanne.libquassel.protocol.serializers.handshake
import de.justjanne.libquassel.protocol.features.FeatureSet
import de.justjanne.libquassel.protocol.models.BackendInfo
import de.justjanne.libquassel.protocol.models.HandshakeMessage
import de.justjanne.libquassel.protocol.models.SetupEntry
import de.justjanne.libquassel.protocol.models.types.QtType
import de.justjanne.libquassel.protocol.testutil.handshakeSerializerTest
import de.justjanne.libquassel.protocol.variant.qVariant
import org.junit.jupiter.api.Test
class ClientInitAckSerializerTest {
@Test
fun testSimple() = handshakeSerializerTest(
HandshakeMessage.ClientInitAck(
coreConfigured = false,
backendInfo = emptyList(),
authenticatorInfo = emptyList(),
featureSet = FeatureSet.none()
),
// byteBufferOf(0x00u)
)
@Test
fun testRealistic() = handshakeSerializerTest(
HandshakeMessage.ClientInitAck(
coreConfigured = false,
backendInfo = listOf(
BackendInfo(
entries = emptyList(),
isDefault = true,
displayName = "SQLite",
description = "SQLite is a file-based database engine that does not " +
"require any setup. It is suitable for small and medium-sized " +
"databases that do not require access via network. Use SQLite if " +
"your Quassel Core should store its data on the same machine it is " +
"running on, and if you only expect a few users to use your core.",
backendId = "SQLite"
),
BackendInfo(
entries = listOf(
SetupEntry(
"Username",
"Username",
qVariant("quassel", QtType.QString)
),
SetupEntry(
"Password",
"Password",
qVariant<String?>(null, QtType.QString)
),
SetupEntry(
"Hostname",
"Hostname",
qVariant("localhost", QtType.QString)
),
SetupEntry(
"Port",
"Port",
qVariant(5432, QtType.Int)
),
SetupEntry(
"Database",
"Database",
qVariant("quassel", QtType.QString)
)
),
isDefault = false,
displayName = "PostgreSQL",
description = "PostgreSQL Turbo Bomber HD!",
backendId = "PostgreSQL"
)
),
authenticatorInfo = listOf(
BackendInfo(
entries = emptyList(),
isDefault = true,
displayName = "Database",
description = "Do not authenticate against any remote service, but " +
"instead save a hashed and salted password in the database " +
"selected in the next step.",
backendId = "Database"
),
BackendInfo(
entries = listOf(
SetupEntry(
"Hostname",
"Hostname",
qVariant("ldap://localhost", QtType.QString),
),
SetupEntry(
"Port",
"Port",
qVariant(389, QtType.Int),
),
SetupEntry(
"BindDN",
"Bind DN",
qVariant<String?>(null, QtType.QString),
),
SetupEntry(
"BindPassword",
"Bind Password",
qVariant<String?>(null, QtType.QString),
),
SetupEntry(
"BaseDN",
"Base DN",
qVariant<String?>(null, QtType.QString),
),
SetupEntry(
"Filter",
"Filter",
qVariant<String?>(null, QtType.QString),
),
SetupEntry(
"UidAttribute",
"UID Attribute",
qVariant("uid", QtType.QString),
)
),
isDefault = false,
displayName = "LDAP",
description = "Authenticate users using an LDAP server.",
backendId = "LDAP"
)
),
featureSet = FeatureSet.none()
),
// byteBufferOf(0x00u)
)
}
......@@ -18,9 +18,7 @@
*/
package de.justjanne.libquassel.protocol.serializers.handshake
import de.justjanne.bitflags.none
import de.justjanne.libquassel.protocol.features.FeatureSet
import de.justjanne.libquassel.protocol.features.LegacyFeature
import de.justjanne.libquassel.protocol.models.HandshakeMessage
import de.justjanne.libquassel.protocol.testutil.byteBufferOf
import de.justjanne.libquassel.protocol.testutil.handshakeSerializerTest
......@@ -32,8 +30,7 @@ class ClientInitSerializerTest {
HandshakeMessage.ClientInit(
clientVersion = "Quasseldroid test",
buildDate = "Never",
clientFeatures = LegacyFeature.none(),
featureList = emptyList()
featureSet = FeatureSet.none()
),
byteBufferOf(
0x00u, 0x00u, 0x00u, 0x0Au, 0x00u, 0x00u, 0x00u, 0x0Cu,
......@@ -71,8 +68,8 @@ class ClientInitSerializerTest {
clientVersion = "Quasseldroid <a href=\"https://git.kuschku.de/justJanne/QuasselDroid-ng/commit/" +
"b622ad63056b6054b06e09f8e1f1ef2b0c3aaf9a\">v1.3.3</a>",
buildDate = "2020-04-27T22:21:17Z",
clientFeatures = FeatureSet.all().legacyFeatures(),
featureList = FeatureSet.all().featureList()
)
featureSet = FeatureSet.all()
),
// byteBufferOf(0x00u)
)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment