Commit 6a8e6b93 authored by Him188's avatar Him188

zip support

parent b50f3e4d
...@@ -14,9 +14,9 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.data.RequestPacke ...@@ -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.DefaultLogger
import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.cryptor.adjustToPublicKey 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.cryptor.decryptBy
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.unzip
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract import kotlin.contracts.contract
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
...@@ -126,6 +126,8 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( ...@@ -126,6 +126,8 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
}?.let { }?.let {
// 处理内层真实的包 // 处理内层真实的包
if (it.packetFactory == null) { if (it.packetFactory == null) {
PacketLogger.warning("找不到 PacketFactory")
PacketLogger.verbose("传递给 PacketFactory 的数据 = ${it.data.useBytes { data, length -> data.toUHexString(length = length) }}")
return return
} }
...@@ -147,7 +149,6 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( ...@@ -147,7 +149,6 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
PacketLogger.error("任何key都无法解密: ${data.take(size).toUHexString()}") PacketLogger.error("任何key都无法解密: ${data.take(size).toUHexString()}")
return return
} }
} }
} }
} }
...@@ -183,7 +184,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( ...@@ -183,7 +184,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
private fun parseSsoFrame(bot: QQAndroidBot, input: ByteReadPacket): IncomingPacket { private fun parseSsoFrame(bot: QQAndroidBot, input: ByteReadPacket): IncomingPacket {
val commandName: String val commandName: String
val ssoSequenceId: Int val ssoSequenceId: Int
val dataCompressed: Int
// head // head
input.readIoBuffer(input.readInt() - 4).withUse { input.readIoBuffer(input.readInt() - 4).withUse {
ssoSequenceId = readInt() ssoSequenceId = readInt()
...@@ -196,26 +197,25 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( ...@@ -196,26 +197,25 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
val unknown = readBytes(readInt() - 4) val unknown = readBytes(readInt() - 4)
if (unknown.toInt() != 0x02B05B8B) DebugLogger.debug("got new unknown: ${unknown.toUHexString()}") if (unknown.toInt() != 0x02B05B8B) DebugLogger.debug("got new unknown: ${unknown.toUHexString()}")
readInt().let { dataCompressed = readInt()
if (it != 0) { }
DebugLogger.debug("!! 得到一个原本是 0, 现在是 ${it.contentToString()}")
if (it == 1){ val packet = when (dataCompressed) {
PacketLogger.info("无法处理的数据 = ${input.readBytes().toUHexString()}") 0 -> input
return IncomingPacket(null, ssoSequenceId, input) 1 -> {
} input.discardExact(4)
} input.useBytes { data, length ->
data.unzip(length = length)
}.toReadPacket()
} }
else -> error("unknown dataCompressed flag: $dataCompressed")
} }
// body // body
val packetFactory = findPacketFactory(commandName) val packetFactory = findPacketFactory(commandName)
bot.logger.verbose(commandName) bot.logger.info("Received: $commandName")
if (packetFactory == null) { return IncomingPacket(packetFactory, ssoSequenceId, packet)
bot.logger.warning("找不到包 PacketFactory")
PacketLogger.verbose("传递给 PacketFactory 的数据 = ${input.readBytes().toUHexString()}")
}
return IncomingPacket(packetFactory, ssoSequenceId, input)
} }
private suspend fun <T : Packet> ByteReadPacket.parseOicqResponse( private suspend fun <T : Packet> ByteReadPacket.parseOicqResponse(
...@@ -224,14 +224,13 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( ...@@ -224,14 +224,13 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
ssoSequenceId: Int, ssoSequenceId: Int,
consumer: PacketConsumer<T> consumer: PacketConsumer<T>
) { ) {
val qq: Long
readIoBuffer(readInt() - 4).withUse { readIoBuffer(readInt() - 4).withUse {
check(readByte().toInt() == 2) check(readByte().toInt() == 2)
this.discardExact(2) // 27 + 2 + body.size this.discardExact(2) // 27 + 2 + body.size
this.discardExact(2) // const, =8001 this.discardExact(2) // const, =8001
this.readUShort() // commandId this.readUShort() // commandId
this.readShort() // const, =0x0001 this.readShort() // const, =0x0001
qq = this.readUInt().toLong() this.readUInt().toLong() // qq
val encryptionMethod = this.readUShort().toInt() val encryptionMethod = this.readUShort().toInt()
this.discardExact(1) // const = 0 this.discardExact(1) // const = 0
......
...@@ -6,8 +6,9 @@ import net.mamoe.mirai.data.Packet ...@@ -6,8 +6,9 @@ import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.io.JceStruct import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
@Suppress("ArrayInDataClass")
@Serializable @Serializable
internal class RequestPushNotify( internal data class RequestPushNotify(
@SerialId(0) val uin: Long = 0L, @SerialId(0) val uin: Long = 0L,
@SerialId(1) val ctype: Byte = 0, @SerialId(1) val ctype: Byte = 0,
@SerialId(2) val strService: String?, @SerialId(2) val strService: String?,
......
...@@ -5,6 +5,8 @@ import io.ktor.client.engine.cio.CIO ...@@ -5,6 +5,8 @@ import io.ktor.client.engine.cio.CIO
import io.ktor.util.KtorExperimentalAPI import io.ktor.util.KtorExperimentalAPI
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.io.pool.useInstance
import net.mamoe.mirai.utils.io.ByteArrayPool
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.DataInput import java.io.DataInput
import java.io.EOFException import java.io.EOFException
...@@ -83,17 +85,23 @@ actual fun crc32(key: ByteArray): Int = CRC32().apply { update(key) }.value.toIn ...@@ -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 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() val inflater = Inflater()
inflater.reset() inflater.reset()
val output = ByteArrayOutputStream() ByteArrayOutputStream().use { output ->
inflater.setInput(this) inflater.setInput(this, offset, length)
val buffer = ByteArray(128) ByteArrayPool.useInstance {
while (!inflater.finished()) { while (!inflater.finished()) {
output.write(buffer, 0, inflater.inflate(buffer)) output.write(it, 0, inflater.inflate(it))
}
}
inflater.end()
return output.toByteArray()
} }
inflater.end()
return output.toByteArray()
} }
actual fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher { actual fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher {
......
...@@ -24,7 +24,10 @@ expect val deviceName: String ...@@ -24,7 +24,10 @@ expect val deviceName: String
*/ */
expect fun crc32(key: ByteArray): Int 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 算法 * MD5 算法
...@@ -48,4 +51,10 @@ expect fun localIpAddress(): String ...@@ -48,4 +51,10 @@ expect fun localIpAddress(): String
*/ */
expect val Http: HttpClient expect val Http: HttpClient
expect fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher expect fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher
\ No newline at end of file
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
...@@ -38,6 +38,14 @@ fun ByteReadPacket.transferTo(outputStream: OutputStream) { ...@@ -38,6 +38,14 @@ fun ByteReadPacket.transferTo(outputStream: OutputStream) {
} }
} }
fun <R> 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( fun ByteReadPacket.readRemainingBytes(
n: Int = remaining.toInt()//not that safe but adequate n: Int = remaining.toInt()//not that safe but adequate
): ByteArray = ByteArray(n).also { readAvailable(it, 0, n) } ): ByteArray = ByteArray(n).also { readAvailable(it, 0, n) }
...@@ -79,11 +87,11 @@ private inline fun <R> inline(block: () -> R): R = block() ...@@ -79,11 +87,11 @@ private inline fun <R> inline(block: () -> R): R = block()
typealias TlvMap = MutableMap<Int, ByteArray> typealias TlvMap = MutableMap<Int, ByteArray>
fun TlvMap.getOrFail(tag: Int): ByteArray { 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 { 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) fun Input.readTLVMap(tagSize: Int = 2): TlvMap = readTLVMap(true, tagSize)
......
...@@ -7,8 +7,10 @@ import io.ktor.client.engine.cio.CIO ...@@ -7,8 +7,10 @@ import io.ktor.client.engine.cio.CIO
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.io.core.copyTo import kotlinx.io.core.copyTo
import kotlinx.io.pool.useInstance
import kotlinx.io.streams.asInput import kotlinx.io.streams.asInput
import kotlinx.io.streams.asOutput import kotlinx.io.streams.asOutput
import net.mamoe.mirai.utils.io.ByteArrayPool
import java.io.* import java.io.*
import java.net.InetAddress import java.net.InetAddress
import java.security.MessageDigest import java.security.MessageDigest
...@@ -56,18 +58,23 @@ actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress ...@@ -56,18 +58,23 @@ actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress
actual val Http: HttpClient get() = HttpClient(CIO) 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() val inflater = Inflater()
inflater.reset() inflater.reset()
val input = this ByteArrayOutputStream().use { output ->
val output = ByteArrayOutputStream() inflater.setInput(this, offset, length)
inflater.setInput(input) ByteArrayPool.useInstance {
val buffer = ByteArray(128) while (!inflater.finished()) {
while (!inflater.finished()) { output.write(it, 0, inflater.inflate(it))
output.write(buffer, 0, inflater.inflate(buffer)) }
}
inflater.end()
return output.toByteArray()
} }
inflater.end()
return output.toByteArray()
} }
actual fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher { actual fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment