Commit ed99f8f8 authored by jiahua.liu's avatar jiahua.liu

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/list/FriendListPacket.kt
parents 704e301c b3e9d553
......@@ -25,9 +25,11 @@ enum class JceCharset(val kotlinCharset: Charset) {
UTF8(Charset.forName("UTF8"))
}
internal fun getSerialId(desc: SerialDescriptor, index: Int): Int? = desc.findAnnotation<SerialId>(index)?.id
/**
* Jce 数据结构序列化和反序列化工具, 能将 kotlinx.serialization 通用的注解标记格式的 `class` 序列化为 [ByteArray]
*/
class Jce private constructor(private val charset: JceCharset, context: SerialModule = EmptyModule) : AbstractSerialFormat(context), BinaryFormat {
private inner class ListWriter(
......@@ -152,7 +154,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
this.writeHead(STRUCT_END, 0)
}
} else if (value is ProtoBuf) {
this.encodeTaggedByteArray(popTag(), net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport.dump(value))
this.encodeTaggedByteArray(popTag(), ProtoBufWithNullableSupport.dump(value))
} else {
serializer.serialize(this, value)
}
......@@ -417,7 +419,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
@Suppress("UNCHECKED_CAST")
override fun <T : Any> decodeNullableSerializableValue(deserializer: DeserializationStrategy<T?>): T? {
println("decodeNullableSerializableValue: ${deserializer.getClassName()}")
// println("decodeNullableSerializableValue: ${deserializer.getClassName()}")
if (deserializer is NullReader) {
return null
}
......@@ -444,7 +446,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
else input.readByteArray(tag).toMutableList() as T
}
val tag = popTag()
println(tag)
// println(tag)
@Suppress("SENSELESS_COMPARISON") // false positive
if (input.skipToTagOrNull(tag) {
return deserializer.deserialize(JceListReader(input.readInt(0), input))
......
package net.mamoe.mirai.qqandroid.io.serialization
import kotlinx.serialization.SerialDescriptor
/*
* Helper for kotlinx.serialization
*/
internal inline fun <reified A: Annotation> SerialDescriptor.findAnnotation(elementIndex: Int): A? {
val candidates = getElementAnnotations(elementIndex).filterIsInstance<A>()
return when (candidates.size) {
0 -> null
1 -> candidates[0]
else -> throw IllegalStateException("There are duplicate annotations of type ${A::class} in the descriptor $this")
}
}
......@@ -2,6 +2,7 @@ package net.mamoe.mirai.qqandroid.io.serialization
import kotlinx.io.core.*
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerialDescriptor
import kotlinx.serialization.SerializationStrategy
import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.qqandroid.io.ProtoBuf
......@@ -92,3 +93,26 @@ fun <T : ProtoBuf> ByteArray.loadAs(deserializer: DeserializationStrategy<T>): T
fun <T : ProtoBuf> Input.readRemainingAsProtoBuf(serializer: DeserializationStrategy<T>): T {
return ProtoBufWithNullableSupport.load(serializer, this.readBytes())
}
/**
* 构造 [RequestPacket] 的 [RequestPacket.sBuffer]
*/
fun <T : JceStruct> jceRequestSBuffer(name: String, serializer: SerializationStrategy<T>, jceStruct: T): ByteArray {
return RequestDataVersion3(
mapOf(
name to JCE_STRUCT_HEAD_OF_TAG_0 + jceStruct.toByteArray(serializer) + JCE_STRUCT_TAIL_OF_TAG_0
)
).toByteArray(RequestDataVersion3.serializer())
}
private val JCE_STRUCT_HEAD_OF_TAG_0 = byteArrayOf(0x0A)
private val JCE_STRUCT_TAIL_OF_TAG_0 = byteArrayOf(0x0B)
internal inline fun <reified A : Annotation> SerialDescriptor.findAnnotation(elementIndex: Int): A? {
val candidates = getElementAnnotations(elementIndex).filterIsInstance<A>()
return when (candidates.size) {
0 -> null
1 -> candidates[0]
else -> throw IllegalStateException("There are duplicate annotations of type ${A::class} in the descriptor $this")
}
}
......@@ -18,7 +18,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.KnownPacketFactories
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketLogger
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendListPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket.LoginPacketResponse.*
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc
......@@ -32,6 +31,10 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
override val bot: QQAndroidBot by bot.unsafeWeakRef()
override val supervisor: CompletableJob = SupervisorJob(bot.coroutineContext[Job])
override val coroutineContext: CoroutineContext = bot.coroutineContext + CoroutineExceptionHandler { _, throwable ->
throwable.logStacktrace("Exception in NetworkHandler")
}
private lateinit var channel: PlatformSocket
override suspend fun login() {
......@@ -88,19 +91,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
println("d2key=${bot.client.wLoginSigInfo.d2Key.toUHexString()}")
StatSvc.Register(bot.client).sendAndExpect<StatSvc.Register.Response>()
println("登陆完成 开始尝试获取friendList")
println("登陆完成 开始尝试获取friendList")
println("登陆完成 开始尝试获取friendList")
println("登陆完成 开始尝试获取friendList")
println("登陆完成 开始尝试获取friendList")
FriendListPacket(
bot.client,
0,
20,
0,
0
).sendAndExpect<FriendListPacket.GetFriendListResponse>()
}
/**
......@@ -168,7 +158,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
}
private suspend inline fun <P : Packet> generifiedParsePacket(input: Input) {
try {
KnownPacketFactories.parseIncomingPacket(bot, input) { packetFactory: PacketFactory<P>, packet: P, commandName: String, sequenceId: Int ->
handlePacket(packetFactory, packet, commandName, sequenceId)
if (packet is MultiPacket<*>) {
......@@ -177,10 +166,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
}
}
}
} finally {
println()
println() // separate for debugging
}
}
/**
......@@ -211,7 +196,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
if (packet is Cancellable && packet.cancelled) return
}
bot.logger.info(packet)
bot.logger.info("Received packet: $packet")
packetFactory?.run {
bot.handle(packet)
......@@ -325,17 +310,19 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
/**
* 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms)
*/
suspend fun <E : Packet> OutgoingPacket.sendAndExpect(): E {
suspend fun <E : Packet> OutgoingPacket.sendAndExpect(timoutMillis: Long = 3000): E {
val handler = PacketListener(commandName = commandName, sequenceId = sequenceId)
packetListeners.addLast(handler)
bot.logger.info("Send: ${this.commandName}")
channel.send(delegate)
return withTimeoutOrNull(3000) {
return withTimeoutOrNull(timoutMillis) {
@Suppress("UNCHECKED_CAST")
handler.await() as E
// 不要 `withTimeout`. timeout 的异常会不知道去哪了.
} ?: net.mamoe.mirai.qqandroid.utils.inline {
packetListeners.remove(handler)
error("timeout when sending ${this.commandName}")
error("timeout when sending ${commandName}")
}
}
......@@ -351,6 +338,4 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
}
override suspend fun awaitDisconnection() = supervisor.join()
override val coroutineContext: CoroutineContext = bot.coroutineContext
}
\ No newline at end of file
......@@ -124,6 +124,7 @@ internal open class QQAndroidClient(
@PublishedApi
internal val apkId: ByteArray = "com.tencent.mobileqq".toByteArray()
var outgoingPacketUnknownValue: ByteArray = 0x02B05B8B.toByteArray()
var loginState = 0
var t150: Tlv? = null
......
......@@ -3,8 +3,6 @@ package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable
import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Vec0xd50
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Vec0xd6b
@Serializable
internal class GetFriendListReq(
......
......@@ -23,7 +23,7 @@ class RequestPacket(
@Serializable
class RequestDataVersion3(
@SerialId(0) val map: Map<String, ByteArray>
@SerialId(0) val map: Map<String, ByteArray> // 注意: ByteArray 不能直接放序列化的 JceStruct!! 要放类似 RequestDataStructSvcReqRegister 的
) : JceStruct
@Serializable
......
......@@ -9,6 +9,7 @@ import net.mamoe.mirai.qqandroid.io.serialization.loadAs
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePush
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc
import net.mamoe.mirai.utils.DefaultLogger
......@@ -17,6 +18,7 @@ import net.mamoe.mirai.utils.cryptor.adjustToPublicKey
import net.mamoe.mirai.utils.cryptor.decryptBy
import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.unzip
import net.mamoe.mirai.utils.withSwitch
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
import kotlin.jvm.JvmName
......@@ -53,7 +55,7 @@ internal val DECRYPTER_16_ZERO = ByteArray(16)
internal typealias PacketConsumer<T> = suspend (packetFactory: PacketFactory<T>, packet: T, commandName: String, ssoSequenceId: Int) -> Unit
@PublishedApi
internal val PacketLogger: MiraiLogger = DefaultLogger("Packet")
internal val PacketLogger: MiraiLogger = DefaultLogger("Packet").withSwitch(false)
@UseExperimental(ExperimentalUnsignedTypes::class)
internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
......@@ -63,7 +65,8 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
MessageSvc.PushNotify,
MessageSvc.PbGetMsg,
MessageSvc.PushForceOffline,
MessageSvc.PbSendMsg
MessageSvc.PbSendMsg,
FriendList.GetFriendGroupList
) {
// SvcReqMSFLoginNotify 自己的其他设备上限
// MessageSvc.PushReaded 电脑阅读了别人的消息, 告知手机
......@@ -193,8 +196,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
PacketLogger.verbose("sso(inner)extraData = ${extraData.toUHexString()}")
commandName = readString(readInt() - 4)
val unknown = readBytes(readInt() - 4)
if (unknown.toInt() != 0x02B05B8B) DebugLogger.debug("got new unknown: ${unknown.toUHexString()}")
bot.client.outgoingPacketUnknownValue = readBytes(readInt() - 4)
dataCompressed = readInt()
}
......@@ -213,7 +215,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
// body
val packetFactory = findPacketFactory(commandName)
bot.logger.info("Received: $commandName")
bot.logger.debug("Received commandName: $commandName")
return IncomingPacket(packetFactory, ssoSequenceId, packet)
}
......
......@@ -2,7 +2,6 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import kotlinx.io.core.writeFully
import net.mamoe.mirai.data.MultiPacket
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.message.FriendMessage
......@@ -26,8 +25,6 @@ import net.mamoe.mirai.qqandroid.utils.toRichTextElems
import net.mamoe.mirai.utils.cryptor.contentToString
import net.mamoe.mirai.utils.io.hexToBytes
import net.mamoe.mirai.utils.io.toReadPacket
import kotlin.math.absoluteValue
import kotlin.random.Random
class MessageSvc {
/**
......@@ -145,11 +142,11 @@ class MessageSvc {
richText = ImMsgBody.RichText(
elems = message.toRichTextElems()
)
),
msgSeq = 17041,
msgRand = Random.nextInt().absoluteValue,
syncCookie = client.c2cMessageSync.syncCookie.takeIf { it.isNotEmpty() } ?: "08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00".hexToBytes(),
msgVia = 1
)
// msgSeq = 17041,
// msgRand = Random.nextInt().absoluteValue,
// syncCookie = client.c2cMessageSync.syncCookie.takeIf { it.isNotEmpty() } ?: "08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00".hexToBytes(),
// msgVia = 1
)
)
}
......
......@@ -4,11 +4,9 @@ import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport
import net.mamoe.mirai.qqandroid.io.serialization.toByteArray
import net.mamoe.mirai.qqandroid.io.serialization.jceRequestSBuffer
import net.mamoe.mirai.qqandroid.io.serialization.writeJceStruct
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataStructSvcReqRegister
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataVersion3
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.SvcReqRegister
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
......@@ -61,9 +59,9 @@ class StatSvc {
RequestPacket(
sServantName = "PushService",
sFuncName = "SvcReqRegister",
sBuffer = RequestDataVersion3(
mapOf(
"SvcReqRegister" to RequestDataStructSvcReqRegister(
sBuffer = jceRequestSBuffer(
"SvcReqRegister",
SvcReqRegister.serializer(),
SvcReqRegister(
cConnType = 0,
lBid = 1 or 2 or 4,
......@@ -119,9 +117,7 @@ class StatSvc {
),
bSetMute = 0
)
).toByteArray(RequestDataStructSvcReqRegister.serializer())
)
).toByteArray(RequestDataVersion3.serializer())
)
)
}
......
......@@ -219,7 +219,7 @@ private fun parseSsoFrame(input: ByteReadPacket): KnownPacketFactories.IncomingP
commandName = readString(readInt() - 4)
DebugLogger.warning("commandName=$commandName")
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()}")
check(readInt() == 0)
}
......@@ -261,7 +261,7 @@ private fun parseUniFrame(input: ByteReadPacket): KnownPacketFactories.IncomingP
commandName = readString(readInt() - 4)
DebugLogger.warning("commandName=$commandName")
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()}")
check(readInt() == 0)
}
......
......@@ -3,19 +3,23 @@ package test;
import java.io.File
fun main(){
val var9 = toJCEInfo(
File(
"""
E:\Projects\QQAndroidFF\app\src\main\java\PushNotifyPack\RequestPushForceOffline.java
""".trimIndent()
).readText()
)
println(
"import kotlinx.serialization.SerialId\n" +
"import kotlinx.serialization.Serializable\n" +
"import net.mamoe.mirai.qqandroid.io.JceStruct\n"
)
println(var9.toString())
File(
"""
E:\Projects\QQAndroidFF\app\src\main\java\friendlist\
""".trimIndent()
).listFiles()!!.forEach {
try {
println(toJCEInfo(it.readText()).toString())
} catch (e: Exception) {
println("when processing ${it.path}")
throw e
}
}
}
......@@ -91,7 +95,7 @@ class Property(
}
fun toStringWithSpacing(maxIDLength:Int): String {
val space = " ".repeat(maxIDLength - (jceID.toString().length))
val space = " ".repeat((maxIDLength - (jceID.toString().length)).coerceAtLeast(0))
var base = " @SerialId(" + jceID + ") " + space + "val " + name + ":" + type + ""
if(!isRequired){
if(defaultValue == null) {
......@@ -114,7 +118,7 @@ fun toJCEInfo(source:String):JCEInfo{
val info = JCEInfo()
val allProperties = mutableMapOf<String,Property>()
var inputStreamVariableRegix:String? = null
println(source)
// println(source)
source.split("\n").forEach{
when{
it.contains("class") -> {
......
......@@ -92,7 +92,7 @@ abstract class Bot : CoroutineScope {
abstract val network: BotNetworkHandler
/**
* 登录.
* 登录, 或重新登录.
*
* 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.login]
*
......
......@@ -7,6 +7,7 @@ import kotlinx.coroutines.CompletableJob
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import net.mamoe.mirai.Bot
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.io.PlatformDatagramChannel
/**
......@@ -40,7 +41,10 @@ abstract class BotNetworkHandler : CoroutineScope {
/**
* 依次尝试登录到可用的服务器. 在任一服务器登录完成后返回.
* 本函数将挂起直到登录成功.
*
* 不要使用这个 API. 请使用 [Bot.login]
*/
@MiraiInternalAPI
abstract suspend fun login()
/**
......
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