From 6a8e6b93a070dd35717ad5350ce4fce05ba0c88a Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 29 Jan 2020 19:54:09 +0800 Subject: [PATCH] zip support --- .../network/protocol/packet/PacketFactory.kt | 37 +++++++++---------- .../packet/chat/data/PushNotifyPack.kt | 3 +- .../mamoe/mirai/utils/PlatformUtilsAndroid.kt | 24 ++++++++---- .../net.mamoe.mirai/utils/PlatformUtils.kt | 13 ++++++- .../net.mamoe.mirai/utils/io/InputUtils.kt | 12 +++++- .../net/mamoe/mirai/utils/PlatformUtilsJvm.kt | 25 ++++++++----- 6 files changed, 73 insertions(+), 41 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt index aac2b1078..e95628143 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt @@ -14,9 +14,9 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.data.RequestPacke import net.mamoe.mirai.utils.DefaultLogger import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.cryptor.adjustToPublicKey -import net.mamoe.mirai.utils.cryptor.contentToString import net.mamoe.mirai.utils.cryptor.decryptBy import net.mamoe.mirai.utils.io.* +import net.mamoe.mirai.utils.unzip import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract import kotlin.jvm.JvmName @@ -126,6 +126,8 @@ internal object KnownPacketFactories : List> by mutableListOf( }?.let { // 处理内层真实的包 if (it.packetFactory == null) { + PacketLogger.warning("找不到 PacketFactory") + PacketLogger.verbose("传递给 PacketFactory 的数据 = ${it.data.useBytes { data, length -> data.toUHexString(length = length) }}") return } @@ -147,7 +149,6 @@ internal object KnownPacketFactories : List> by mutableListOf( PacketLogger.error("任何key都无法解密: ${data.take(size).toUHexString()}") return } - } } } @@ -183,7 +184,7 @@ internal object KnownPacketFactories : List> by mutableListOf( private fun parseSsoFrame(bot: QQAndroidBot, input: ByteReadPacket): IncomingPacket { val commandName: String val ssoSequenceId: Int - + val dataCompressed: Int // head input.readIoBuffer(input.readInt() - 4).withUse { ssoSequenceId = readInt() @@ -196,26 +197,25 @@ internal object KnownPacketFactories : List> by mutableListOf( val unknown = readBytes(readInt() - 4) if (unknown.toInt() != 0x02B05B8B) DebugLogger.debug("got new unknown: ${unknown.toUHexString()}") - readInt().let { - if (it != 0) { - DebugLogger.debug("!! 得到一个原本是 0, 现在是 ${it.contentToString()}") - if (it == 1){ - PacketLogger.info("无法处理的数据 = ${input.readBytes().toUHexString()}") - return IncomingPacket(null, ssoSequenceId, input) - } - } + dataCompressed = readInt() + } + + val packet = when (dataCompressed) { + 0 -> input + 1 -> { + input.discardExact(4) + input.useBytes { data, length -> + data.unzip(length = length) + }.toReadPacket() } + else -> error("unknown dataCompressed flag: $dataCompressed") } // body val packetFactory = findPacketFactory(commandName) - bot.logger.verbose(commandName) - if (packetFactory == null) { - bot.logger.warning("找不到包 PacketFactory") - PacketLogger.verbose("传递给 PacketFactory 的数据 = ${input.readBytes().toUHexString()}") - } - return IncomingPacket(packetFactory, ssoSequenceId, input) + bot.logger.info("Received: $commandName") + return IncomingPacket(packetFactory, ssoSequenceId, packet) } private suspend fun ByteReadPacket.parseOicqResponse( @@ -224,14 +224,13 @@ internal object KnownPacketFactories : List> by mutableListOf( ssoSequenceId: Int, consumer: PacketConsumer ) { - val qq: Long readIoBuffer(readInt() - 4).withUse { check(readByte().toInt() == 2) this.discardExact(2) // 27 + 2 + body.size this.discardExact(2) // const, =8001 this.readUShort() // commandId this.readShort() // const, =0x0001 - qq = this.readUInt().toLong() + this.readUInt().toLong() // qq val encryptionMethod = this.readUShort().toInt() this.discardExact(1) // const = 0 diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/data/PushNotifyPack.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/data/PushNotifyPack.kt index 234623085..cea4cdc3b 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/data/PushNotifyPack.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/data/PushNotifyPack.kt @@ -6,8 +6,9 @@ import net.mamoe.mirai.data.Packet import net.mamoe.mirai.qqandroid.io.JceStruct import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY +@Suppress("ArrayInDataClass") @Serializable -internal class RequestPushNotify( +internal data class RequestPushNotify( @SerialId(0) val uin: Long = 0L, @SerialId(1) val ctype: Byte = 0, @SerialId(2) val strService: String?, diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsAndroid.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsAndroid.kt index d2bb58323..1e7649f67 100644 --- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsAndroid.kt +++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsAndroid.kt @@ -5,6 +5,8 @@ import io.ktor.client.engine.cio.CIO import io.ktor.util.KtorExperimentalAPI import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.io.pool.useInstance +import net.mamoe.mirai.utils.io.ByteArrayPool import java.io.ByteArrayOutputStream import java.io.DataInput import java.io.EOFException @@ -83,17 +85,23 @@ actual fun crc32(key: ByteArray): Int = CRC32().apply { update(key) }.value.toIn */ actual fun solveIpAddress(hostname: String): String = InetAddress.getByName(hostname).hostAddress -actual fun ByteArray.unzip(): ByteArray { +actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray { + this.checkOffsetAndLength(offset, length) + if (length == 0) return ByteArray(0) + val inflater = Inflater() inflater.reset() - val output = ByteArrayOutputStream() - inflater.setInput(this) - val buffer = ByteArray(128) - while (!inflater.finished()) { - output.write(buffer, 0, inflater.inflate(buffer)) + ByteArrayOutputStream().use { output -> + inflater.setInput(this, offset, length) + ByteArrayPool.useInstance { + while (!inflater.finished()) { + output.write(it, 0, inflater.inflate(it)) + } + } + + inflater.end() + return output.toByteArray() } - inflater.end() - return output.toByteArray() } actual fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher { diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt index 974ed9d44..ea91627d7 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt @@ -24,7 +24,10 @@ expect val deviceName: String */ expect fun crc32(key: ByteArray): Int -expect fun ByteArray.unzip(): ByteArray +/** + * 解 zip 压缩 + */ +expect fun ByteArray.unzip(offset: Int = 0, length: Int = this.size - offset): ByteArray /** * MD5 算法 @@ -48,4 +51,10 @@ expect fun localIpAddress(): String */ expect val Http: HttpClient -expect fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher \ No newline at end of file +expect fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher + +internal fun ByteArray.checkOffsetAndLength(offset: Int, length: Int){ + require(offset >= 0) { "offset shouldn't be negative: $offset" } + require(length >= 0) { "length shouldn't be negative: $length" } + require(offset + length <= this.size) { "offset ($offset) + length ($length) > array.size (${this.size})" } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt index 23046d883..2c598eead 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt @@ -38,6 +38,14 @@ fun ByteReadPacket.transferTo(outputStream: OutputStream) { } } +fun ByteReadPacket.useBytes( + n: Int = remaining.toInt(),//not that safe but adequate + block: (data: ByteArray, length: Int) -> R +): R = ByteArrayPool.useInstance { + this.readFully(it, 0, n) + block(it, n) +} + fun ByteReadPacket.readRemainingBytes( n: Int = remaining.toInt()//not that safe but adequate ): ByteArray = ByteArray(n).also { readAvailable(it, 0, n) } @@ -79,11 +87,11 @@ private inline fun inline(block: () -> R): R = block() typealias TlvMap = MutableMap fun TlvMap.getOrFail(tag: Int): ByteArray { - return this[tag]?: error("cannot find tlv 0x${tag.toUHexString("")}($tag)") + return this[tag] ?: error("cannot find tlv 0x${tag.toUHexString("")}($tag)") } inline fun TlvMap.getOrFail(tag: Int, lazyMessage: (tag: Int) -> String): ByteArray { - return this[tag]?: error(lazyMessage(tag)) + return this[tag] ?: error(lazyMessage(tag)) } fun Input.readTLVMap(tagSize: Int = 2): TlvMap = readTLVMap(true, tagSize) diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt index 262fdb04c..10c1566cb 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt @@ -7,8 +7,10 @@ import io.ktor.client.engine.cio.CIO import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.io.core.copyTo +import kotlinx.io.pool.useInstance import kotlinx.io.streams.asInput import kotlinx.io.streams.asOutput +import net.mamoe.mirai.utils.io.ByteArrayPool import java.io.* import java.net.InetAddress import java.security.MessageDigest @@ -56,18 +58,23 @@ actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress actual val Http: HttpClient get() = HttpClient(CIO) -actual fun ByteArray.unzip(): ByteArray { +actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray { + this.checkOffsetAndLength(offset, length) + if (length == 0) return ByteArray(0) + val inflater = Inflater() inflater.reset() - val input = this - val output = ByteArrayOutputStream() - inflater.setInput(input) - val buffer = ByteArray(128) - while (!inflater.finished()) { - output.write(buffer, 0, inflater.inflate(buffer)) + ByteArrayOutputStream().use { output -> + inflater.setInput(this, offset, length) + ByteArrayPool.useInstance { + while (!inflater.finished()) { + output.write(it, 0, inflater.inflate(it)) + } + } + + inflater.end() + return output.toByteArray() } - inflater.end() - return output.toByteArray() } actual fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher {