mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-24 06:50:08 +08:00
zip support
This commit is contained in:
parent
b50f3e4d16
commit
6a8e6b93a0
@ -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(
|
|||||||
}?.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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
*/
|
*/
|
||||||
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
|
|||||||
*/
|
*/
|
||||||
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
|
|||||||
*/
|
*/
|
||||||
expect val Http: HttpClient
|
expect val Http: HttpClient
|
||||||
|
|
||||||
expect fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher
|
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})" }
|
||||||
|
}
|
@ -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()
|
|||||||
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
|
|||||||
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
|
|||||||
|
|
||||||
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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user