Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • justJanne/libquassel
1 result
Show changes
Showing
with 297 additions and 1034 deletions
/*
* libquassel
* Copyright (c) 2024 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.backend
import de.justjanne.libquassel.protocol.api.client.HighlightRuleManagerClientApi
import de.justjanne.libquassel.protocol.variant.QVariantMap
import javax.inject.Inject
class HighlightRuleManagerPersister @Inject constructor(): HighlightRuleManagerClientApi {
override fun removeHighlightRule(highlightRule: Int) = Unit
override fun toggleHighlightRule(highlightRule: Int) = Unit
override fun addHighlightRule(id: Int, content: String?, isRegEx: Boolean, isCaseSensitive: Boolean, isEnabled: Boolean, isInverse: Boolean, sender: String?, channel: String?) = Unit
override fun setHighlightNick(highlightNick: Int) = Unit
override fun setNicksCaseSensitive(nicksCaseSensitive: Boolean) = Unit
override fun update(properties: QVariantMap) = Unit
}
/*
* libquassel
* Copyright (c) 2024 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.backend
import de.justjanne.libquassel.protocol.api.ObjectName
import de.justjanne.libquassel.protocol.api.client.IdentityClientApi
import de.justjanne.libquassel.protocol.models.QStringList
import de.justjanne.libquassel.protocol.models.ids.IdentityId
import de.justjanne.libquassel.protocol.variant.QVariantMap
import javax.inject.Inject
class IdentityPersister @Inject constructor(): IdentityClientApi {
override fun setAutoAwayEnabled(objectName: ObjectName, enabled: Boolean) = Unit
override fun setAutoAwayReason(objectName: ObjectName, reason: String?) = Unit
override fun setAutoAwayReasonEnabled(objectName: ObjectName, enabled: Boolean) = Unit
override fun setAutoAwayTime(objectName: ObjectName, time: Int) = Unit
override fun setAwayNick(objectName: ObjectName, awayNick: String?) = Unit
override fun setAwayNickEnabled(objectName: ObjectName, enabled: Boolean) = Unit
override fun setAwayReason(objectName: ObjectName, awayReason: String?) = Unit
override fun setAwayReasonEnabled(objectName: ObjectName, enabled: Boolean) = Unit
override fun setDetachAwayEnabled(objectName: ObjectName, enabled: Boolean) = Unit
override fun setDetachAwayReason(objectName: ObjectName, reason: String?) = Unit
override fun setDetachAwayReasonEnabled(objectName: ObjectName, enabled: Boolean) = Unit
override fun setId(objectName: ObjectName, id: IdentityId) = Unit
override fun setIdent(objectName: ObjectName, ident: String?) = Unit
override fun setIdentityName(objectName: ObjectName, name: String?) = Unit
override fun setKickReason(objectName: ObjectName, reason: String?) = Unit
override fun setNicks(objectName: ObjectName, nicks: QStringList) = Unit
override fun setPartReason(objectName: ObjectName, reason: String?) = Unit
override fun setQuitReason(objectName: ObjectName, reason: String?) = Unit
override fun setRealName(objectName: ObjectName, realName: String?) = Unit
override fun update(objectName: ObjectName, properties: QVariantMap) = Unit
}
/*
* libquassel
* Copyright (c) 2024 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.backend
import de.justjanne.libquassel.protocol.api.client.IgnoreListManagerClientApi
import de.justjanne.libquassel.protocol.variant.QVariantMap
import javax.inject.Inject
class IgnoreListManagerPersister @Inject constructor(): IgnoreListManagerClientApi {
override fun addIgnoreListItem(type: Int, ignoreRule: String?, isRegEx: Boolean, strictness: Int, scope: Int, scopeRule: String?, isActive: Boolean) = Unit
override fun removeIgnoreListItem(ignoreRule: String?) = Unit
override fun toggleIgnoreRule(ignoreRule: String?) = Unit
override fun update(properties: QVariantMap) = Unit
}
/*
* libquassel
* Copyright (c) 2024 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.backend
import de.justjanne.libquassel.protocol.api.ObjectName
import de.justjanne.libquassel.protocol.api.client.IrcChannelClientApi
import de.justjanne.libquassel.protocol.models.QStringList
import de.justjanne.libquassel.protocol.variant.QVariantMap
import javax.inject.Inject
class IrcChannelPersister @Inject constructor(): IrcChannelClientApi {
override fun addChannelMode(objectName: ObjectName, mode: Char, value: String?) = Unit
override fun addUserMode(objectName: ObjectName, nick: String, mode: String?) = Unit
override fun joinIrcUsers(objectName: ObjectName, nicks: QStringList, modes: QStringList) = Unit
override fun part(objectName: ObjectName, nick: String) = Unit
override fun removeChannelMode(objectName: ObjectName, mode: Char, value: String?) = Unit
override fun removeUserMode(objectName: ObjectName, nick: String, mode: String?) = Unit
override fun setEncrypted(objectName: ObjectName, encrypted: Boolean) = Unit
override fun setPassword(objectName: ObjectName, password: String) = Unit
override fun setTopic(objectName: ObjectName, topic: String) = Unit
override fun setUserModes(objectName: ObjectName, nick: String, modes: String?) = Unit
override fun update(objectName: ObjectName, properties: QVariantMap) = Unit
}
/*
* libquassel
* Copyright (c) 2024 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.backend
import de.justjanne.libquassel.protocol.api.client.IrcListHelperClientApi
import de.justjanne.libquassel.protocol.models.QStringList
import de.justjanne.libquassel.protocol.models.ids.NetworkId
import de.justjanne.libquassel.protocol.variant.QVariantList
import javax.inject.Inject
class IrcListHelperPersister @Inject constructor(): IrcListHelperClientApi {
override fun receiveChannelList(netId: NetworkId, channelFilters: QStringList, channels: QVariantList) = Unit
override fun reportError(error: String?) = Unit
override fun reportFinishedList(netId: NetworkId) = Unit
}
/*
* libquassel
* Copyright (c) 2024 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.backend
import de.justjanne.libquassel.protocol.api.ObjectName
import de.justjanne.libquassel.protocol.api.client.IrcUserClientApi
import de.justjanne.libquassel.protocol.variant.QVariantMap
import org.threeten.bp.temporal.Temporal
import javax.inject.Inject
class IrcUserPersister @Inject constructor(): IrcUserClientApi {
override fun addUserModes(objectName: ObjectName, modes: String) = Unit
override fun joinChannel(objectName: ObjectName, channelname: String) = Unit
override fun partChannel(objectName: ObjectName, channelname: String) = Unit
override fun quit(objectName: ObjectName) = Unit
override fun removeUserModes(objectName: ObjectName, modes: String) = Unit
override fun setAccount(objectName: ObjectName, account: String) = Unit
override fun setAway(objectName: ObjectName, away: Boolean) = Unit
override fun setAwayMessage(objectName: ObjectName, awayMessage: String) = Unit
override fun setEncrypted(objectName: ObjectName, encrypted: Boolean) = Unit
override fun setHost(objectName: ObjectName, host: String) = Unit
override fun setIdleTime(objectName: ObjectName, idleTime: Temporal) = Unit
override fun setIrcOperator(objectName: ObjectName, ircOperator: String) = Unit
override fun setLastAwayMessage(objectName: ObjectName, lastAwayMessage: Int) = Unit
override fun setLastAwayMessageTime(objectName: ObjectName, lastAwayMessageTime: Temporal) = Unit
override fun setLoginTime(objectName: ObjectName, loginTime: Temporal) = Unit
override fun setNick(objectName: ObjectName, newNick: String) = Unit
override fun setRealName(objectName: ObjectName, realName: String) = Unit
override fun setServer(objectName: ObjectName, server: String) = Unit
override fun setSuserHost(objectName: ObjectName, suserHost: String) = Unit
override fun setUser(objectName: ObjectName, user: String) = Unit
override fun setUserModes(objectName: ObjectName, modes: String) = Unit
override fun setWhoisServiceReply(objectName: ObjectName, whoisServiceReply: String) = Unit
override fun updateHostmask(objectName: ObjectName, mask: String) = Unit
override fun update(objectName: ObjectName, properties: QVariantMap) = Unit
}
/*
* libquassel
* Copyright (c) 2024 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.backend
import de.justjanne.libquassel.protocol.api.client.NetworkConfigClientApi
import de.justjanne.libquassel.protocol.variant.QVariantMap
import javax.inject.Inject
class NetworkConfigPersister @Inject constructor(): NetworkConfigClientApi {
override fun setAutoWhoDelay(delay: Int) = Unit
override fun setAutoWhoEnabled(enabled: Boolean) = Unit
override fun setAutoWhoInterval(interval: Int) = Unit
override fun setAutoWhoNickLimit(limit: Int) = Unit
override fun setMaxPingCount(count: Int) = Unit
override fun setPingInterval(interval: Int) = Unit
override fun setPingTimeoutEnabled(enabled: Boolean) = Unit
override fun setStandardCtcp(enabled: Boolean) = Unit
override fun update(properties: QVariantMap) = Unit
}
/*
* libquassel
* Copyright (c) 2024 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.backend
import de.justjanne.libquassel.protocol.api.ObjectName
import de.justjanne.libquassel.protocol.api.client.NetworkClientApi
import de.justjanne.libquassel.protocol.models.QStringList
import de.justjanne.libquassel.protocol.models.ids.IdentityId
import de.justjanne.libquassel.protocol.variant.QVariantList
import de.justjanne.libquassel.protocol.variant.QVariantMap
import java.nio.ByteBuffer
import javax.inject.Inject
class NetworkPersister @Inject constructor(): NetworkClientApi {
override fun setNetworkName(objectName: ObjectName, networkName: String) = Unit
override fun setCurrentServer(objectName: ObjectName, currentServer: String?) = Unit
override fun setMyNick(objectName: ObjectName, myNick: String?) = Unit
override fun setLatency(objectName: ObjectName, latency: Int) = Unit
override fun setCodecForServer(objectName: ObjectName, codecForServer: ByteBuffer) = Unit
override fun setCodecForEncoding(objectName: ObjectName, codecForEncoding: ByteBuffer) = Unit
override fun setCodecForDecoding(objectName: ObjectName, codecForDecoding: ByteBuffer) = Unit
override fun setIdentity(objectName: ObjectName, identityId: IdentityId) = Unit
override fun setConnected(objectName: ObjectName, isConnected: Boolean) = Unit
override fun setConnectionState(objectName: ObjectName, connectionState: Int) = Unit
override fun setUseRandomServer(objectName: ObjectName, useRandomServer: Boolean) = Unit
override fun setPerform(objectName: ObjectName, perform: QStringList) = Unit
override fun setSkipCaps(objectName: ObjectName, skipCaps: QStringList) = Unit
override fun setUseAutoIdentify(objectName: ObjectName, useAutoIdentify: Boolean) = Unit
override fun setAutoIdentifyService(objectName: ObjectName, autoIdentifyService: String) = Unit
override fun setAutoIdentifyPassword(objectName: ObjectName, autoIdentifyPassword: String) = Unit
override fun setUseSasl(objectName: ObjectName, useSasl: Boolean) = Unit
override fun setSaslAccount(objectName: ObjectName, saslAccount: String) = Unit
override fun setSaslPassword(objectName: ObjectName, saslPassword: String) = Unit
override fun setUseAutoReconnect(objectName: ObjectName, useAutoReconnect: Boolean) = Unit
override fun setAutoReconnectInterval(objectName: ObjectName, autoReconnectInterval: UInt) = Unit
override fun setAutoReconnectRetries(objectName: ObjectName, autoReconnectRetries: UShort) = Unit
override fun setUnlimitedReconnectRetries(objectName: ObjectName, unlimitedReconnectRetries: Boolean) = Unit
override fun setRejoinChannels(objectName: ObjectName, rejoinChannels: Boolean) = Unit
override fun setUseCustomMessageRate(objectName: ObjectName, useCustomMessageRate: Boolean) = Unit
override fun setMessageRateBurstSize(objectName: ObjectName, messageRateBurstSize: UInt) = Unit
override fun setMessageRateDelay(objectName: ObjectName, messageRateDelay: UInt) = Unit
override fun setUnlimitedMessageRate(objectName: ObjectName, unlimitedMessageRate: Boolean) = Unit
override fun setServerList(objectName: ObjectName, serverList: QVariantList) = Unit
override fun addSupport(objectName: ObjectName, param: String, value: String) = Unit
override fun removeSupport(objectName: ObjectName, param: String) = Unit
override fun addCap(objectName: ObjectName, capability: String, value: String) = Unit
override fun acknowledgeCap(objectName: ObjectName, capability: String) = Unit
override fun removeCap(objectName: ObjectName, capability: String) = Unit
override fun clearCaps(objectName: ObjectName) = Unit
override fun addIrcUser(objectName: ObjectName, hostmask: String) = Unit
override fun addIrcChannel(objectName: ObjectName, channel: String) = Unit
override fun update(objectName: ObjectName, properties: QVariantMap) = Unit
}
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2024 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.protocol.syncables.common
package de.justjanne.libquassel.backend
import de.justjanne.libquassel.protocol.api.client.RpcClientApi
import de.justjanne.libquassel.protocol.models.BufferInfo
import de.justjanne.libquassel.protocol.models.Message
import de.justjanne.libquassel.protocol.models.ids.IdentityId
import de.justjanne.libquassel.protocol.models.ids.NetworkId
import de.justjanne.libquassel.protocol.session.Session
import de.justjanne.libquassel.protocol.syncables.SyncableObject
import de.justjanne.libquassel.protocol.syncables.stubs.RpcHandlerStub
import de.justjanne.libquassel.protocol.variant.QVariantMap
open class RpcHandler(
session: Session? = null
) : SyncableObject(session, ""), RpcHandlerStub {
init {
initialized = true
}
override fun bufferInfoUpdated(bufferInfo: BufferInfo) {
session?.bufferSyncer?.bufferInfoUpdated(bufferInfo)
}
override fun identityCreated(identity: QVariantMap) {
session?.addIdentity(identity)
}
override fun identityRemoved(identityId: IdentityId) {
session?.removeIdentity(identityId)
}
override fun networkCreated(networkId: NetworkId) {
session?.addNetwork(networkId)
}
override fun networkRemoved(networkId: NetworkId) {
session?.removeNetwork(networkId)
}
import java.nio.ByteBuffer
import javax.inject.Inject
class RpcPersister @Inject constructor(): RpcClientApi {
override fun objectRenamed(classname: ByteBuffer, newName: String?, oldName: String?) = Unit
override fun displayMsg(message: Message) = Unit
override fun displayStatusMsg(net: String?, msg: String?) = Unit
override fun bufferInfoUpdated(bufferInfo: BufferInfo) = Unit
override fun identityCreated(identity: QVariantMap) = Unit
override fun identityRemoved(identityId: IdentityId) = Unit
override fun networkCreated(networkId: NetworkId) = Unit
override fun networkRemoved(networkId: NetworkId) = Unit
override fun passwordChanged(peer: ULong, success: Boolean) = Unit
override fun disconnectFromCore() = Unit
}
/*
* 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.StateHolder
import de.justjanne.libquassel.protocol.util.update
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
class BaseInitHandler(
private val session: ClientSession
) : StateHolder<BaseInitHandlerState> {
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
override fun state(): BaseInitHandlerState = state.value
override fun flow(): Flow<BaseInitHandlerState> = state
private val state = MutableStateFlow(BaseInitHandlerState())
}
/*
* 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()
)
/*
* 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.models.HandshakeMessage
import de.justjanne.libquassel.protocol.models.SignalProxyMessage
import de.justjanne.libquassel.protocol.session.ConnectionHandler
import de.justjanne.libquassel.protocol.session.MessageChannel
abstract class ClientConnectionHandler : ConnectionHandler {
protected var channel: MessageChannel? = null
private val readyQueue = CoroutineQueue<Unit>()
override suspend fun init(channel: MessageChannel): Boolean {
this.channel = channel
readyQueue.resume(Unit)
return false
}
override suspend fun done() {
this.channel = null
}
suspend fun emit(message: SignalProxyMessage) {
if (channel == null) {
readyQueue.wait()
}
channel?.emit(message)
}
suspend fun emit(message: HandshakeMessage) {
if (channel == null) {
readyQueue.wait()
}
channel?.emit(message)
}
}
/*
* 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.CoroutineKeyedQueue
import de.justjanne.libquassel.protocol.exceptions.HandshakeException
import de.justjanne.libquassel.protocol.features.FeatureSet
import de.justjanne.libquassel.protocol.models.HandshakeMessage
import de.justjanne.libquassel.protocol.serializers.HandshakeMessageSerializer
import de.justjanne.libquassel.protocol.session.CoreState
import de.justjanne.libquassel.protocol.session.HandshakeHandler
import de.justjanne.libquassel.protocol.session.Session
import de.justjanne.libquassel.protocol.util.log.trace
import de.justjanne.libquassel.protocol.variant.QVariantMap
import org.slf4j.LoggerFactory
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))
}
private fun dispatch(message: HandshakeMessage): Boolean {
logger.trace { "Read handshake message $message" }
messageQueue.resume(message.javaClass, message)
if (message is HandshakeMessage.SessionInit) {
sessionInit = message
return true
}
return false
}
override suspend fun done() {
super.done()
val message = sessionInit
if (message != null) {
session.init(message.identities, message.bufferInfos, message.networkIds)
}
}
override suspend fun init(
clientVersion: String,
buildDate: String,
featureSet: FeatureSet
): CoreState {
when (
val response = messageQueue.wait(
HandshakeMessage.ClientInitAck::class.java,
HandshakeMessage.ClientInitReject::class.java
) {
emit(HandshakeMessage.ClientInit(clientVersion, buildDate, featureSet))
}
) {
is HandshakeMessage.ClientInitReject ->
throw HandshakeException.InitException(response.errorString ?: "Unknown Error")
is HandshakeMessage.ClientInitAck -> {
channel!!.negotiatedFeatures = response.featureSet
if (response.coreConfigured == null) {
throw HandshakeException.InitException("Unknown Error")
} else if (response.coreConfigured == true) {
return CoreState.Configured
} else {
return CoreState.Unconfigured(
response.backendInfo,
response.authenticatorInfo
)
}
}
else -> throw HandshakeException.InitException("Unknown Error")
}
}
override suspend fun login(username: String, password: String) {
when (
val response = messageQueue.wait(
HandshakeMessage.ClientLoginAck::class.java,
HandshakeMessage.ClientLoginReject::class.java
) {
emit(HandshakeMessage.ClientLogin(username, password))
}
) {
is HandshakeMessage.ClientLoginReject ->
throw HandshakeException.LoginException(response.errorString ?: "Unknown Error")
is HandshakeMessage.ClientLoginAck ->
return
else -> throw HandshakeException.LoginException("Unknown Error")
}
}
override suspend fun configureCore(
adminUsername: String,
adminPassword: String,
backend: String,
backendConfiguration: QVariantMap,
authenticator: String,
authenticatorConfiguration: QVariantMap
) {
when (
val response = messageQueue.wait(
HandshakeMessage.CoreSetupAck::class.java,
HandshakeMessage.CoreSetupReject::class.java
) {
emit(
HandshakeMessage.CoreSetupData(
adminUsername, adminPassword, backend, backendConfiguration, authenticator, authenticatorConfiguration
)
)
}
) {
is HandshakeMessage.CoreSetupReject ->
throw HandshakeException.SetupException(response.errorString ?: "Unknown Error")
is HandshakeMessage.CoreSetupAck ->
return
else -> throw HandshakeException.SetupException("Unknown Error")
}
}
companion object {
private val logger = LoggerFactory.getLogger(ClientHandshakeHandler::class.java)
}
}
/*
* 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.connection.ClientHeader
import de.justjanne.libquassel.protocol.connection.ClientHeaderSerializer
import de.justjanne.libquassel.protocol.connection.CoreHeaderSerializer
import de.justjanne.libquassel.protocol.connection.ProtocolFeature
import de.justjanne.libquassel.protocol.connection.ProtocolFeatures
import de.justjanne.libquassel.protocol.connection.ProtocolMeta
import de.justjanne.libquassel.protocol.features.FeatureSet
import de.justjanne.libquassel.protocol.session.ConnectionHandler
import de.justjanne.libquassel.protocol.session.MessageChannel
import de.justjanne.libquassel.protocol.util.log.trace
import org.slf4j.LoggerFactory
import java.nio.ByteBuffer
import javax.net.ssl.SSLContext
class ClientMagicHandler(
private val protocolFeatures: ProtocolFeatures,
private val protocols: List<ProtocolMeta>,
private val sslContext: SSLContext
) : ConnectionHandler {
private val connectionFeatureSet = FeatureSet.none()
override suspend fun init(channel: MessageChannel): Boolean {
val header = ClientHeader(
features = protocolFeatures,
versions = protocols
)
logger.trace { "Writing client header $header" }
channel.emit(sizePrefix = false) {
ClientHeaderSerializer.serialize(
it,
header,
connectionFeatureSet
)
}
val handshakeBuffer = ByteBuffer.allocateDirect(4)
channel.channel.read(handshakeBuffer)
handshakeBuffer.flip()
val protocol = CoreHeaderSerializer.deserialize(handshakeBuffer, connectionFeatureSet)
logger.trace { "Read server header $protocol" }
if (protocol.features.contains(ProtocolFeature.TLS)) {
channel.channel.enableTLS(sslContext)
}
if (protocol.features.contains(ProtocolFeature.Compression)) {
channel.channel.enableCompression()
}
return true
}
override suspend fun read(buffer: ByteBuffer) = true
override suspend fun done() = Unit
companion object {
private val logger = LoggerFactory.getLogger(ClientMagicHandler::class.java)
}
}
/*
* 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.annotations.ProtocolSide
import de.justjanne.libquassel.protocol.exceptions.RpcInvocationFailedException
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.nio.ByteBuffer
class ClientProxyMessageHandler(
private val heartBeatHandler: HeartBeatHandler,
private val objectRepository: ObjectRepository,
private val rpcHandler: RpcHandler,
private val baseInitHandler: BaseInitHandler
) : ProxyMessageHandler, ClientConnectionHandler() {
override suspend fun read(buffer: ByteBuffer): Boolean {
dispatch(SignalProxyMessageSerializer.deserialize(buffer, channel!!.negotiatedFeatures))
return false
}
override suspend fun dispatch(message: SignalProxyMessage) {
logger.trace { "Read signal proxy message $message" }
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
)
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)
}
}
}
companion object {
private val logger = LoggerFactory.getLogger(ClientProxyMessageHandler::class.java)
}
}
/*
* 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.models.Message
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) {
override fun objectRenamed(classname: ByteBuffer, newName: String?, oldName: String?) {
val objectRepository = session?.objectRepository ?: return
val className = StringSerializerUtf8.deserializeRaw(classname)
val syncable = objectRepository.find(className, oldName ?: return)
?: return
objectRepository.rename(syncable, newName ?: return)
}
override fun displayMsg(message: Message) {
runBlocking(Dispatchers.Default) {
messages.emit(message)
}
}
override fun displayStatusMsg(net: String?, msg: String?) {
runBlocking(Dispatchers.Default) {
statusMessage.emit(StatusMessage(net, msg ?: ""))
}
}
@Suppress("NOTHING_TO_INLINE")
inline fun messages(): Flow<Message> = messages
@PublishedApi
internal val messages = MutableSharedFlow<Message>()
@Suppress("NOTHING_TO_INLINE")
inline fun statusMessage(): StateFlow<StatusMessage?> = statusMessage
@PublishedApi
internal val statusMessage = MutableStateFlow<StatusMessage?>(null)
}
/*
* 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.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
import de.justjanne.libquassel.protocol.models.ids.NetworkId
import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8
import de.justjanne.libquassel.protocol.session.CommonSyncProxy
import de.justjanne.libquassel.protocol.session.MessageChannel
import de.justjanne.libquassel.protocol.session.MessageChannelReader
import de.justjanne.libquassel.protocol.session.Session
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.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
import de.justjanne.libquassel.protocol.syncables.common.Identity
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.StateHolder
import de.justjanne.libquassel.protocol.util.log.info
import de.justjanne.libquassel.protocol.util.update
import de.justjanne.libquassel.protocol.variant.QVariantMap
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import org.slf4j.LoggerFactory
import javax.net.ssl.SSLContext
class ClientSession(
connection: CoroutineChannel,
protocolFeatures: ProtocolFeatures,
protocols: List<ProtocolMeta>,
sslContext: SSLContext
) : Session, StateHolder<ClientSessionState> {
override val side = ProtocolSide.CLIENT
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,
baseInitHandler
)
override val proxy = CommonSyncProxy(
ProtocolSide.CLIENT,
objectRepository,
proxyMessageHandler
)
private val magicHandler = ClientMagicHandler(protocolFeatures, protocols, sslContext)
private val messageChannel = MessageChannel(connection)
init {
messageChannel.register(magicHandler)
messageChannel.register(handshakeHandler)
messageChannel.register(proxyMessageHandler)
MessageChannelReader(messageChannel).start()
}
override fun init(
identityInfo: List<QVariantMap>,
bufferInfos: List<BufferInfo>,
networkIds: List<NetworkId>
) {
logger.info {
"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),
)
}
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]
override fun addNetwork(id: NetworkId) {
val network = Network(this, state = NetworkState(id))
proxy.synchronize(network)
state.update {
copy(networks = networks + Pair(id, network))
}
}
override fun removeNetwork(id: NetworkId) {
val network = network(id) ?: return
proxy.stopSynchronize(network)
state.update {
copy(networks = networks - id)
}
}
override fun networks() = state().networks.values.toSet()
override fun identity(id: IdentityId) = state().identities[id]
override fun addIdentity(properties: QVariantMap) {
val identity = Identity(this)
identity.fromVariantMap(properties)
proxy.synchronize(identity)
state.update {
copy(identities = identities + Pair(identity.id(), identity))
}
}
override fun removeIdentity(id: IdentityId) {
val identity = identity(id) ?: return
proxy.stopSynchronize(identity)
state.update {
copy(identities = identities - id)
}
}
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),
oldName,
newName
)
}
override val aliasManager get() = state().aliasManager
override val backlogManager get() = state().backlogManager
override val bufferSyncer get() = state().bufferSyncer
override val bufferViewManager get() = state().bufferViewManager
override val highlightRuleManager get() = state().highlightRuleManager
override val ignoreListManager get() = state().ignoreListManager
override val ircListHelper get() = state().ircListHelper
override val coreInfo get() = state().coreInfo
override val dccConfig get() = state().dccConfig
override val networkConfig get() = state().networkConfig
override fun state(): ClientSessionState = state.value
override fun flow(): Flow<ClientSessionState> = state
private val state = MutableStateFlow(
ClientSessionState(
networks = mapOf(),
identities = mapOf(),
certManagers = mapOf(),
aliasManager = AliasManager(this),
backlogManager = ClientBacklogManager(this),
bufferSyncer = BufferSyncer(this),
bufferViewManager = BufferViewManager(this),
highlightRuleManager = HighlightRuleManager(this),
ignoreListManager = IgnoreListManager(this),
ircListHelper = IrcListHelper(this),
coreInfo = CoreInfo(this),
dccConfig = DccConfig(this),
networkConfig = NetworkConfig(this)
)
)
companion object {
private val logger = LoggerFactory.getLogger(ClientSession::class.java)
}
}
/*
* 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.syncables.ClientBacklogManager
import de.justjanne.libquassel.protocol.models.ids.IdentityId
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
import de.justjanne.libquassel.protocol.syncables.common.Identity
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
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,
val bufferViewManager: BufferViewManager,
val ignoreListManager: IgnoreListManager,
val highlightRuleManager: HighlightRuleManager,
val ircListHelper: IrcListHelper,
val coreInfo: CoreInfo,
val dccConfig: DccConfig,
val networkConfig: NetworkConfig
)
/*
* 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.syncables
import de.justjanne.bitflags.none
import de.justjanne.bitflags.of
import de.justjanne.bitflags.toBits
import de.justjanne.libquassel.client.util.CoroutineKeyedQueue
import de.justjanne.libquassel.protocol.models.flags.MessageFlag
import de.justjanne.libquassel.protocol.models.flags.MessageFlags
import de.justjanne.libquassel.protocol.models.flags.MessageType
import de.justjanne.libquassel.protocol.models.flags.MessageTypes
import de.justjanne.libquassel.protocol.models.ids.BufferId
import de.justjanne.libquassel.protocol.models.ids.MsgId
import de.justjanne.libquassel.protocol.session.Session
import de.justjanne.libquassel.protocol.syncables.common.BacklogManager
import de.justjanne.libquassel.protocol.variant.QVariantList
class ClientBacklogManager(
session: Session
) : BacklogManager(session) {
private val bufferQueue = CoroutineKeyedQueue<BacklogData.Buffer, QVariantList>()
private val bufferFilteredQueue = CoroutineKeyedQueue<BacklogData.BufferFiltered, QVariantList>()
private val bufferForwardQueue = CoroutineKeyedQueue<BacklogData.BufferForward, QVariantList>()
private val allQueue = CoroutineKeyedQueue<BacklogData.All, QVariantList>()
private val allFilteredQueue = CoroutineKeyedQueue<BacklogData.AllFiltered, QVariantList>()
suspend fun backlog(
bufferId: BufferId,
first: MsgId = MsgId(-1),
last: MsgId = MsgId(-1),
limit: Int = -1,
additional: Int = 0
): QVariantList =
bufferQueue.wait(BacklogData.Buffer(bufferId, first, last, limit, additional)) {
requestBacklog(bufferId, first, last, limit, additional)
}
suspend fun backlogFiltered(
bufferId: BufferId,
first: MsgId = MsgId(-1),
last: MsgId = MsgId(-1),
limit: Int = -1,
additional: Int = 0,
type: MessageTypes = MessageType.none(),
flags: MessageFlags = MessageFlag.none()
): QVariantList =
bufferFilteredQueue.wait(BacklogData.BufferFiltered(bufferId, first, last, limit, additional, type, flags)) {
requestBacklogFiltered(bufferId, first, last, limit, additional, type.toBits().toInt(), flags.toBits().toInt())
}
suspend fun backlogForward(
bufferId: BufferId,
first: MsgId = MsgId(-1),
last: MsgId = MsgId(-1),
limit: Int = -1,
type: MessageTypes = MessageType.none(),
flags: MessageFlags = MessageFlag.none()
): QVariantList =
bufferForwardQueue.wait(BacklogData.BufferForward(bufferId, first, last, limit, type, flags)) {
requestBacklogForward(bufferId, first, last, limit, type.toBits().toInt(), flags.toBits().toInt())
}
suspend fun backlogAll(
first: MsgId = MsgId(-1),
last: MsgId = MsgId(-1),
limit: Int = -1,
additional: Int = 0
): QVariantList =
allQueue.wait(BacklogData.All(first, last, limit, additional)) {
requestBacklogAll(first, last, limit, additional)
}
suspend fun backlogAllFiltered(
first: MsgId = MsgId(-1),
last: MsgId = MsgId(-1),
limit: Int = -1,
additional: Int = 0,
type: MessageTypes = MessageType.none(),
flags: MessageFlags = MessageFlag.none()
): QVariantList =
allFilteredQueue.wait(BacklogData.AllFiltered(first, last, limit, additional, type, flags)) {
requestBacklogAllFiltered(first, last, limit, additional, type.toBits().toInt(), flags.toBits().toInt())
}
override fun receiveBacklog(
bufferId: BufferId,
first: MsgId,
last: MsgId,
limit: Int,
additional: Int,
messages: QVariantList
) {
bufferQueue.resume(
BacklogData.Buffer(
bufferId,
first,
last,
limit,
additional
),
messages
)
super.receiveBacklog(bufferId, first, last, limit, additional, messages)
}
override fun receiveBacklogFiltered(
bufferId: BufferId,
first: MsgId,
last: MsgId,
limit: Int,
additional: Int,
type: Int,
flags: Int,
messages: QVariantList
) {
bufferFilteredQueue.resume(
BacklogData.BufferFiltered(
bufferId,
first,
last,
limit,
additional,
MessageType.of(type.toUInt()),
MessageFlag.of(flags.toUInt())
),
messages
)
super.receiveBacklogFiltered(bufferId, first, last, limit, additional, type, flags, messages)
}
override fun receiveBacklogForward(
bufferId: BufferId,
first: MsgId,
last: MsgId,
limit: Int,
type: Int,
flags: Int,
messages: QVariantList
) {
bufferForwardQueue.resume(
BacklogData.BufferForward(
bufferId,
first,
last,
limit,
MessageType.of(type.toUInt()),
MessageFlag.of(flags.toUInt())
),
messages
)
super.receiveBacklogForward(bufferId, first, last, limit, type, flags, messages)
}
override fun receiveBacklogAll(first: MsgId, last: MsgId, limit: Int, additional: Int, messages: QVariantList) {
allQueue.resume(
BacklogData.All(
first,
last,
limit,
additional
),
messages
)
super.receiveBacklogAll(first, last, limit, additional, messages)
}
override fun receiveBacklogAllFiltered(
first: MsgId,
last: MsgId,
limit: Int,
additional: Int,
type: Int,
flags: Int,
messages: QVariantList
) {
allFilteredQueue.resume(
BacklogData.AllFiltered(
first,
last,
limit,
additional,
MessageType.of(type.toUInt()),
MessageFlag.of(flags.toUInt()),
),
messages
)
super.receiveBacklogAllFiltered(first, last, limit, additional, type, flags, messages)
}
sealed class BacklogData {
data class Buffer(
val bufferId: BufferId,
val first: MsgId = MsgId(-1),
val last: MsgId = MsgId(-1),
val limit: Int = -1,
val additional: Int = 0
) : BacklogData()
data class BufferFiltered(
val bufferId: BufferId,
val first: MsgId = MsgId(-1),
val last: MsgId = MsgId(-1),
val limit: Int = -1,
val additional: Int = 0,
val type: MessageTypes = MessageType.all,
val flags: MessageFlags = MessageFlag.all
) : BacklogData()
data class BufferForward(
val bufferId: BufferId,
val first: MsgId = MsgId(-1),
val last: MsgId = MsgId(-1),
val limit: Int = -1,
val type: MessageTypes = MessageType.all,
val flags: MessageFlags = MessageFlag.all
) : BacklogData()
data class All(
val first: MsgId = MsgId(-1),
val last: MsgId = MsgId(-1),
val limit: Int = -1,
val additional: Int = 0
) : BacklogData()
data class AllFiltered(
val first: MsgId = MsgId(-1),
val last: MsgId = MsgId(-1),
val limit: Int = -1,
val additional: Int = 0,
val type: MessageTypes = MessageType.all,
val flags: MessageFlags = MessageFlag.all
) : BacklogData()
}
}
/*
* 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.syncables
import de.justjanne.libquassel.client.exceptions.IrcListException
import de.justjanne.libquassel.protocol.models.QStringList
import de.justjanne.libquassel.protocol.models.ids.NetworkId
import de.justjanne.libquassel.protocol.session.Session
import de.justjanne.libquassel.protocol.syncables.common.IrcListHelper
import de.justjanne.libquassel.protocol.variant.QVariantList
import de.justjanne.libquassel.protocol.variant.into
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
class ClientIrcListHelper(
session: Session
) : IrcListHelper(session) {
private val waitingContinuations = mutableMapOf<NetworkId, Continuation<List<ChannelDescription>>>()
private val readyContinuations = mutableMapOf<NetworkId, Continuation<List<ChannelDescription>>>()
suspend fun channelList(
networkId: NetworkId,
channelFilters: List<String>
) = suspendCoroutine<List<ChannelDescription>> {
waitingContinuations[networkId] = it
requestChannelList(networkId, channelFilters)
}
override fun reportFinishedList(netId: NetworkId) {
val continuation = waitingContinuations.remove(netId)
if (continuation != null) {
readyContinuations[netId] = continuation
requestChannelList(netId, emptyList())
}
super.reportFinishedList(netId)
}
override fun reportError(error: String?) {
for (continuation in waitingContinuations.values + readyContinuations.values) {
continuation.resumeWith(Result.failure(IrcListException(error ?: "Unknown Error")))
}
super.reportError(error)
}
override fun receiveChannelList(netId: NetworkId, channelFilters: QStringList, channels: QVariantList) {
readyContinuations[netId]?.resume(
channels.mapNotNull {
val list = it.into<QVariantList>().orEmpty()
if (list.size == 3) {
ChannelDescription(
netId,
list[0].into(""),
list[1].into(0u),
list[2].into(""),
)
} else {
null
}
}
)
super.receiveChannelList(netId, channelFilters, channels)
}
data class ChannelDescription(
val netId: NetworkId,
val channelName: String,
val userCount: UInt,
val topic: String
)
}