diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/ExtendedFeature.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/ExtendedFeature.kt new file mode 100644 index 0000000000000000000000000000000000000000..7f5fd50730abc37cb9bd3a707a30b0232674d965 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/ExtendedFeature.kt @@ -0,0 +1,68 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol + +inline class ExtendedFeatureName( + val name: String, +) + +enum class ExtendedFeature { + SynchronizedMarkerLine, + SaslAuthentication, + SaslExternal, + HideInactiveNetworks, + PasswordChange, + /** IRCv3 capability negotiation, account tracking */ + CapNegotiation, + /** IRC server SSL validation */ + VerifyServerSSL, + /** IRC server custom message rate limits */ + CustomRateLimits, + // Currently not supported + DccFileTransfer, + /** Timestamp formatting in away (e.g. %%hh:mm%%) */ + AwayFormatTimestamp, + /** Whether or not the core supports auth backends. */ + Authenticators, + /** Sync buffer activity status */ + BufferActivitySync, + /** Core-Side highlight configuration and matching */ + CoreSideHighlights, + /** Show prefixes for senders in backlog */ + SenderPrefixes, + /** Supports RPC call disconnectFromCore to remotely disconnect a client */ + RemoteDisconnect, + /** Transmit features as list of strings */ + ExtendedFeatures, + /** Serialize message time as 64-bit */ + LongTime, + /** Real Name and Avatar URL in backlog */ + RichMessages, + /** Backlogmanager supports filtering backlog by messagetype */ + BacklogFilterType, + /** ECDSA keys for CertFP in identities */ + EcdsaCertfpKeys, + /** 64-bit IDs for messages */ + LongMessageId, + /** CoreInfo dynamically updated using signals */ + SyncedCoreInfo; + + fun name(): ExtendedFeatureName = ExtendedFeatureName(name) +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/Flag.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/Flag.kt new file mode 100644 index 0000000000000000000000000000000000000000..f524148d2ad80245fe0ae033b125d37a96d0f86f --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/Flag.kt @@ -0,0 +1,67 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol + +import kotlin.experimental.or + + +interface Flag<T> { + val value: T +} + +@JvmName("toByteFlag") +fun Set<Flag<Byte>>.toFlag(): Byte = fold(0.toByte()) { acc, el -> + acc or el.value +} + +@JvmName("toUByteFlag") +fun Set<Flag<UByte>>.toFlag(): UByte = fold(0.toUByte()) { acc, el -> + acc or el.value +} + +@JvmName("toShortFlag") +fun Set<Flag<Short>>.toFlag(): Short = fold(0.toShort()) { acc, el -> + acc or el.value +} + +@JvmName("toUShortFlag") +fun Set<Flag<UShort>>.toFlag(): UShort = fold(0.toUShort()) { acc, el -> + acc or el.value +} + +@JvmName("toIntFlag") +fun Set<Flag<Int>>.toFlag(): Int = fold(0) { acc, el -> + acc or el.value +} + +@JvmName("toUIntFlag") +fun Set<Flag<UInt>>.toFlag(): UInt = fold(0.toUInt()) { acc, el -> + acc or el.value +} + +@JvmName("toLongFlag") +fun Set<Flag<Long>>.toFlag(): Long = fold(0.toLong()) { acc, el -> + acc or el.value +} + +@JvmName("toULongFlag") +fun Set<Flag<ULong>>.toFlag(): ULong = fold(0.toULong()) { acc, el -> + acc or el.value +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/LegacyFeature.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/LegacyFeature.kt new file mode 100644 index 0000000000000000000000000000000000000000..672974f719c9e13f3fd1e1a7d1d8207867c27187 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/LegacyFeature.kt @@ -0,0 +1,58 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol + +/** + * A list of features that are optional in core and/or client, but need runtime checking + * + * Some features require an uptodate counterpart, but don't justify a protocol break. + * This is what we use this enum for. Add such features to it and check at runtime on the other + * side for their existence. + * + * This list should be cleaned up after every protocol break, as we can assume them to be present then. + */ +enum class LegacyFeature(override val value: UInt): Flag<UInt> { + SynchronizedMarkerLine(0x0001u), + SaslAuthentication(0x0002u), + SaslExternal(0x0004u), + HideInactiveNetworks(0x0008u), + PasswordChange(0x0010u), + /** IRCv3 capability negotiation, account tracking */ + CapNegotiation(0x0020u), + /** IRC server SSL validation */ + VerifyServerSSL(0x0040u), + /** IRC server custom message rate limits */ + CustomRateLimits(0x0080u), + DccFileTransfer(0x0100u), + /** Timestamp formatting in away (e.g. %%hh:mm%%) */ + AwayFormatTimestamp(0x0200u), + /** Whether or not the core supports auth backends. */ + Authenticators(0x0400u), + /** Sync buffer activity status */ + BufferActivitySync(0x0800u), + /** Core-Side highlight configuration and matching */ + CoreSideHighlights(0x1000u), + /** Show prefixes for senders in backlog */ + SenderPrefixes(0x2000u), + /** Supports RPC call disconnectFromCore to remotely disconnect a client */ + RemoteDisconnect(0x4000u), + /** Transmit features as list of strings */ + ExtendedFeatures(0x8000u); +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/handshake/ClientInit.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/handshake/ClientInit.kt new file mode 100644 index 0000000000000000000000000000000000000000..9a271d523900b1ab1072cc3b1c90debc76b37cbb --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/handshake/ClientInit.kt @@ -0,0 +1,31 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol.handshake + +import de.kuschku.quasseldroid.protocol.ExtendedFeature +import de.kuschku.quasseldroid.protocol.LegacyFeature +import de.kuschku.quasseldroid.protocol.serializers.QtSerializer + +class ClientInit( + val clientVersion: String?, + val buildDate: String?, + val clientFeatures: Set<LegacyFeature>?, + val featureList: List<ExtendedFeature>?, +) diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/ChainedByteBuffer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/ChainedByteBuffer.kt similarity index 83% rename from app/src/main/java/de/kuschku/quasseldroid/protocol/ChainedByteBuffer.kt rename to app/src/main/java/de/kuschku/quasseldroid/protocol/io/ChainedByteBuffer.kt index 871b104c4d4c8825961a7cc4af9358307bf4ff08..139b1297243b15f6036af106557dfa72aad594c1 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/protocol/ChainedByteBuffer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/ChainedByteBuffer.kt @@ -17,14 +17,9 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid.protocol +package de.kuschku.quasseldroid.protocol.io -import de.kuschku.quasseldroid.util.CoroutineChannel -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.yield import java.nio.ByteBuffer -import java.nio.channels.AsynchronousByteChannel -import java.nio.channels.WritableByteChannel import java.util.* class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct: Boolean = false) { @@ -53,6 +48,11 @@ class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct: this.size += size } + fun nextBuffer(length: Int = 1): ByteBuffer { + ensureSpace(length) + return bufferList.last() + } + fun put(value: Byte) { ensureSpace(1) @@ -96,11 +96,18 @@ class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct: } fun put(value: ByteBuffer) { - while (value.remaining() > 8) { - putLong(value.long) - } - while (value.hasRemaining()) { - put(value.get()) + ensureSpace(value.remaining()) + + while (value.remaining() > 0) { + val buffer = bufferList.last() + if (buffer.remaining() >= value.remaining()) { + buffer.put(value) + } else { + val oldLimit = value.limit() + value.limit(value.position() + buffer.remaining()) + buffer.put(value) + value.limit(oldLimit) + } } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/CoroutineChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/CoroutineChannel.kt similarity index 63% rename from app/src/main/java/de/kuschku/quasseldroid/util/CoroutineChannel.kt rename to app/src/main/java/de/kuschku/quasseldroid/protocol/io/CoroutineChannel.kt index fe3bd2b67a6ec431067a175619971bbb92e66ae9..156c6ec4ae04ba051270c4f8bd07d2401c71dcfc 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/CoroutineChannel.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/CoroutineChannel.kt @@ -17,23 +17,47 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid.util +package de.kuschku.quasseldroid.protocol.io -import de.kuschku.quasseldroid.protocol.ChainedByteBuffer +import de.kuschku.quasseldroid.util.TlsInfo +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.runInterruptible import java.net.InetSocketAddress import java.net.Socket import java.nio.ByteBuffer import java.util.concurrent.Executors +import javax.net.ssl.SSLContext class CoroutineChannel { private lateinit var channel: StreamChannel private val writeContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher() private val readContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher() + private val _tlsInfo = MutableStateFlow<TlsInfo?>(null) + val tlsInfo: StateFlow<TlsInfo?> get() = _tlsInfo - suspend fun connect(address: InetSocketAddress) = runInterruptible(writeContext) { - this.channel = StreamChannel(Socket(address.address, address.port)) + suspend fun connect( + address: InetSocketAddress, + timeout: Int = 0, + keepAlive: Boolean = false, + ) = runInterruptible(Dispatchers.IO) { + val socket = Socket() + socket.keepAlive = keepAlive + socket.connect(address, timeout) + this.channel = StreamChannel(socket) + } + + fun enableCompression() { + channel = channel.withCompression() + } + + suspend fun enableTLS(context: SSLContext) { + channel = runInterruptible(writeContext) { + channel.withTLS(context) + } + _tlsInfo.emit(channel.tlsInfo()) } suspend fun read(buffer: ByteBuffer): Int = runInterruptible(readContext) { diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/ShortSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/FixedDeflaterOutputStream.kt similarity index 70% rename from app/src/main/java/de/kuschku/quasseldroid/protocol/ShortSerializer.kt rename to app/src/main/java/de/kuschku/quasseldroid/protocol/io/FixedDeflaterOutputStream.kt index a7cd95cb2efb3f89ad315978454160f99b5a77d7..abdad9f25eddc40e639b1e0d094cf674f544cf4f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/protocol/ShortSerializer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/FixedDeflaterOutputStream.kt @@ -17,16 +17,19 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid.protocol +package de.kuschku.quasseldroid.protocol.io -import java.nio.ByteBuffer +import java.io.OutputStream +import java.util.zip.DeflaterOutputStream -object ShortSerializer : Serializer<Short> { - override fun serialize(buffer: ChainedByteBuffer, data: Short) { - buffer.putShort(data) - } - - override fun deserialize(buffer: ByteBuffer): Short { - return buffer.getShort() +class FixedDeflaterOutputStream( + stream: OutputStream +) : DeflaterOutputStream(stream, true) { + override fun close() { + try { + super.close() + } finally { + def.end() + } } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ReadableWrappedChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/ReadableWrappedChannel.kt similarity index 98% rename from app/src/main/java/de/kuschku/quasseldroid/util/ReadableWrappedChannel.kt rename to app/src/main/java/de/kuschku/quasseldroid/protocol/io/ReadableWrappedChannel.kt index 6bb7c3127d629519e0fda2ab855f0127dda8155a..ea399d6e80fe15194cac9d9ba81413d704a029ac 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/ReadableWrappedChannel.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/ReadableWrappedChannel.kt @@ -17,7 +17,7 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid.util +package de.kuschku.quasseldroid.protocol.io import android.util.Log import java.io.InputStream diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/StreamChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/StreamChannel.kt similarity index 72% rename from app/src/main/java/de/kuschku/quasseldroid/util/StreamChannel.kt rename to app/src/main/java/de/kuschku/quasseldroid/protocol/io/StreamChannel.kt index c744310e31c2e08bef2162ed3e04a50d88a584e2..28d2697ea1cd7ceca71eebc54f9fda25e72afb5e 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/StreamChannel.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/StreamChannel.kt @@ -17,8 +17,9 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid.util +package de.kuschku.quasseldroid.protocol.io +import de.kuschku.quasseldroid.util.TlsInfo import java.io.Flushable import java.io.InputStream import java.io.OutputStream @@ -26,8 +27,9 @@ import java.net.Socket import java.nio.ByteBuffer import java.nio.channels.ByteChannel import java.nio.channels.InterruptibleChannel -import java.util.zip.DeflaterOutputStream import java.util.zip.InflaterInputStream +import javax.net.ssl.SSLContext +import javax.net.ssl.SSLSocket class StreamChannel constructor( private val socket: Socket, @@ -37,14 +39,33 @@ class StreamChannel constructor( private val input = ReadableWrappedChannel(inputStream) private val output = WritableWrappedChannel(outputStream) + fun tlsInfo(): TlsInfo? { + val sslSocket = socket as? SSLSocket ?: return null + return TlsInfo.ofSession(sslSocket.session) + } + fun withCompression(): StreamChannel { return StreamChannel( socket, InflaterInputStream(inputStream), - DeflaterOutputStream(outputStream), + FixedDeflaterOutputStream(outputStream), ) } + 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) + } + override fun close() { input.close() output.close() diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/io/StringEncoder.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/StringEncoder.kt new file mode 100644 index 0000000000000000000000000000000000000000..694d2291b2e9b38104292f60be6d489cfecb6da4 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/StringEncoder.kt @@ -0,0 +1,88 @@ +/* + * Quasseldroid 1 Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol.io + +import java.nio.ByteBuffer +import java.nio.CharBuffer +import java.nio.charset.Charset +import java.nio.charset.CoderResult + +class StringEncoder(charset: Charset) { + private val encoder = charset.newEncoder() + private val decoder = charset.newDecoder() + private val charBuffer = CharBuffer.allocate(1024) + + private fun charBuffer(length: Int): CharBuffer = + if (length < 1024) charBuffer + else CharBuffer.allocate(length) + + private fun encodingLength(length: Int, nullLimited: Boolean) = + if (nullLimited) length + 1 + else length + + private fun decodingLength(length: Int, nullLimited: Boolean) = + if (nullLimited) length - 1 + else length + + fun encode(data: String?, target: ChainedByteBuffer, nullLimited: Boolean = false) { + if (data == null) return + + val charBuffer = charBuffer(encodingLength(data.length, nullLimited)) + charBuffer.put(data) + if (nullLimited) charBuffer.put(0.toChar()) + charBuffer.flip() + encoder.reset() + var result: CoderResult + do { + result = encoder.encode(charBuffer, target.nextBuffer(data.length), true) + } while (result == CoderResult.OVERFLOW) + } + + fun encode(data: String?, nullLimited: Boolean = false): ByteBuffer { + if (data == null) return ByteBuffer.allocate(0) + + val charBuffer = charBuffer(encodingLength(data.length, nullLimited)) + charBuffer.put(data) + if (nullLimited) charBuffer.put(0.toChar()) + charBuffer.flip() + encoder.reset() + return encoder.encode(charBuffer) + } + + fun decode(source: ByteBuffer, length: Int, nullLimited: Boolean = false): String { + val charBuffer = charBuffer(decodingLength(length, nullLimited)) + val oldlimit = source.limit() + source.limit(decodingLength(source.position() + length, nullLimited)) + decoder.reset() + decoder.decode(source, charBuffer, true) + source.limit(oldlimit) + charBuffer.flip() + return charBuffer.toString() + } + + fun decode(source: ByteBuffer, nullLimited: Boolean = false): String { + val charBuffer = charBuffer(decodingLength(source.remaining(), nullLimited)) + source.limit(decodingLength(source.capacity(), nullLimited)) + decoder.reset() + decoder.decode(source, charBuffer, true) + charBuffer.flip() + return charBuffer.toString() + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/io/StringEncoders.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/StringEncoders.kt new file mode 100644 index 0000000000000000000000000000000000000000..f32172bc8b410f8527719e6751244c29baa0e166 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/StringEncoders.kt @@ -0,0 +1,30 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol.io + +import kotlin.concurrent.getOrSet + +private val ascii = ThreadLocal<StringEncoder>() +private val utf8 = ThreadLocal<StringEncoder>() +private val utf16 = ThreadLocal<StringEncoder>() + +fun stringEncoderAscii() = ascii.getOrSet { StringEncoder(Charsets.ISO_8859_1) } +fun stringEncoderUtf8() = utf8.getOrSet { StringEncoder(Charsets.UTF_8) } +fun stringEncoderUtf16() = utf16.getOrSet { StringEncoder(Charsets.UTF_16BE) } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/WritableWrappedChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/WritableWrappedChannel.kt similarity index 98% rename from app/src/main/java/de/kuschku/quasseldroid/util/WritableWrappedChannel.kt rename to app/src/main/java/de/kuschku/quasseldroid/protocol/io/WritableWrappedChannel.kt index ef0b3fa3c7437c807519f3ba65f9dc8e3b7cb7d0..9e7fe390c9bb90ee60208b41443f3b77af0b30a4 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/WritableWrappedChannel.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/io/WritableWrappedChannel.kt @@ -17,7 +17,7 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid.util +package de.kuschku.quasseldroid.protocol.io import java.io.OutputStream import java.nio.ByteBuffer diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/NoSerializerForTypeException.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/NoSerializerForTypeException.kt new file mode 100644 index 0000000000000000000000000000000000000000..0cf1b79f732f306aab857b16a0106c6bae97aa04 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/NoSerializerForTypeException.kt @@ -0,0 +1,42 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol.serializers + +import de.kuschku.quasseldroid.protocol.variant.QtType +import de.kuschku.quasseldroid.protocol.variant.QuasselType + +class NoSerializerForTypeException( + private val javaType: Class<*>?, + private val qtType: Int, + private val quasselType: String?, +) : Exception() { + constructor(quasselType: QuasselType, javaType: Class<*>? = null) : + this(javaType, quasselType.qtType.id, quasselType.typeName) + + constructor(qtType: QtType, javaType: Class<*>? = null) : + this(javaType, qtType.id, null) + + constructor(qtType: Int, quasselType: String?) : + this(null, qtType, quasselType) + + override fun toString(): String { + return "NoSerializerForTypeException(javaType=$javaType, qtType=${QtType.of(qtType) ?: qtType}, quasselType=${QuasselType.of(quasselType) ?: quasselType})" + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/Serializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/QtSerializer.kt similarity index 77% rename from app/src/main/java/de/kuschku/quasseldroid/protocol/Serializer.kt rename to app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/QtSerializer.kt index 2d6589765231d87943d96d3de980b42bf320b9d2..1066584e25a8482991193996d2f05fde8bdde787 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/protocol/Serializer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/QtSerializer.kt @@ -17,11 +17,15 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid.protocol +package de.kuschku.quasseldroid.protocol.serializers +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.variant.QtType import java.nio.ByteBuffer -interface Serializer<T> { +interface QtSerializer<T> { + val qtType: QtType + val javaType: Class<out T> fun serialize(buffer: ChainedByteBuffer, data: T) fun deserialize(buffer: ByteBuffer): T } diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/ByteSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/QuasselSerializer.kt similarity index 70% rename from app/src/main/java/de/kuschku/quasseldroid/protocol/ByteSerializer.kt rename to app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/QuasselSerializer.kt index d11a523a09cbcb2aa563ca2b01ec7c546d0bc0f6..651497420a862f00835262267b6800a046ae0cb3 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/protocol/ByteSerializer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/QuasselSerializer.kt @@ -17,16 +17,12 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid.protocol +package de.kuschku.quasseldroid.protocol.serializers -import java.nio.ByteBuffer +import de.kuschku.quasseldroid.protocol.variant.QtType +import de.kuschku.quasseldroid.protocol.variant.QuasselType -object ByteSerializer : Serializer<Byte> { - override fun serialize(buffer: ChainedByteBuffer, data: Byte) { - buffer.put(data) - } - - override fun deserialize(buffer: ByteBuffer): Byte { - return buffer.get() - } +interface QuasselSerializer<T> : QtSerializer<T> { + override val qtType: QtType get() = quasselType.qtType + val quasselType: QuasselType } diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/Serializers.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/Serializers.kt new file mode 100644 index 0000000000000000000000000000000000000000..db203e6b367c1741f8de2f061aa8d189bc60be45 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/Serializers.kt @@ -0,0 +1,70 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol.serializers + +import de.kuschku.quasseldroid.protocol.serializers.primitive.* +import de.kuschku.quasseldroid.protocol.variant.QtType +import de.kuschku.quasseldroid.protocol.variant.QuasselType +import java.util.* + +object Serializers { + private val qtSerializers = listOf<QtSerializer<*>>( + BoolSerializer, + ByteSerializer, + IntSerializer, + LongSerializer, + ShortSerializer, + UByteSerializer, + UIntSerializer, + ULongSerializer, + UShortSerializer, + StringSerializerUtf16, + VariantSerializer, + VariantMapSerializer, + ).associateBy(QtSerializer<*>::qtType) + + private val quasselSerializers = listOf<QuasselSerializer<*>>( + ).associateBy(QuasselSerializer<*>::quasselType) + + operator fun get(type: QtType) = qtSerializers[type] + operator fun get(type: QuasselType) = quasselSerializers[type] +} + +@Suppress("UNCHECKED_CAST") +inline fun <reified T> serializerFor(type: QtType): QtSerializer<T> { + val serializer = Serializers[type] + ?: throw NoSerializerForTypeException(type, T::class.java) + if (serializer.javaType == T::class.java) { + return serializer as QtSerializer<T> + } else { + throw NoSerializerForTypeException(type, T::class.java) + } +} + +@Suppress("UNCHECKED_CAST") +inline fun <reified T> serializerFor(type: QuasselType): QuasselSerializer<T> { + val serializer = Serializers[type] + ?: throw NoSerializerForTypeException(type, T::class.java) + if (serializer.javaType == T::class.java) { + return serializer as QuasselSerializer<T> + } else { + throw NoSerializerForTypeException(type, T::class.java) + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/BoolSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/BoolSerializer.kt similarity index 71% rename from app/src/main/java/de/kuschku/quasseldroid/protocol/BoolSerializer.kt rename to app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/BoolSerializer.kt index ff6758462fd83aafbf74fadf0d91daa7da8fee96..30af9a947325b4630803e2ae2dad86bc840d39d2 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/protocol/BoolSerializer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/BoolSerializer.kt @@ -17,11 +17,17 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid.protocol +package de.kuschku.quasseldroid.protocol.serializers.primitive +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.serializers.QtSerializer +import de.kuschku.quasseldroid.protocol.variant.QtType import java.nio.ByteBuffer -object BoolSerializer : Serializer<Boolean> { +object BoolSerializer : QtSerializer<Boolean> { + override val qtType: QtType = QtType.Bool + override val javaType: Class<Boolean> = Boolean::class.java + override fun serialize(buffer: ChainedByteBuffer, data: Boolean) { buffer.put( if (data) 0x01.toByte() diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/ByteSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/ByteSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..7235b80c2d927b25e80c6a955fa99fcff97375b0 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/ByteSerializer.kt @@ -0,0 +1,38 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol.serializers.primitive + +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.serializers.QtSerializer +import de.kuschku.quasseldroid.protocol.variant.QtType +import java.nio.ByteBuffer + +object ByteSerializer : QtSerializer<Byte> { + override val qtType: QtType = QtType.Char + override val javaType: Class<Byte> = Byte::class.java + + override fun serialize(buffer: ChainedByteBuffer, data: Byte) { + buffer.put(data ?: 0) + } + + override fun deserialize(buffer: ByteBuffer): Byte { + return buffer.get() + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/IntSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/IntSerializer.kt similarity index 68% rename from app/src/main/java/de/kuschku/quasseldroid/protocol/IntSerializer.kt rename to app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/IntSerializer.kt index c414476e5b3eea866ba057e800d5e97bba04540e..9962005954a98e7beb77a122fe500d61f1822bef 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/protocol/IntSerializer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/IntSerializer.kt @@ -17,13 +17,19 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid.protocol +package de.kuschku.quasseldroid.protocol.serializers.primitive +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.serializers.QtSerializer +import de.kuschku.quasseldroid.protocol.variant.QtType import java.nio.ByteBuffer -object IntSerializer : Serializer<Int> { +object IntSerializer : QtSerializer<Int> { + override val qtType: QtType = QtType.Int + override val javaType: Class<Int> = Int::class.java + override fun serialize(buffer: ChainedByteBuffer, data: Int) { - buffer.putInt(data) + buffer.putInt(data ?: 0) } override fun deserialize(buffer: ByteBuffer): Int { diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/LongSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/LongSerializer.kt similarity index 68% rename from app/src/main/java/de/kuschku/quasseldroid/protocol/LongSerializer.kt rename to app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/LongSerializer.kt index 334218595faf6a3a49a383715856300dccf896dc..31f09503163b0c0cd682ceb41220092a79febbaf 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/protocol/LongSerializer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/LongSerializer.kt @@ -17,13 +17,19 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid.protocol +package de.kuschku.quasseldroid.protocol.serializers.primitive +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.serializers.QtSerializer +import de.kuschku.quasseldroid.protocol.variant.QtType import java.nio.ByteBuffer -object LongSerializer : Serializer<Long> { +object LongSerializer : QtSerializer<Long> { + override val qtType: QtType = QtType.Long + override val javaType: Class<Long> = Long::class.java + override fun serialize(buffer: ChainedByteBuffer, data: Long) { - buffer.putLong(data) + buffer.putLong(data?: 0) } override fun deserialize(buffer: ByteBuffer): Long { diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/ProtocolInfoSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/ProtocolInfoSerializer.kt similarity index 75% rename from app/src/main/java/de/kuschku/quasseldroid/protocol/ProtocolInfoSerializer.kt rename to app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/ProtocolInfoSerializer.kt index e4048406798dba29fca52cd45f28f6823df55e9b..60fe5d2c28a9edf441d24aaddddaecced2fb0835 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/protocol/ProtocolInfoSerializer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/ProtocolInfoSerializer.kt @@ -17,12 +17,18 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid.protocol +package de.kuschku.quasseldroid.protocol.serializers.primitive import de.kuschku.quasseldroid.ProtocolInfo +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.serializers.QtSerializer +import de.kuschku.quasseldroid.protocol.variant.QtType import java.nio.ByteBuffer -object ProtocolInfoSerializer : Serializer<ProtocolInfo> { +object ProtocolInfoSerializer : QtSerializer<ProtocolInfo> { + override val qtType: QtType = QtType.UserType + override val javaType: Class<ProtocolInfo> = ProtocolInfo::class.java + override fun serialize(buffer: ChainedByteBuffer, data: ProtocolInfo) { UByteSerializer.serialize(buffer, data.flags) UShortSerializer.serialize(buffer, data.data) diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/ShortSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/ShortSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..aeb0e5dd52663cf5a70d8729486f9aad43584405 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/ShortSerializer.kt @@ -0,0 +1,38 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol.serializers.primitive + +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.serializers.QtSerializer +import de.kuschku.quasseldroid.protocol.variant.QtType +import java.nio.ByteBuffer + +object ShortSerializer : QtSerializer<Short> { + override val qtType: QtType = QtType.Short + override val javaType: Class<Short> = Short::class.java + + override fun serialize(buffer: ChainedByteBuffer, data: Short) { + buffer.putShort(data) + } + + override fun deserialize(buffer: ByteBuffer): Short { + return buffer.getShort() + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/StringSerializerAscii.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/StringSerializerAscii.kt new file mode 100644 index 0000000000000000000000000000000000000000..3f2d326d051869afbd4a6829038f3723544a19d0 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/StringSerializerAscii.kt @@ -0,0 +1,50 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol.serializers.primitive + +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.io.stringEncoderAscii +import de.kuschku.quasseldroid.protocol.serializers.QtSerializer +import de.kuschku.quasseldroid.protocol.variant.QtType +import java.nio.ByteBuffer + +object StringSerializerAscii : QtSerializer<String?> { + override val qtType = QtType.QString + override val javaType: Class<out String> = String::class.java + + override fun serialize(buffer: ChainedByteBuffer, data: String?) { + if (data == null) { + IntSerializer.serialize(buffer, -1) + } else { + val stringBuffer = stringEncoderAscii().encode(data, true) + IntSerializer.serialize(buffer, stringBuffer.remaining()) + buffer.put(stringBuffer) + } + } + + override fun deserialize(buffer: ByteBuffer): String? { + val length = IntSerializer.deserialize(buffer) - 1 + return if (length < 0) { + null + } else { + stringEncoderAscii().decode(buffer, length, true) + } + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/StringSerializerUtf16.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/StringSerializerUtf16.kt new file mode 100644 index 0000000000000000000000000000000000000000..5f11ccafc1e82d3af6af9878121ba0a81f0753ad --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/StringSerializerUtf16.kt @@ -0,0 +1,50 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol.serializers.primitive + +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.io.stringEncoderUtf16 +import de.kuschku.quasseldroid.protocol.serializers.QtSerializer +import de.kuschku.quasseldroid.protocol.variant.QtType +import java.nio.ByteBuffer + +object StringSerializerUtf16 : QtSerializer<String?> { + override val qtType = QtType.QString + override val javaType: Class<out String> = String::class.java + + override fun serialize(buffer: ChainedByteBuffer, data: String?) { + if (data == null) { + IntSerializer.serialize(buffer, -1) + } else { + val stringBuffer = stringEncoderUtf16().encode(data) + IntSerializer.serialize(buffer, stringBuffer.remaining()) + buffer.put(stringBuffer) + } + } + + override fun deserialize(buffer: ByteBuffer): String? { + val length = IntSerializer.deserialize(buffer) + return if (length < 0) { + null + } else { + stringEncoderUtf16().decode(buffer, length) + } + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/StringSerializerUtf8.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/StringSerializerUtf8.kt new file mode 100644 index 0000000000000000000000000000000000000000..c400b6dc8a61c22239dd0bf66fce197a0f875ae8 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/StringSerializerUtf8.kt @@ -0,0 +1,51 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol.serializers.primitive + +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.io.stringEncoderUtf8 +import de.kuschku.quasseldroid.protocol.serializers.QtSerializer +import de.kuschku.quasseldroid.protocol.variant.QtType +import java.nio.ByteBuffer + +object StringSerializerUtf8 : QtSerializer<String?> { + override val qtType = QtType.QString + override val javaType: Class<out String> = String::class.java + + override fun serialize(buffer: ChainedByteBuffer, data: String?) { + if (data == null) { + IntSerializer.serialize(buffer, -1) + } else { + val stringBuffer = stringEncoderUtf8().encode(data) + IntSerializer.serialize(buffer, stringBuffer.remaining()) + buffer.put(stringBuffer) + } + } + + override fun deserialize(buffer: ByteBuffer): String? { + val length = IntSerializer.deserialize(buffer) + return if (length < 0) { + null + } else { + stringEncoderUtf8().decode(buffer, length) + } + } +} + diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/UByteSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/UByteSerializer.kt similarity index 70% rename from app/src/main/java/de/kuschku/quasseldroid/protocol/UByteSerializer.kt rename to app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/UByteSerializer.kt index a288c1ebe4bef01c75e33d77c826fa37373241bd..221a4b528ad56743a893d2671bcc3cea7c1c966f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/protocol/UByteSerializer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/UByteSerializer.kt @@ -17,11 +17,17 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid.protocol +package de.kuschku.quasseldroid.protocol.serializers.primitive +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.serializers.QtSerializer +import de.kuschku.quasseldroid.protocol.variant.QtType import java.nio.ByteBuffer -object UByteSerializer : Serializer<UByte> { +object UByteSerializer : QtSerializer<UByte> { + override val qtType: QtType = QtType.UChar + override val javaType: Class<UByte> = UByte::class.java + override fun serialize(buffer: ChainedByteBuffer, data: UByte) { buffer.put(data.toByte()) } diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/UIntSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/UIntSerializer.kt similarity index 71% rename from app/src/main/java/de/kuschku/quasseldroid/protocol/UIntSerializer.kt rename to app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/UIntSerializer.kt index 535a6cf3ca800c4fae54d47b4dbf64144ff02d27..c416039a94270615d1a33928bf17c0e447fd0add 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/protocol/UIntSerializer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/UIntSerializer.kt @@ -17,11 +17,17 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid.protocol +package de.kuschku.quasseldroid.protocol.serializers.primitive +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.serializers.QtSerializer +import de.kuschku.quasseldroid.protocol.variant.QtType import java.nio.ByteBuffer -object UIntSerializer : Serializer<UInt> { +object UIntSerializer : QtSerializer<UInt> { + override val qtType: QtType = QtType.UInt + override val javaType: Class<UInt> = UInt::class.java + override fun serialize(buffer: ChainedByteBuffer, data: UInt) { buffer.putInt(data.toInt()) } diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/ULongSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/ULongSerializer.kt similarity index 70% rename from app/src/main/java/de/kuschku/quasseldroid/protocol/ULongSerializer.kt rename to app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/ULongSerializer.kt index 188a41874123002527e285a2f6bb79d6035e70a3..1bf483839d03997bade315ee4272dc10e0e75db7 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/protocol/ULongSerializer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/ULongSerializer.kt @@ -17,11 +17,17 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid.protocol +package de.kuschku.quasseldroid.protocol.serializers.primitive +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.serializers.QtSerializer +import de.kuschku.quasseldroid.protocol.variant.QtType import java.nio.ByteBuffer -object ULongSerializer : Serializer<ULong> { +object ULongSerializer : QtSerializer<ULong> { + override val qtType: QtType = QtType.ULong + override val javaType: Class<ULong> = ULong::class.java + override fun serialize(buffer: ChainedByteBuffer, data: ULong) { buffer.putLong(data.toLong()) } diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/UShortSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/UShortSerializer.kt similarity index 70% rename from app/src/main/java/de/kuschku/quasseldroid/protocol/UShortSerializer.kt rename to app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/UShortSerializer.kt index da78b5a5e66558ed69c3e85ac5212567e602df53..330da157bf16ef6798694fa40a5fb0553d64204b 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/protocol/UShortSerializer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/UShortSerializer.kt @@ -17,11 +17,17 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid.protocol +package de.kuschku.quasseldroid.protocol.serializers.primitive +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.serializers.QtSerializer +import de.kuschku.quasseldroid.protocol.variant.QtType import java.nio.ByteBuffer -object UShortSerializer : Serializer<UShort> { +object UShortSerializer : QtSerializer<UShort> { + override val qtType: QtType = QtType.UShort + override val javaType: Class<UShort> = UShort::class.java + override fun serialize(buffer: ChainedByteBuffer, data: UShort) { buffer.putShort(data.toShort()) } diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/VariantMapSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/VariantMapSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..74cc2382bac3877ddb6c27f99fc99807249f35d3 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/VariantMapSerializer.kt @@ -0,0 +1,50 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol.serializers.primitive + +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.serializers.QtSerializer +import de.kuschku.quasseldroid.protocol.variant.QVariantMap +import de.kuschku.quasseldroid.protocol.variant.QVariant_ +import de.kuschku.quasseldroid.protocol.variant.QtType +import java.nio.ByteBuffer + +object VariantMapSerializer : QtSerializer<QVariantMap> { + override val qtType = QtType.QVariantMap + @Suppress("UNCHECKED_CAST") + override val javaType: Class<out QVariantMap> = Map::class.java as Class<QVariantMap> + + override fun serialize(buffer: ChainedByteBuffer, data: QVariantMap) { + IntSerializer.serialize(buffer, data.size) + data.entries.forEach { (key, value) -> + StringSerializerUtf16.serialize(buffer, key) + VariantSerializer.serialize(buffer, value) + } + } + + override fun deserialize(buffer: ByteBuffer): QVariantMap { + val result = mutableMapOf<String, QVariant_>() + val length = IntSerializer.deserialize(buffer) + for (i in 0 until length) { + result[StringSerializerUtf16.deserialize(buffer) ?: ""] = VariantSerializer.deserialize(buffer) + } + return result + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/VariantSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/VariantSerializer.kt new file mode 100644 index 0000000000000000000000000000000000000000..7c4f1841457930497e53991cb38f8938323b1908 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/serializers/primitive/VariantSerializer.kt @@ -0,0 +1,75 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol.serializers.primitive + +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.serializers.* +import de.kuschku.quasseldroid.protocol.variant.QVariant +import de.kuschku.quasseldroid.protocol.variant.QVariant_ +import de.kuschku.quasseldroid.protocol.variant.QtType +import de.kuschku.quasseldroid.protocol.variant.QuasselType +import java.nio.ByteBuffer + +object VariantSerializer : QtSerializer<QVariant_> { + override val qtType = QtType.QVariant + override val javaType: Class<QVariant_> = QVariant::class.java + + override fun serialize(buffer: ChainedByteBuffer, data: QVariant_) { + IntSerializer.serialize(buffer, data.serializer.qtType.id) + BoolSerializer.serialize(buffer, false) + if (data is QVariant.Custom && data.serializer.qtType == QtType.UserType) { + StringSerializerAscii.serialize(buffer, data.serializer.quasselType.typeName) + } + data.serialize(buffer) + } + + override fun deserialize(buffer: ByteBuffer): QVariant_ { + val rawType = IntSerializer.deserialize(buffer) + val qtType = QtType.of(rawType) + ?: throw NoSerializerForTypeException(rawType, null) + // isNull, but we ignore it as it has no meaning + BoolSerializer.deserialize(buffer) + + return if (qtType == QtType.UserType) { + val name = StringSerializerAscii.deserialize(buffer) + val quasselType = QuasselType.of(name) + ?: throw NoSerializerForTypeException(qtType.id, name) + deserialize(quasselType, buffer) + } else { + deserialize(qtType, buffer) + } + } + + @Suppress("UNCHECKED_CAST") + private fun deserialize(type: QtType, buffer: ByteBuffer): QVariant_ { + val serializer = Serializers[type] + ?: throw NoSerializerForTypeException(type) + val value = serializer.deserialize(buffer) + return QVariant.of(value, serializer as QuasselSerializer<Any?>) + } + + @Suppress("UNCHECKED_CAST") + private fun deserialize(type: QuasselType, buffer: ByteBuffer): QVariant_ { + val serializer = Serializers[type] + ?: throw NoSerializerForTypeException(type) + val value = serializer.deserialize(buffer) + return QVariant.of(value, serializer as QuasselSerializer<Any?>) + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/variant/QVariant.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/variant/QVariant.kt new file mode 100644 index 0000000000000000000000000000000000000000..77f58ce592834a1daaf0b8fc7a12634dbed8f31c --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/variant/QVariant.kt @@ -0,0 +1,99 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol.variant + +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.serializers.QtSerializer +import de.kuschku.quasseldroid.protocol.serializers.QuasselSerializer +import de.kuschku.quasseldroid.protocol.serializers.primitive.IntSerializer +import de.kuschku.quasseldroid.protocol.serializers.serializerFor + +typealias QVariant_ = QVariant<*> +typealias QVariantList = List<QVariant_> +typealias QVariantMap = Map<String, QVariant_> + +sealed class QVariant<T> constructor( + val data: T, + open val serializer: QtSerializer<T>, +) { + class Typed<T> internal constructor(data: T, serializer: QtSerializer<T>) : + QVariant<T>(data, serializer) { + override fun toString() = "QVariant.Typed(${serializer.qtType.serializableName}, $data)" + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Typed<*>) return false + + if (data != other.data) return false + if (serializer.qtType != other.serializer.qtType) return false + + return true + } + + override fun hashCode(): Int { + var result = data?.hashCode() ?: 0 + result = 31 * result + serializer.qtType.hashCode() + return result + } + } + + class Custom<T> internal constructor(data: T, override val serializer: QuasselSerializer<T>) : + QVariant<T>(data, serializer) { + override fun toString() = "QVariant.Custom(${serializer.quasselType}, $data)" + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Custom<*>) return false + + if (data != other.data) return false + if (serializer.quasselType != other.serializer.quasselType) return false + + return true + } + + override fun hashCode(): Int { + var result = data?.hashCode() ?: 0 + result = 31 * result + serializer.quasselType.hashCode() + return result + } + } + + fun serialize(buffer: ChainedByteBuffer) { + serializer.serialize(buffer, data) + } + + fun or(defValue: T): T { + return data ?: defValue + } + + companion object { + fun <T> of(data: T, serializer: QtSerializer<T>) = Typed(data, serializer) + fun <T> of(data: T, serializer: QuasselSerializer<T>) = Custom(data, serializer) + } +} + +inline fun <reified T> of(data: T, type: QtType): QVariant<T> = + QVariant.of(data, serializerFor(type)) + +inline fun <reified T> of(data: T, type: QuasselType): QVariant<T> = + QVariant.of(data, serializerFor(type)) + +@Suppress("UNCHECKED_CAST") +inline fun <reified T> QVariant_.into(): QVariant<T>? = + if (this.serializer.javaType == T::class.java) this as QVariant<T> + else null diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/variant/QtType.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/variant/QtType.kt new file mode 100644 index 0000000000000000000000000000000000000000..f80dc2ca8c61e86d1e9f1c6d449708261bef5866 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/variant/QtType.kt @@ -0,0 +1,119 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol.variant + +import java.util.* + +enum class QtType(val id: kotlin.Int) { + Void(0),//, VoidSerializer), + Bool(1),//, BoolSerializer), + Int(2),//, IntSerializer), + UInt(3),//, UIntSerializer), + LongLong(4), + ULongLong(5), + + Double(6), + QChar(7),//, CharSerializer), + QVariantMap(8),//, VariantMapSerializer), + QVariantList(9),//, VariantListSerializer), + + QString(10),//, StringSerializer.UTF16), + QStringList(11),//, StringListSerializer), + QByteArray(12),//, ByteArraySerializer), + + QBitArray(13), + QDate(14), + QTime(15),//, TimeSerializer), + QDateTime(16),//, DateTimeSerializer), + QUrl(17), + + QLocale(18), + QRect(19), + QRectF(20), + QSize(21), + QSizeF(22), + + QLine(23), + QLineF(24), + QPoint(25), + QPointF(26), + QRegExp(27), + + QVariantHash(28), + QEasingCurve(29), + + FirstGuiType(63), + + QFont(64), + QPixmap(65), + QBrush(66), + QColor(67), + QPalette(68), + + QIcon(69), + QImage(70), + QPolygon(71), + QRegion(72), + QBitmap(73), + + QCursor(74), + QSizePolicy(75), + QKeySequence(76), + QPen(77), + + QTextLength(78), + QTextFormat(79), + QMatrix(80), + QTransform(81), + + QMatrix4x4(82), + QVector2D(83), + QVector3D(84), + QVector4D(85), + + QQuaternion(86), + + VoidStar(128), + Long(129),//, LongSerializer), + Short(130),//, ShortSerializer), + Char(131),//, ByteSerializer), + ULong(132),//, ULongSerializer), + + UShort(133),//, UShortSerializer), + UChar(134),//, UByteSerializer), + Float(135), + QObjectStar(136), + QWidgetStar(137), + + QVariant(138),//, VariantSerializer), + + User(256), + UserType(127), + LastType(-1); + + val serializableName = + if (name.startsWith("Q")) name + else name.toLowerCase(Locale.ENGLISH) + + companion object { + private val values = values().associateBy(QtType::id) + fun of(id: kotlin.Int): QtType? = values[id] + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/variant/QuasselType.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/variant/QuasselType.kt new file mode 100644 index 0000000000000000000000000000000000000000..e38ab4ecec2223c4f46f1cadc85022ce248c04a9 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/variant/QuasselType.kt @@ -0,0 +1,46 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.protocol.variant + +enum class QuasselType( + val typeName: String, + val qtType: QtType = QtType.UserType, +) { + BufferId("BufferId"), + BufferInfo("BufferInfo"), + DccConfigIpDetectionMode("DccConfig::IpDetectionMode"), + DccConfigPortSelectionMode("DccConfig::PortSelectionMode"), + IrcUser("IrcUser"), + IrcChannel("IrcChannel"), + Identity("Identity"), + IdentityId("IdentityId"), + Message("Message"), + MsgId("MsgId"), + NetworkId("NetworkId"), + NetworkInfo("NetworkInfo"), + NetworkServer("Network::Server"), + QHostAddress("QHostAddress"), + PeerPtr("PeerPtr"); + + companion object { + private val values = values().associateBy(QuasselType::typeName) + fun of(typeName: String?): QuasselType? = values[typeName] + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/TlsInfo.kt b/app/src/main/java/de/kuschku/quasseldroid/util/TlsInfo.kt new file mode 100644 index 0000000000000000000000000000000000000000..3b070eb3b0981d7d4a977e83a993eae660d73fe4 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/TlsInfo.kt @@ -0,0 +1,71 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.util + +import java.security.cert.X509Certificate +import javax.net.ssl.SSLSession + +data class TlsInfo( + val protocol: String, + val cipherSuite: String, + val keyExchangeMechanism: String?, + val certificateChain: List<X509Certificate>, +) { + + override fun toString(): String { + return "TlsInfo(protocol='$protocol', cipherSuite='$cipherSuite', keyExchangeMechanism=$keyExchangeMechanism)" + } + + companion object { + private val cipherSuiteRegex13 = "TLS_(.*)".toRegex() + private val cipherSuiteRegex12 = "TLS_(.*)_WITH_(.*)".toRegex() + + private fun cipherSuiteRegex(protocol: String): Regex = + if (protocol == "TLSv1.3") cipherSuiteRegex13 + else cipherSuiteRegex12 + + private fun parseCipherSuite(protocol: String, cipherSuite: String): Pair<String, String?>? { + val match = cipherSuiteRegex(protocol) + .matchEntire(cipherSuite) + ?: return null + + return if (protocol == "TLSv1.3") { + Pair(match.groupValues[1], null) + } else { + Pair(match.groupValues[1], match.groupValues.getOrNull(2)) + } + } + + fun ofSession(session: SSLSession): TlsInfo? { + val (cipherSuite, keyExchangeMechanism) = parseCipherSuite( + session.protocol, + session.cipherSuite, + ) ?: return null + + return TlsInfo( + session.protocol, + cipherSuite, + keyExchangeMechanism, + session.peerCertificateChain + .map(javax.security.cert.X509Certificate::toJavaCertificate) + ) + } + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/X509Helper.kt b/app/src/main/java/de/kuschku/quasseldroid/util/X509Helper.kt new file mode 100644 index 0000000000000000000000000000000000000000..0c76f12178f8abaa85a07f2ea8c3cf83d86ca169 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/X509Helper.kt @@ -0,0 +1,34 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.util + +import java.io.ByteArrayInputStream +import java.security.cert.CertificateFactory +import java.security.cert.X509Certificate as javaCertificate +import javax.security.cert.X509Certificate as javaxCertificate + +private val certificateFactory = CertificateFactory.getInstance("X.509") + +fun javaxCertificate.toJavaCertificate(): javaCertificate = + certificateFactory.generateCertificate(ByteArrayInputStream(this.encoded)) + as javaCertificate + +fun javaCertificate.toJavaXCertificate(): javaxCertificate = + javaxCertificate.getInstance(this.encoded) diff --git a/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt b/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt index 4ebc9b8ae91cd877f5dd8ffa27905f971635334e..f5e8fb03f154c988a4c7648469d8209c3cfcca3c 100644 --- a/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt +++ b/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt @@ -1,14 +1,18 @@ package de.kuschku.quasseldroid -import de.kuschku.quasseldroid.protocol.ChainedByteBuffer -import de.kuschku.quasseldroid.protocol.IntSerializer -import de.kuschku.quasseldroid.protocol.UIntSerializer -import de.kuschku.quasseldroid.util.CoroutineChannel +import de.kuschku.quasseldroid.protocol.io.ChainedByteBuffer +import de.kuschku.quasseldroid.protocol.serializers.primitive.IntSerializer +import de.kuschku.quasseldroid.protocol.serializers.primitive.ProtocolInfoSerializer +import de.kuschku.quasseldroid.protocol.serializers.primitive.UIntSerializer +import de.kuschku.quasseldroid.protocol.io.CoroutineChannel import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Test import java.net.InetSocketAddress import java.nio.ByteBuffer +import java.security.cert.X509Certificate +import javax.net.ssl.SSLContext +import javax.net.ssl.X509TrustManager /** * Example local unit test, which will execute on the development machine (host). @@ -23,19 +27,39 @@ class ExampleUnitTest { @Test fun testNetworking() { + val context = SSLContext.getInstance("TLSv1.3") + context.init(null, arrayOf(object : X509TrustManager { + override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) { + // FIXME: accept everything + } + + override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) { + // FIXME: accept everything + } + + override fun getAcceptedIssuers(): Array<X509Certificate> { + // FIXME: accept nothing + return emptyArray() + } + }), null) + runBlocking { val sizeBuffer = ByteBuffer.allocateDirect(4) val sendBuffer = ChainedByteBuffer(direct = true) val channel = CoroutineChannel() channel.connect(InetSocketAddress("kuschku.de", 4242)) val readBuffer = ByteBuffer.allocateDirect(4) - UIntSerializer.serialize(sendBuffer, 0x42b3_3f00u) + UIntSerializer.serialize(sendBuffer, 0x42b3_3f00u or 0x03u) IntSerializer.serialize(sendBuffer, 2) UIntSerializer.serialize(sendBuffer, 0x8000_0000u) channel.write(sendBuffer) channel.read(readBuffer) readBuffer.flip() - println(IntSerializer.deserialize(readBuffer)) + println(ProtocolInfoSerializer.deserialize(readBuffer)) + println(channel.tlsInfo.value) + channel.enableTLS(context) + println(channel.tlsInfo.value) + channel.enableCompression() } } }