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
Select Git revision

Target

Select target project
  • justJanne/libquassel
1 result
Select Git revision
Show changes
Showing
with 107 additions and 147 deletions
......@@ -65,6 +65,7 @@ enum class LegacyFeature(
* Support for custom rate limits for connections to IRC servers
*/
CustomRateLimits(0x0080u, QuasselFeature.CustomRateLimits),
/**
* Experimental support for (X)DCC transfers
*/
......@@ -107,7 +108,8 @@ enum class LegacyFeature(
/**
* Support for feature negotiation via a list of strings
*/
ExtendedFeatures(0x8000u, QuasselFeature.ExtendedFeatures);
ExtendedFeatures(0x8000u, QuasselFeature.ExtendedFeatures),
;
companion object : Flags<UInt, LegacyFeature> {
private val features = values().associateBy(LegacyFeature::feature)
......
......@@ -140,7 +140,9 @@ enum class QuasselFeature {
/**
* Support for controlling what IRCv3 capabilities are skipped during negotiation
*/
SkipIrcCaps;
SkipIrcCaps,
;
/**
* Get the standardized feature name
......
......@@ -9,12 +9,9 @@
package de.justjanne.libquassel.protocol.features
import de.justjanne.libquassel.annotations.Generated
/**
* Inline class encapsulating a quassel feature name
*/
@Generated
@JvmInline
value class QuasselFeatureName(
/**
......
......@@ -22,7 +22,11 @@ import java.nio.ByteBuffer
* @param to target buffer to copy to
* @param desiredAmount maximum amount to copy
*/
fun copyData(from: ByteBuffer, to: ByteBuffer, desiredAmount: Int) {
fun copyData(
from: ByteBuffer,
to: ByteBuffer,
desiredAmount: Int,
) {
val limit = from.limit()
val availableAmount = minOf(from.remaining(), to.remaining())
val amount = minOf(availableAmount, desiredAmount)
......@@ -41,7 +45,10 @@ fun copyData(from: ByteBuffer, to: ByteBuffer, desiredAmount: Int) {
* @param desiredAmount maximum amount to copy
* @return buffer of the copied data
*/
fun copyData(from: ByteBuffer, desiredAmount: Int): ByteBuffer {
fun copyData(
from: ByteBuffer,
desiredAmount: Int,
): ByteBuffer {
val to = ByteBuffer.allocate(minOf(from.remaining(), desiredAmount))
copyData(from, to, desiredAmount)
return to.withFlip()
......@@ -52,10 +59,11 @@ fun copyData(from: ByteBuffer, desiredAmount: Int): ByteBuffer {
*/
fun ByteBuffer?.isEmpty() = this == null || !this.hasRemaining()
private val alphabet = charArrayOf(
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
)
private val alphabet =
charArrayOf(
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
)
/**
* Utility function to generate a hexdump of a buffer
......
......@@ -36,8 +36,11 @@ class ChainedByteBuffer(
require(limit <= 0 || size + amount <= limit) {
"Can not allocate $amount bytes, currently at $size, limit is $limit"
}
return if (direct) ByteBuffer.allocateDirect(amount)
else ByteBuffer.allocate(amount)
return if (direct) {
ByteBuffer.allocateDirect(amount)
} else {
ByteBuffer.allocate(amount)
}
}
private fun ensureSpace(requested: Int) {
......@@ -168,8 +171,7 @@ class ChainedByteBuffer(
size = 0
}
override fun iterator(): Iterator<ByteBuffer> =
ChainedByteBufferIterator(this)
override fun iterator(): Iterator<ByteBuffer> = ChainedByteBufferIterator(this)
/**
* Convert this buffer into a bytebuffer of the same capacity and content.
......@@ -184,14 +186,12 @@ class ChainedByteBuffer(
}
private class ChainedByteBufferIterator(
private val buffer: ChainedByteBuffer
private val buffer: ChainedByteBuffer,
) : Iterator<ByteBuffer> {
private var index = 0
override fun hasNext() =
index < buffer.bufferList.size
override fun hasNext() = index < buffer.bufferList.size
override fun next(): ByteBuffer =
buffer.bufferList[index++].duplicate().withFlip()
override fun next(): ByteBuffer = buffer.bufferList[index++].duplicate().withFlip()
}
}
......@@ -10,11 +10,11 @@
package de.justjanne.libquassel.protocol.io
import de.justjanne.libquassel.protocol.util.StateHolder
import de.justjanne.libquassel.protocol.util.update
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.runInterruptible
import java.io.Closeable
import java.net.InetSocketAddress
......@@ -38,33 +38,36 @@ class CoroutineChannel : StateHolder<CoroutineChannelState>, Closeable {
socket.connect(address, timeout)
this.channel = StreamChannel(socket)
state.update {
copy(connected = true)
it.copy(connected = true)
}
}
fun enableCompression() {
channel = channel.withCompression()
state.update {
copy(compression = true)
it.copy(compression = true)
}
}
suspend fun enableTLS(context: SSLContext) {
channel = runInterruptible(writeContext) {
channel.withTLS(context)
}
channel =
runInterruptible(writeContext) {
channel.withTLS(context)
}
state.update {
copy(tlsInfo = channel.tlsInfo())
it.copy(tlsInfo = channel.tlsInfo())
}
}
suspend fun read(buffer: ByteBuffer): Int = runInterruptible(readContext) {
channel.read(buffer)
}
suspend fun read(buffer: ByteBuffer): Int =
runInterruptible(readContext) {
channel.read(buffer)
}
suspend fun write(buffer: ByteBuffer): Int = runInterruptible(writeContext) {
channel.write(buffer)
}
suspend fun write(buffer: ByteBuffer): Int =
runInterruptible(writeContext) {
channel.write(buffer)
}
suspend fun write(chainedBuffer: ChainedByteBuffer) {
for (buffer in chainedBuffer.iterator()) {
......@@ -72,18 +75,21 @@ class CoroutineChannel : StateHolder<CoroutineChannelState>, Closeable {
}
}
suspend fun flush() = runInterruptible(writeContext) {
channel.flush()
}
suspend fun flush() =
runInterruptible(writeContext) {
channel.flush()
}
override fun close() {
channel.close()
state.update {
copy(connected = false)
it.copy(connected = false)
}
}
override fun state(): CoroutineChannelState = state.value
override fun flow(): Flow<CoroutineChannelState> = state
private val state = MutableStateFlow(CoroutineChannelState())
}
......@@ -18,7 +18,7 @@ import java.util.zip.DeflaterOutputStream
* the current stream
*/
class FixedDeflaterOutputStream(
stream: OutputStream
stream: OutputStream,
) : DeflaterOutputStream(stream, true) {
/**
* Close the underlying stream and deflater
......
......@@ -18,7 +18,7 @@ import java.nio.channels.spi.AbstractInterruptibleChannel
* Utility function to wrap an input stream into a readable channel
*/
class ReadableWrappedChannel(
private var backingStream: InputStream
private var backingStream: InputStream,
) : AbstractInterruptibleChannel(), ReadableByteChannel {
private val buffer = ByteBuffer.allocate(PAGE_SIZE)
private val lock = Any()
......@@ -61,8 +61,9 @@ class ReadableWrappedChannel(
}
// read is negative if no data was read, in that case, terminate
if (readData < 0)
if (readData < 0) {
break
}
// add read amount to total
remainingData -= readData
......
......@@ -54,15 +54,14 @@ class StreamChannel constructor(
/**
* Return a copy of the current channel with TLS
*/
fun withTLS(
context: SSLContext,
): StreamChannel {
val sslSocket = context.socketFactory.createSocket(
this.socket,
this.socket.inetAddress.hostAddress,
this.socket.port,
true
) as SSLSocket
fun withTLS(context: SSLContext): StreamChannel {
val sslSocket =
context.socketFactory.createSocket(
this.socket,
this.socket.inetAddress.hostAddress,
this.socket.port,
true,
) as SSLSocket
sslSocket.useClientMode = true
sslSocket.startHandshake()
return StreamChannel(sslSocket)
......
......@@ -72,7 +72,10 @@ class StringEncoder(charset: Charset) {
return encodeInternal(charBuffer)
}
private fun decodeInternal(source: ByteBuffer, length: Int): CharBuffer {
private fun decodeInternal(
source: ByteBuffer,
length: Int,
): CharBuffer {
val charBuffer = charBuffer(length)
val oldlimit = source.limit()
source.limit(source.position() + length)
......@@ -90,7 +93,10 @@ class StringEncoder(charset: Charset) {
/**
* Decode a string with known length from a bytebuffer
*/
fun decode(source: ByteBuffer, length: Int): String {
fun decode(
source: ByteBuffer,
length: Int,
): String {
return decodeInternal(source, length).toString()
}
......
......@@ -14,9 +14,7 @@ import java.nio.ByteBuffer
/**
* Utility function to apply a closure to a chained byte buffer and return its data
*/
inline fun useChainedByteBuffer(
function: (ChainedByteBuffer) -> Unit
): ByteBuffer {
inline fun useChainedByteBuffer(function: (ChainedByteBuffer) -> Unit): ByteBuffer {
val buffer = ChainedByteBuffer()
function(buffer)
return buffer.toBuffer()
......
......@@ -18,7 +18,7 @@ import java.nio.channels.spi.AbstractInterruptibleChannel
* Utility function to wrap an output stream into a writable channel
*/
class WritableWrappedChannel(
private var backingStream: OutputStream
private var backingStream: OutputStream,
) : AbstractInterruptibleChannel(), WritableByteChannel {
private val buffer = ByteBuffer.allocate(PAGE_SIZE)
private val lock = Any()
......
......@@ -24,18 +24,20 @@ enum class BufferActivity(
OtherActivity(1),
/**
* A new unread mesage is available on this buffer
* A new unread message is available on this buffer
*/
NewMessage(2),
/**
* A highlight for the current user is available on this buffer
*/
Highlight(4);
Highlight(4),
;
companion object {
private val map = enumValues<BufferActivity>()
.associateBy(BufferActivity::value)
private val map =
enumValues<BufferActivity>()
.associateBy(BufferActivity::value)
/**
* Obtain a zone specification by its underlying representation
......
/*
* 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.protocol.models
import de.justjanne.bitflags.of
import de.justjanne.bitflags.toBits
import de.justjanne.libquassel.protocol.features.FeatureSet
import de.justjanne.libquassel.protocol.features.LegacyFeature
import de.justjanne.libquassel.protocol.features.QuasselFeatureName
import de.justjanne.libquassel.protocol.models.types.QtType
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,
val remoteAddress: String,
val location: String,
val version: String,
val versionDate: Instant?,
val connectedSince: Instant,
val secure: Boolean,
val features: FeatureSet
) {
fun toVariantMap() = mapOf(
"id" to qVariant(id, QtType.Int),
"remoteAddress" to qVariant(remoteAddress, QtType.QString),
"location" to qVariant(location, QtType.QString),
"clientVersion" to qVariant(version, QtType.QString),
"clientVersionDate" to qVariant(versionDate?.epochSecond?.toString(), QtType.QString),
"connectedSince" to qVariant(connectedSince.atOffset(ZoneOffset.UTC), QtType.QDateTime),
"secure" to qVariant(secure, QtType.Bool),
"features" to qVariant(features.legacyFeatures().toBits(), QtType.UInt),
"featureList" to qVariant(features.featureList().map(QuasselFeatureName::name), QtType.QStringList)
)
companion object {
fun fromVariantMap(properties: QVariantMap) = ConnectedClient(
id = properties["id"].into(-1),
remoteAddress = properties["remoteAddress"].into(""),
location = properties["location"].into(""),
version = properties["clientVersion"].into(""),
versionDate = properties["clientVersionDate"].into("")
.toLongOrNull()
?.let(Instant::ofEpochSecond),
connectedSince = properties["connectedSince"].into(Instant.EPOCH.atOffset(ZoneOffset.UTC)).toInstant(),
secure = properties["secure"].into(false),
features = FeatureSet.build(
LegacyFeature.of(properties["features"].into()),
properties["featureList"].into<QStringList>()
?.filterNotNull()
?.map(::QuasselFeatureName)
.orEmpty()
)
)
}
}
......@@ -11,6 +11,7 @@ package de.justjanne.libquassel.protocol.models
import de.justjanne.libquassel.protocol.features.FeatureSet
import de.justjanne.libquassel.protocol.models.ids.NetworkId
import de.justjanne.libquassel.protocol.models.network.IdentityDto
import de.justjanne.libquassel.protocol.models.setup.BackendInfo
import de.justjanne.libquassel.protocol.variant.QVariantMap
......@@ -75,7 +76,7 @@ sealed class HandshakeMessage {
/**
* HTML-formatted error message
*/
val errorString: String?
val errorString: String?,
) : HandshakeMessage()
/**
......@@ -91,7 +92,7 @@ sealed class HandshakeMessage {
/**
* Password of the core account
*/
val password: String?
val password: String?,
) : HandshakeMessage()
/**
......@@ -114,7 +115,7 @@ sealed class HandshakeMessage {
/**
* HTML-formatted error message
*/
val errorString: String?
val errorString: String?,
) : HandshakeMessage()
/**
......@@ -158,7 +159,7 @@ sealed class HandshakeMessage {
/**
* Authenticator backend configuration data
*/
val authSetupData: QVariantMap
val authSetupData: QVariantMap,
) : HandshakeMessage()
/**
......@@ -170,7 +171,7 @@ sealed class HandshakeMessage {
/**
* HTML-formatted error message
*/
val errorString: String?
val errorString: String?,
) : HandshakeMessage()
/**
......@@ -183,7 +184,7 @@ sealed class HandshakeMessage {
* Identity objects created or modified after [SessionInit] will be defined
* via sync updates and RPC identity creation messages.
*/
val identities: List<QVariantMap>,
val identities: List<IdentityDto>,
/**
* List of existing buffers at the current time.
*
......@@ -197,6 +198,6 @@ sealed class HandshakeMessage {
* Network objects created or modified after [SessionInit] will be defined
* via sync updates and RPC identity creation messages.
*/
val networkIds: List<NetworkId>
val networkIds: List<NetworkId>,
) : HandshakeMessage()
}
......@@ -57,7 +57,7 @@ data class Message(
/**
* Message content
*/
val content: String
val content: String,
) {
override fun toString(): String {
return "Message(" +
......
......@@ -36,7 +36,7 @@ sealed class SignalProxyMessage {
/**
* Parameters to the function call
*/
val params: QVariantList
val params: QVariantList,
) : SignalProxyMessage() {
override fun toString(): String {
return "SyncMessage::$className($objectName):$slotName(${params.size})"
......@@ -54,7 +54,7 @@ sealed class SignalProxyMessage {
/**
* Parameters to the function call
*/
val params: QVariantList
val params: QVariantList,
) : SignalProxyMessage() {
override fun toString(): String {
return "RpcCall::$slotName(${params.size})"
......@@ -73,7 +73,7 @@ sealed class SignalProxyMessage {
/**
* Name/ID of the specified object
*/
val objectName: String
val objectName: String,
) : SignalProxyMessage() {
override fun toString(): String {
return "InitRequest::$className($objectName)"
......@@ -96,7 +96,7 @@ sealed class SignalProxyMessage {
/**
* Current state of the specified object
*/
val initData: QVariantMap
val initData: QVariantMap,
) : SignalProxyMessage() {
override fun toString(): String {
return "InitData::$className($objectName)"
......@@ -110,7 +110,7 @@ sealed class SignalProxyMessage {
/**
* Local timestamp of the moment the message was sent
*/
val timestamp: Instant
val timestamp: Instant,
) : SignalProxyMessage() {
override fun toString(): String {
return "HeartBeat::$timestamp"
......@@ -124,7 +124,7 @@ sealed class SignalProxyMessage {
/**
* Local timestamp of the moment the original heartbeat was sent
*/
val timestamp: Instant
val timestamp: Instant,
) : SignalProxyMessage() {
override fun toString(): String {
return "HeartBeatReply::$timestamp"
......
......@@ -11,5 +11,5 @@ package de.justjanne.libquassel.protocol.models
data class StatusMessage(
val network: String?,
val message: String
val message: String,
)
......@@ -16,7 +16,7 @@ enum class TimeSpec(
/**
* Underlying representation
*/
val value: Byte
val value: Byte,
) {
/**
* Unknown zone data
......@@ -45,11 +45,13 @@ enum class TimeSpec(
/**
* Time with specified offset in seconds
*/
OffsetFromUTC(3);
OffsetFromUTC(3),
;
companion object {
private val map = enumValues<TimeSpec>()
.associateBy(TimeSpec::value)
private val map =
enumValues<TimeSpec>()
.associateBy(TimeSpec::value)
/**
* Obtain a zone specification by its underlying representation
......
......@@ -26,11 +26,13 @@ enum class DccIpDetectionMode(
/**
* Manually specified
*/
Manual(0x01u);
Manual(0x01u),
;
companion object {
private val values = enumValues<DccIpDetectionMode>()
.associateBy(DccIpDetectionMode::value)
private val values =
enumValues<DccIpDetectionMode>()
.associateBy(DccIpDetectionMode::value)
/**
* Obtain from underlying representation
......