From 10d6cd5a8f1cbc90aeb428cebb24c0fea795fd1b Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski <janne@kuschku.de> Date: Sun, 6 Jun 2021 19:53:04 +0200 Subject: [PATCH] Implement sync fully --- .../client/session/BaseInitHandler.kt | 56 +++++++++++++++ .../client/session/BaseInitHandlerState.kt | 18 +++++ .../client/session/ClientConnectionHandler.kt | 4 ++ .../client/session/ClientHandshakeHandler.kt | 21 +++++- .../client/session/ClientMagicHandler.kt | 2 + .../session/ClientProxyMessageHandler.kt | 48 ++++++------- .../client/session/ClientRpcHandler.kt | 10 ++- .../client/session/ClientSession.kt | 68 ++++++++++++++++--- .../client/session/ClientSessionState.kt | 2 + .../justjanne/libquassel/client/ClientTest.kt | 19 +++--- .../test/resources/simplelogger.properties | 2 +- .../protocol/models/ConnectedClient.kt | 3 +- .../serializers/qt/QDateTimeSerializer.kt | 6 +- .../serializers/qt/QTimeSerializer.kt | 6 +- .../signalproxy/HeartBeatSerializer.kt | 3 +- .../signalproxy/InitDataSerializer.kt | 2 +- .../protocol/session/CommonSyncProxy.kt | 7 +- .../protocol/session/ConnectionHandler.kt | 1 + .../protocol/session/MessageChannel.kt | 20 ++++-- .../libquassel/protocol/session/Session.kt | 13 +++- .../syncables/common/BacklogManager.kt | 6 +- .../protocol/syncables/common/BufferSyncer.kt | 7 ++ .../syncables/common/BufferViewConfig.kt | 4 ++ .../protocol/syncables/common/CertManager.kt | 5 ++ .../protocol/syncables/common/CoreInfo.kt | 3 +- .../syncables/common/IrcListHelper.kt | 6 +- .../protocol/syncables/common/IrcUser.kt | 8 ++- .../syncables/state/BufferViewConfigState.kt | 4 +- .../syncables/state/CertManagerState.kt | 4 +- .../libquassel/protocol/variant/QVariant.kt | 2 + .../protocol/variant/QVariantList.kt | 10 ++- 31 files changed, 296 insertions(+), 74 deletions(-) create mode 100644 libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/BaseInitHandler.kt create mode 100644 libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/BaseInitHandlerState.kt diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/BaseInitHandler.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/BaseInitHandler.kt new file mode 100644 index 0000000..1b14319 --- /dev/null +++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/BaseInitHandler.kt @@ -0,0 +1,56 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * + * 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.client.session + +import de.justjanne.libquassel.client.util.CoroutineQueue +import de.justjanne.libquassel.protocol.syncables.ObjectIdentifier +import de.justjanne.libquassel.protocol.syncables.SyncableStub +import de.justjanne.libquassel.protocol.util.update +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow + +class BaseInitHandler( + private val session: ClientSession +) { + private val coroutineQueue = CoroutineQueue<Unit>() + + fun sync(stub: SyncableStub) { + if (!stub.initialized) { + state.update { + copy(started = true, total = total + 1, waiting = waiting + ObjectIdentifier(stub)) + } + } + session.proxy.synchronize(stub) + } + + suspend fun initialized(identifier: ObjectIdentifier) { + state.update { + copy(waiting = waiting - identifier) + } + if (initDone()) { + coroutineQueue.resume(Unit) + } + } + + fun initDone() = state().started && state().waiting.isEmpty() + + suspend fun waitForInitDone() = if (!initDone()) { + coroutineQueue.wait() + } else Unit + + @Suppress("NOTHING_TO_INLINE") + inline fun state(): BaseInitHandlerState = state.value + + @Suppress("NOTHING_TO_INLINE") + inline fun flow(): Flow<BaseInitHandlerState> = state + + @PublishedApi + internal val state = MutableStateFlow(BaseInitHandlerState()) +} diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/BaseInitHandlerState.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/BaseInitHandlerState.kt new file mode 100644 index 0000000..63142f8 --- /dev/null +++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/BaseInitHandlerState.kt @@ -0,0 +1,18 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * + * 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.client.session + +import de.justjanne.libquassel.protocol.syncables.ObjectIdentifier + +data class BaseInitHandlerState( + val started: Boolean = false, + val total: Int = 0, + val waiting: Set<ObjectIdentifier> = setOf() +) diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientConnectionHandler.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientConnectionHandler.kt index 63c798b..506e1a2 100644 --- a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientConnectionHandler.kt +++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientConnectionHandler.kt @@ -24,6 +24,10 @@ abstract class ClientConnectionHandler : ConnectionHandler { return false } + override suspend fun done() { + this.channel = null + } + suspend fun emit(message: SignalProxyMessage) { if (channel == null) { readyQueue.wait() diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientHandshakeHandler.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientHandshakeHandler.kt index b0077fe..c7c409a 100644 --- a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientHandshakeHandler.kt +++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientHandshakeHandler.kt @@ -20,13 +20,21 @@ import de.justjanne.libquassel.protocol.session.MessageChannelReadThread import de.justjanne.libquassel.protocol.session.Session import de.justjanne.libquassel.protocol.util.log.trace import de.justjanne.libquassel.protocol.variant.QVariantMap +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.runInterruptible +import kotlinx.coroutines.withContext import org.slf4j.LoggerFactory +import sun.security.util.DisabledAlgorithmConstraints import java.nio.ByteBuffer class ClientHandshakeHandler( val session: Session ) : HandshakeHandler, ClientConnectionHandler() { private val messageQueue = CoroutineKeyedQueue<Class<out HandshakeMessage>, HandshakeMessage>() + private var sessionInit: HandshakeMessage.SessionInit? = null override suspend fun read(buffer: ByteBuffer): Boolean { return dispatch(HandshakeMessageSerializer.deserialize(buffer, channel!!.negotiatedFeatures)) @@ -36,10 +44,17 @@ class ClientHandshakeHandler( logger.trace { "Read handshake message $message" } messageQueue.resume(message.javaClass, message) if (message is HandshakeMessage.SessionInit) { - session.init(message.identities, message.bufferInfos, message.networkIds) + sessionInit = message return true - } else { - return false + } + return false + } + + override suspend fun done() { + super.done() + val message = sessionInit + if (message != null) { + session.init(message.identities, message.bufferInfos, message.networkIds) } } diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientMagicHandler.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientMagicHandler.kt index 31f78d9..f067518 100644 --- a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientMagicHandler.kt +++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientMagicHandler.kt @@ -60,6 +60,8 @@ class ClientMagicHandler( override suspend fun read(buffer: ByteBuffer) = true + override suspend fun done() = Unit + companion object { private val logger = LoggerFactory.getLogger(ClientMagicHandler::class.java) } diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientProxyMessageHandler.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientProxyMessageHandler.kt index 424f823..e1669ea 100644 --- a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientProxyMessageHandler.kt +++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientProxyMessageHandler.kt @@ -15,18 +15,19 @@ import de.justjanne.libquassel.protocol.models.SignalProxyMessage import de.justjanne.libquassel.protocol.serializers.SignalProxyMessageSerializer import de.justjanne.libquassel.protocol.session.ProxyMessageHandler import de.justjanne.libquassel.protocol.syncables.HeartBeatHandler +import de.justjanne.libquassel.protocol.syncables.ObjectIdentifier import de.justjanne.libquassel.protocol.syncables.ObjectRepository import de.justjanne.libquassel.protocol.syncables.common.RpcHandler import de.justjanne.libquassel.protocol.syncables.invoker.Invokers import de.justjanne.libquassel.protocol.util.log.trace import org.slf4j.LoggerFactory -import java.lang.Exception import java.nio.ByteBuffer class ClientProxyMessageHandler( private val heartBeatHandler: HeartBeatHandler, private val objectRepository: ObjectRepository, - private val rpcHandler: RpcHandler + private val rpcHandler: RpcHandler, + private val baseInitHandler: BaseInitHandler ) : ProxyMessageHandler, ClientConnectionHandler() { override suspend fun read(buffer: ByteBuffer): Boolean { @@ -36,32 +37,31 @@ class ClientProxyMessageHandler( override suspend fun dispatch(message: SignalProxyMessage) { logger.trace { "Read signal proxy message $message" } - try { - when (message) { - is SignalProxyMessage.HeartBeat -> emit(SignalProxyMessage.HeartBeatReply(message.timestamp)) - is SignalProxyMessage.HeartBeatReply -> heartBeatHandler.recomputeLatency(message.timestamp, force = true) - is SignalProxyMessage.InitData -> objectRepository.init( + when (message) { + is SignalProxyMessage.HeartBeat -> emit(SignalProxyMessage.HeartBeatReply(message.timestamp)) + is SignalProxyMessage.HeartBeatReply -> heartBeatHandler.recomputeLatency(message.timestamp, force = true) + is SignalProxyMessage.InitData -> { + objectRepository.init( objectRepository.find(message.className, message.objectName) ?: return, message.initData ) - is SignalProxyMessage.InitRequest -> { - // Ignore incoming requests, we’re a client, we shouldn’t ever receive these - } - is SignalProxyMessage.Rpc -> { - val invoker = Invokers.get(ProtocolSide.CLIENT, "RpcHandler") - ?: throw RpcInvocationFailedException.InvokerNotFoundException("RpcHandler") - invoker.invoke(rpcHandler, message.slotName, message.params) - } - is SignalProxyMessage.Sync -> { - val invoker = Invokers.get(ProtocolSide.CLIENT, message.className) - ?: throw RpcInvocationFailedException.InvokerNotFoundException(message.className) - val syncable = objectRepository.find(message.className, message.objectName) - ?: throw RpcInvocationFailedException.SyncableNotFoundException(message.className, message.objectName) - invoker.invoke(syncable, message.slotName, message.params) - } + baseInitHandler.initialized(ObjectIdentifier(message.className, message.objectName)) + } + is SignalProxyMessage.InitRequest -> { + // Ignore incoming requests, we’re a client, we shouldn’t ever receive these + } + is SignalProxyMessage.Rpc -> { + val invoker = Invokers.get(ProtocolSide.CLIENT, "RpcHandler") + ?: throw RpcInvocationFailedException.InvokerNotFoundException("RpcHandler") + invoker.invoke(rpcHandler, message.slotName, message.params) + } + is SignalProxyMessage.Sync -> { + val invoker = Invokers.get(ProtocolSide.CLIENT, message.className) + ?: throw RpcInvocationFailedException.InvokerNotFoundException(message.className) + val syncable = objectRepository.find(message.className, message.objectName) + ?: throw RpcInvocationFailedException.SyncableNotFoundException(message.className, message.objectName) + invoker.invoke(syncable, message.slotName, message.params) } - } catch (e: Exception) { - println(e) } } diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientRpcHandler.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientRpcHandler.kt index c2e5257..62a5493 100644 --- a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientRpcHandler.kt +++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientRpcHandler.kt @@ -14,10 +14,12 @@ import de.justjanne.libquassel.protocol.models.StatusMessage import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8 import de.justjanne.libquassel.protocol.session.Session import de.justjanne.libquassel.protocol.syncables.common.RpcHandler +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.runBlocking import java.nio.ByteBuffer class ClientRpcHandler(session: Session) : RpcHandler(session) { @@ -30,11 +32,15 @@ class ClientRpcHandler(session: Session) : RpcHandler(session) { } override fun displayMsg(message: Message) { - messages.tryEmit(message) + runBlocking(Dispatchers.Default) { + messages.emit(message) + } } override fun displayStatusMsg(net: String?, msg: String?) { - statusMessage.tryEmit(StatusMessage(net, msg ?: return)) + runBlocking(Dispatchers.Default) { + statusMessage.emit(StatusMessage(net, msg ?: "")) + } } @Suppress("NOTHING_TO_INLINE") diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientSession.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientSession.kt index 03e6584..f9d4448 100644 --- a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientSession.kt +++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientSession.kt @@ -12,6 +12,7 @@ import de.justjanne.libquassel.annotations.ProtocolSide import de.justjanne.libquassel.client.syncables.ClientBacklogManager import de.justjanne.libquassel.protocol.connection.ProtocolFeatures import de.justjanne.libquassel.protocol.connection.ProtocolMeta +import de.justjanne.libquassel.protocol.features.QuasselFeature import de.justjanne.libquassel.protocol.io.CoroutineChannel import de.justjanne.libquassel.protocol.models.BufferInfo import de.justjanne.libquassel.protocol.models.ids.IdentityId @@ -26,6 +27,7 @@ import de.justjanne.libquassel.protocol.syncables.ObjectRepository import de.justjanne.libquassel.protocol.syncables.common.AliasManager import de.justjanne.libquassel.protocol.syncables.common.BufferSyncer import de.justjanne.libquassel.protocol.syncables.common.BufferViewManager +import de.justjanne.libquassel.protocol.syncables.common.CertManager import de.justjanne.libquassel.protocol.syncables.common.CoreInfo import de.justjanne.libquassel.protocol.syncables.common.DccConfig import de.justjanne.libquassel.protocol.syncables.common.HighlightRuleManager @@ -34,6 +36,7 @@ import de.justjanne.libquassel.protocol.syncables.common.IgnoreListManager import de.justjanne.libquassel.protocol.syncables.common.IrcListHelper import de.justjanne.libquassel.protocol.syncables.common.Network import de.justjanne.libquassel.protocol.syncables.common.NetworkConfig +import de.justjanne.libquassel.protocol.syncables.state.CertManagerState import de.justjanne.libquassel.protocol.syncables.state.NetworkState import de.justjanne.libquassel.protocol.util.log.info import de.justjanne.libquassel.protocol.util.update @@ -51,21 +54,23 @@ class ClientSession( ) : Session { override val side = ProtocolSide.CLIENT - private val rpcHandler = ClientRpcHandler(this) - private val heartBeatHandler = HeartBeatHandler() + override val rpcHandler = ClientRpcHandler(this) + override val heartBeatHandler = HeartBeatHandler() override val objectRepository = ObjectRepository() val handshakeHandler = ClientHandshakeHandler(this) + val baseInitHandler = BaseInitHandler(this) private val proxyMessageHandler = ClientProxyMessageHandler( heartBeatHandler, objectRepository, - rpcHandler + rpcHandler, + baseInitHandler ) - private val magicHandler = ClientMagicHandler(protocolFeatures, protocols, sslContext) override val proxy = CommonSyncProxy( ProtocolSide.CLIENT, objectRepository, proxyMessageHandler ) + private val magicHandler = ClientMagicHandler(protocolFeatures, protocols, sslContext) private val messageChannel = MessageChannel(connection) init { @@ -76,15 +81,53 @@ class ClientSession( } override fun init( - identities: List<QVariantMap>, + identityInfo: List<QVariantMap>, bufferInfos: List<BufferInfo>, networkIds: List<NetworkId> ) { logger.info { - "Client session initialized: networks = $networkIds, buffers = $bufferInfos, identities = $identities" + "Client session initialized: networks = $networkIds, buffers = $bufferInfos, identities = $identityInfo" + } + + bufferSyncer.initializeBufferInfos(bufferInfos) + + val networks = networkIds.map { + Network(this@ClientSession, NetworkState(networkId = it)) + } + val identities = identityInfo.map { + Identity(this@ClientSession).apply { + fromVariantMap(it) + } + } + + state.update { + copy( + networks = networks.associateBy(Network::networkId), + identities = identities.associateBy(Identity::id), + ) } - objectRepository.add(state().coreInfo) - objectRepository.add(state().backlogManager) + + for (network in networks) { + baseInitHandler.sync(network) + } + for (identity in identities) { + baseInitHandler.sync(identity) + baseInitHandler.sync(CertManager(this, CertManagerState(identityId = identity.id()))) + } + baseInitHandler.sync(state().aliasManager) + baseInitHandler.sync(state().bufferSyncer) + baseInitHandler.sync(state().bufferViewManager) + baseInitHandler.sync(state().coreInfo) + if (messageChannel.negotiatedFeatures.hasFeature(QuasselFeature.DccFileTransfer)) { + baseInitHandler.sync(state().dccConfig) + } + baseInitHandler.sync(state().ignoreListManager) + if (messageChannel.negotiatedFeatures.hasFeature(QuasselFeature.CoreSideHighlights)) { + baseInitHandler.sync(state().highlightRuleManager) + } + baseInitHandler.sync(state().ircListHelper) + baseInitHandler.sync(state().networkConfig) + baseInitHandler.sync(state().backlogManager) } override fun network(id: NetworkId) = state().networks[id] @@ -104,6 +147,8 @@ class ClientSession( } } + override fun networks() = state().networks.values.toSet() + override fun identity(id: IdentityId) = state().identities[id] override fun addIdentity(properties: QVariantMap) { @@ -123,6 +168,12 @@ class ClientSession( } } + override fun identities() = state().identities.values.toSet() + + override fun certManager(id: IdentityId) = state().certManagers[id] + + override fun certManagers() = state().certManagers.values.toSet() + override fun rename(className: String, oldName: String, newName: String) { rpcHandler.objectRenamed( StringSerializerUtf8.serializeRaw(className), @@ -162,6 +213,7 @@ class ClientSession( ClientSessionState( networks = mapOf(), identities = mapOf(), + certManagers = mapOf(), aliasManager = AliasManager(this), backlogManager = ClientBacklogManager(this), bufferSyncer = BufferSyncer(this), diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientSessionState.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientSessionState.kt index d03123d..3103847 100644 --- a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientSessionState.kt +++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientSessionState.kt @@ -15,6 +15,7 @@ import de.justjanne.libquassel.protocol.models.ids.NetworkId import de.justjanne.libquassel.protocol.syncables.common.AliasManager import de.justjanne.libquassel.protocol.syncables.common.BufferSyncer import de.justjanne.libquassel.protocol.syncables.common.BufferViewManager +import de.justjanne.libquassel.protocol.syncables.common.CertManager import de.justjanne.libquassel.protocol.syncables.common.CoreInfo import de.justjanne.libquassel.protocol.syncables.common.DccConfig import de.justjanne.libquassel.protocol.syncables.common.HighlightRuleManager @@ -27,6 +28,7 @@ import de.justjanne.libquassel.protocol.syncables.common.NetworkConfig data class ClientSessionState( val networks: Map<NetworkId, Network>, val identities: Map<IdentityId, Identity>, + val certManagers: Map<IdentityId, CertManager>, val aliasManager: AliasManager, val backlogManager: ClientBacklogManager, val bufferSyncer: BufferSyncer, diff --git a/libquassel-client/src/test/kotlin/de/justjanne/libquassel/client/ClientTest.kt b/libquassel-client/src/test/kotlin/de/justjanne/libquassel/client/ClientTest.kt index b9b1ea7..21b4eae 100644 --- a/libquassel-client/src/test/kotlin/de/justjanne/libquassel/client/ClientTest.kt +++ b/libquassel-client/src/test/kotlin/de/justjanne/libquassel/client/ClientTest.kt @@ -23,11 +23,11 @@ import de.justjanne.testcontainersci.api.providedContainer import de.justjanne.testcontainersci.extension.CiContainers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import java.net.InetSocketAddress import javax.net.ssl.SSLContext -import kotlin.test.assertTrue @ExperimentalCoroutinesApi @CiContainers @@ -36,17 +36,13 @@ class ClientTest { QuasselCoreContainer() } - private val sslContext = SSLContext.getInstance("TLSv1.3").apply { - init(null, arrayOf(TestX509TrustManager), null) - } - - private val channel = CoroutineChannel() - - private val username = "AzureDiamond" - private val password = "hunter2" + private val username = "kuschku" + private val password = "goalielecturetrawl" @Test fun testConnect(): Unit = runBlocking { + val channel = CoroutineChannel() + channel.connect( InetSocketAddress( quassel.address, @@ -62,7 +58,9 @@ class ClientTest { 0x0000u ) ), - sslContext + SSLContext.getInstance("TLSv1.3").apply { + init(null, arrayOf(TestX509TrustManager), null) + } ) val coreState: CoreState = session.handshakeHandler.init( "Quasseltest v0.1", @@ -92,6 +90,7 @@ class ClientTest { session.handshakeHandler.login("acidburn", "ineverweardresses") } session.handshakeHandler.login(username, password) + session.baseInitHandler.waitForInitDone() channel.close() } } diff --git a/libquassel-client/src/test/resources/simplelogger.properties b/libquassel-client/src/test/resources/simplelogger.properties index 594f59e..1bf2717 100644 --- a/libquassel-client/src/test/resources/simplelogger.properties +++ b/libquassel-client/src/test/resources/simplelogger.properties @@ -7,4 +7,4 @@ # obtain one at https://mozilla.org/MPL/2.0/. # -org.slf4j.simpleLogger.defaultLogLevel=debug +org.slf4j.simpleLogger.defaultLogLevel=trace diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ConnectedClient.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ConnectedClient.kt index 4e12743..75edaf3 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ConnectedClient.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ConnectedClient.kt @@ -19,6 +19,7 @@ import de.justjanne.libquassel.protocol.variant.QVariantMap import de.justjanne.libquassel.protocol.variant.into import de.justjanne.libquassel.protocol.variant.qVariant import org.threeten.bp.Instant +import org.threeten.bp.ZoneOffset data class ConnectedClient( val id: Int, @@ -51,7 +52,7 @@ data class ConnectedClient( versionDate = properties["clientVersionDate"].into("") .toLongOrNull() ?.let(Instant::ofEpochSecond), - connectedSince = properties["connectedSince"].into(Instant.EPOCH), + connectedSince = properties["connectedSince"].into(Instant.EPOCH.atOffset(ZoneOffset.UTC)).toInstant(), secure = properties["secure"].into(false), features = FeatureSet.build( LegacyFeature.of(properties["features"].into()), diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QDateTimeSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QDateTimeSerializer.kt index 80b261e..6ca4bb6 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QDateTimeSerializer.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QDateTimeSerializer.kt @@ -16,6 +16,7 @@ import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer import org.threeten.bp.Instant import org.threeten.bp.LocalDateTime import org.threeten.bp.OffsetDateTime +import org.threeten.bp.ZoneId import org.threeten.bp.ZoneOffset import org.threeten.bp.ZonedDateTime import org.threeten.bp.temporal.Temporal @@ -51,7 +52,7 @@ object QDateTimeSerializer : PrimitiveSerializer<Temporal> { } } - override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): Temporal { + override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): OffsetDateTime { val julianDay = QDateSerializer.deserialize(buffer, featureSet) val localTime = QTimeSerializer.deserialize(buffer, featureSet) val localDateTime = LocalDateTime.of(julianDay, localTime) @@ -61,7 +62,7 @@ object QDateTimeSerializer : PrimitiveSerializer<Temporal> { TimeSpec.LocalStandard, TimeSpec.LocalUnknown, TimeSpec.LocalDST -> - localDateTime + localDateTime.atZone(ZoneId.systemDefault()).toOffsetDateTime() TimeSpec.OffsetFromUTC -> localDateTime .atOffset( @@ -72,7 +73,6 @@ object QDateTimeSerializer : PrimitiveSerializer<Temporal> { TimeSpec.UTC -> localDateTime .atOffset(ZoneOffset.UTC) - .toInstant() } } } diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QTimeSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QTimeSerializer.kt index 4f921ef..a09bd4d 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QTimeSerializer.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QTimeSerializer.kt @@ -28,6 +28,10 @@ object QTimeSerializer : PrimitiveSerializer<LocalTime> { override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): LocalTime { val millisecondOfDay = IntSerializer.deserialize(buffer, featureSet).toLong() - return LocalTime.ofNanoOfDay(millisecondOfDay * 1_000_000) + try { + return LocalTime.ofNanoOfDay(millisecondOfDay * 1_000_000) + } catch (e: Exception) { + return LocalTime.MAX + } } } diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/signalproxy/HeartBeatSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/signalproxy/HeartBeatSerializer.kt index a377cf6..7c2837c 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/signalproxy/HeartBeatSerializer.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/signalproxy/HeartBeatSerializer.kt @@ -16,6 +16,7 @@ import de.justjanne.libquassel.protocol.variant.QVariantList import de.justjanne.libquassel.protocol.variant.into import de.justjanne.libquassel.protocol.variant.qVariant import org.threeten.bp.Instant +import org.threeten.bp.ZoneOffset /** * Serializer for [SignalProxyMessage.HeartBeat] @@ -29,6 +30,6 @@ object HeartBeatSerializer : SignalProxySerializer<SignalProxyMessage.HeartBeat> ) override fun deserialize(data: QVariantList) = SignalProxyMessage.HeartBeat( - data.getOrNull(1).into(Instant.EPOCH) + data.getOrNull(1).into(Instant.EPOCH.atOffset(ZoneOffset.UTC)).toInstant() ) } diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/signalproxy/InitDataSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/signalproxy/InitDataSerializer.kt index 9ab6016..3681fa9 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/signalproxy/InitDataSerializer.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/signalproxy/InitDataSerializer.kt @@ -35,6 +35,6 @@ object InitDataSerializer : SignalProxySerializer<SignalProxyMessage.InitData> { override fun deserialize(data: QVariantList) = SignalProxyMessage.InitData( StringSerializerUtf8.deserializeRaw(data.getOrNull(1).into<ByteBuffer>()), StringSerializerUtf8.deserializeRaw(data.getOrNull(2).into<ByteBuffer>()), - data.drop(3).toVariantMap() + data.drop(3).toVariantMap(byteBuffer = true) ) } diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/CommonSyncProxy.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/CommonSyncProxy.kt index de1d9a6..ffd0cc8 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/CommonSyncProxy.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/CommonSyncProxy.kt @@ -14,6 +14,7 @@ import de.justjanne.libquassel.protocol.models.SignalProxyMessage import de.justjanne.libquassel.protocol.syncables.ObjectRepository import de.justjanne.libquassel.protocol.syncables.SyncableStub import de.justjanne.libquassel.protocol.variant.QVariantList +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking class CommonSyncProxy( @@ -22,9 +23,9 @@ class CommonSyncProxy( private val proxyMessageHandler: ProxyMessageHandler ) : SyncProxy { override fun synchronize(syncable: SyncableStub) { - if (objectRepository.add(syncable)) { - runBlocking { - proxyMessageHandler.dispatch(SignalProxyMessage.InitRequest(syncable.className, syncable.objectName)) + if (objectRepository.add(syncable) && !syncable.initialized) { + runBlocking(context = Dispatchers.IO) { + proxyMessageHandler.emit(SignalProxyMessage.InitRequest(syncable.className, syncable.objectName)) } } } diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/ConnectionHandler.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/ConnectionHandler.kt index 23376b0..80a8b6b 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/ConnectionHandler.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/ConnectionHandler.kt @@ -13,5 +13,6 @@ import java.nio.ByteBuffer interface ConnectionHandler { suspend fun init(channel: MessageChannel): Boolean + suspend fun done() suspend fun read(buffer: ByteBuffer): Boolean } diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/MessageChannel.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/MessageChannel.kt index 9bb1fed..8394110 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/MessageChannel.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/MessageChannel.kt @@ -55,7 +55,8 @@ class MessageChannel( dispatch(messageBuffer) } - private suspend fun setupHandlers() { + private suspend fun setupHandlers(): List<ConnectionHandler> { + val removed = mutableListOf<ConnectionHandler>() while (true) { val handler = handlers.firstOrNull() logger.trace { "Setting up handler $handler" } @@ -63,18 +64,27 @@ class MessageChannel( break } logger.trace { "Handler $handler is done" } - handlers.removeFirst() + removed.add(handlers.removeFirst()) } if (handlers.isEmpty()) { logger.trace { "All handlers done" } channel.close() } + return removed } private suspend fun dispatch(message: ByteBuffer) { - if (handlers.first().read(message)) { - handlers.removeFirst() - setupHandlers() + val handlerDone = try { + handlers.first().read(message) + } catch (e: Exception) { + logger.warn("Error while handling message: ", e) + false + } + if (handlerDone) { + val removed = listOf(handlers.removeFirst()) + setupHandlers() + for (handler in removed) { + handler.done() + } } } diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/Session.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/Session.kt index 056a86b..63f32d1 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/Session.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/Session.kt @@ -13,11 +13,13 @@ import de.justjanne.libquassel.annotations.ProtocolSide import de.justjanne.libquassel.protocol.models.BufferInfo import de.justjanne.libquassel.protocol.models.ids.IdentityId import de.justjanne.libquassel.protocol.models.ids.NetworkId +import de.justjanne.libquassel.protocol.syncables.HeartBeatHandler import de.justjanne.libquassel.protocol.syncables.ObjectRepository import de.justjanne.libquassel.protocol.syncables.common.AliasManager import de.justjanne.libquassel.protocol.syncables.common.BacklogManager import de.justjanne.libquassel.protocol.syncables.common.BufferSyncer import de.justjanne.libquassel.protocol.syncables.common.BufferViewManager +import de.justjanne.libquassel.protocol.syncables.common.CertManager import de.justjanne.libquassel.protocol.syncables.common.CoreInfo import de.justjanne.libquassel.protocol.syncables.common.DccConfig import de.justjanne.libquassel.protocol.syncables.common.HighlightRuleManager @@ -26,6 +28,7 @@ import de.justjanne.libquassel.protocol.syncables.common.IgnoreListManager import de.justjanne.libquassel.protocol.syncables.common.IrcListHelper import de.justjanne.libquassel.protocol.syncables.common.Network import de.justjanne.libquassel.protocol.syncables.common.NetworkConfig +import de.justjanne.libquassel.protocol.syncables.common.RpcHandler import de.justjanne.libquassel.protocol.variant.QVariantMap interface Session { @@ -34,7 +37,7 @@ interface Session { val objectRepository: ObjectRepository fun init( - identities: List<QVariantMap>, + identityInfo: List<QVariantMap>, bufferInfos: List<BufferInfo>, networkIds: List<NetworkId> ) @@ -42,13 +45,21 @@ interface Session { fun network(id: NetworkId): Network? fun addNetwork(id: NetworkId) fun removeNetwork(id: NetworkId) + fun networks(): Set<Network> fun identity(id: IdentityId): Identity? fun addIdentity(properties: QVariantMap) fun removeIdentity(id: IdentityId) + fun identities(): Set<Identity> + + fun certManager(id: IdentityId): CertManager? + fun certManagers(): Set<CertManager> fun rename(className: String, oldName: String, newName: String) + val heartBeatHandler: HeartBeatHandler + val rpcHandler: RpcHandler + val aliasManager: AliasManager val backlogManager: BacklogManager val bufferSyncer: BufferSyncer diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BacklogManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BacklogManager.kt index 706d6bf..e459daa 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BacklogManager.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BacklogManager.kt @@ -15,4 +15,8 @@ import de.justjanne.libquassel.protocol.syncables.stubs.BacklogManagerStub open class BacklogManager( session: Session? = null -) : SyncableObject(session, "BacklogManager"), BacklogManagerStub +) : SyncableObject(session, "BacklogManager"), BacklogManagerStub { + init { + initialized = true + } +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferSyncer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferSyncer.kt index 52b62f4..1db9f81 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferSyncer.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferSyncer.kt @@ -28,6 +28,7 @@ import de.justjanne.libquassel.protocol.util.collections.pairs import de.justjanne.libquassel.protocol.util.update 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 @@ -107,6 +108,12 @@ open class BufferSyncer( initialized = true } + fun initializeBufferInfos(infos: List<BufferInfo>) { + state.update { + copy(bufferInfos = infos.associateBy(BufferInfo::bufferId)) + } + } + fun lastSeenMsg(buffer: BufferId): MsgId = state().lastSeenMsg[buffer] ?: MsgId(0) fun markerLine(buffer: BufferId): MsgId = state().markerLines[buffer] ?: MsgId(0) diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewConfig.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewConfig.kt index a0498d9..769844b 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewConfig.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewConfig.kt @@ -35,6 +35,10 @@ open class BufferViewConfig( state: BufferViewConfigState ) : StatefulSyncableObject<BufferViewConfigState>(session, "BufferViewConfig", state), BufferViewConfigStub { + init { + renameObject(state().identifier()) + } + override fun fromVariantMap(properties: QVariantMap) { state.update { copy( diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CertManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CertManager.kt index c52bdc5..1c36265 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CertManager.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CertManager.kt @@ -33,6 +33,10 @@ open class CertManager( state: CertManagerState ) : StatefulSyncableObject<CertManagerState>(session, "CertManager", state), CertManagerStub { + init { + renameObject(state().identifier()) + } + override fun fromVariantMap(properties: QVariantMap) { val privateKeyPem = properties["sslKey"].into("") val certPem = properties["sslCert"].into("") @@ -45,6 +49,7 @@ open class CertManager( certificate = readCertificate(certPem) ) } + renameObject(state().identifier()) initialized = true } diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CoreInfo.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CoreInfo.kt index 68226de..5dde098 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CoreInfo.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CoreInfo.kt @@ -22,6 +22,7 @@ import de.justjanne.libquassel.protocol.variant.QVariant_ import de.justjanne.libquassel.protocol.variant.into import de.justjanne.libquassel.protocol.variant.qVariant import org.threeten.bp.Instant +import org.threeten.bp.ZoneOffset open class CoreInfo( session: Session? = null, @@ -37,7 +38,7 @@ open class CoreInfo( versionDate = coreData["quasselBuildDate"].into("") .toLongOrNull() ?.let(Instant::ofEpochSecond), - startTime = coreData["startTime"].into(startTime), + startTime = coreData["startTime"].into(startTime.atOffset(ZoneOffset.UTC)).toInstant(), connectedClientCount = coreData["sessionConnectedClients"].into(connectedClientCount), connectedClients = coreData["sessionConnectedClientData"].into<QVariantList>() ?.mapNotNull<QVariant_, QVariantMap>(QVariant_::into) diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcListHelper.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcListHelper.kt index 72fc263..e2b543f 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcListHelper.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcListHelper.kt @@ -15,4 +15,8 @@ import de.justjanne.libquassel.protocol.syncables.stubs.IrcListHelperStub open class IrcListHelper( session: Session? = null -) : SyncableObject(session, "IrcListHelper"), IrcListHelperStub +) : SyncableObject(session, "IrcListHelper"), IrcListHelperStub { + init { + initialized = true + } +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcUser.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcUser.kt index 7cf374b..7870c59 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcUser.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcUser.kt @@ -21,6 +21,8 @@ import de.justjanne.libquassel.protocol.variant.indexed import de.justjanne.libquassel.protocol.variant.into import de.justjanne.libquassel.protocol.variant.qVariant import org.threeten.bp.Instant +import org.threeten.bp.OffsetDateTime +import org.threeten.bp.ZoneOffset import org.threeten.bp.temporal.Temporal open class IrcUser( @@ -45,11 +47,11 @@ open class IrcUser( account = properties["account"].indexed(index).into(account), away = properties["away"].indexed(index).into(away), awayMessage = properties["awayMessage"].indexed(index).into(awayMessage), - idleTime = properties["idleTime"].indexed(index).into(idleTime), - loginTime = properties["loginTime"].indexed(index).into(loginTime), + idleTime = properties["idleTime"].indexed(index).into(idleTime.atOffset(ZoneOffset.UTC)).toInstant(), + loginTime = properties["loginTime"].indexed(index).into(loginTime.atOffset(ZoneOffset.UTC)).toInstant(), server = properties["server"].indexed(index).into(server), ircOperator = properties["ircOperator"].indexed(index).into(ircOperator), - lastAwayMessageTime = properties["lastAwayMessageTime"].indexed(index).into() + lastAwayMessageTime = properties["lastAwayMessageTime"].indexed(index).into<OffsetDateTime>()?.toInstant() ?: properties["lastAwayMessage"].indexed(index).into<Int>()?.toLong() ?.let(Instant::ofEpochSecond) ?: lastAwayMessageTime, diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/BufferViewConfigState.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/BufferViewConfigState.kt index 141716a..760d7d3 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/BufferViewConfigState.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/BufferViewConfigState.kt @@ -30,4 +30,6 @@ data class BufferViewConfigState( val buffers: List<BufferId> = emptyList(), val removedBuffers: Set<BufferId> = emptySet(), val hiddenBuffers: Set<BufferId> = emptySet(), -) +) { + fun identifier() = "$bufferViewId" +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/CertManagerState.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/CertManagerState.kt index 18e9f3c..ea6bb8b 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/CertManagerState.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/CertManagerState.kt @@ -19,4 +19,6 @@ data class CertManagerState( val certificatePem: String = "", val privateKey: PrivateKey? = null, val certificate: Certificate? = null -) +) { + fun identifier() = "${identityId.id}" +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QVariant.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QVariant.kt index 38f03d7..5022ca6 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QVariant.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QVariant.kt @@ -18,6 +18,8 @@ import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer import de.justjanne.libquassel.protocol.util.reflect.instanceof import de.justjanne.libquassel.protocol.util.reflect.subtype import java.nio.ByteBuffer +import org.threeten.bp.Instant +import org.threeten.bp.ZoneOffset /** * Simple alias for a generic QVariant type diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QVariantList.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QVariantList.kt index eba9cee..4ef290d 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QVariantList.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QVariantList.kt @@ -9,7 +9,9 @@ package de.justjanne.libquassel.protocol.variant +import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8 import de.justjanne.libquassel.protocol.util.collections.pairs +import java.nio.ByteBuffer /** * Simple alias for a generic QVariantList type @@ -19,7 +21,11 @@ typealias QVariantList = List<QVariant_> /** * Transform a QVariantList of interleaved keys and values into a QVariantMap */ -fun QVariantList.toVariantMap(): QVariantMap = +fun QVariantList.toVariantMap(byteBuffer: Boolean = false): QVariantMap = pairs { key, value -> - Pair(key.into(""), value) + if (byteBuffer) { + Pair(StringSerializerUtf8.deserializeRaw(key.into<ByteBuffer>()), value) + } else { + Pair(key.into(""), value) + } }.toMap() -- GitLab