From db3a1fb2d1d739f90f839066c09e94f49d383d24 Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Mon, 20 Jan 2020 00:17:45 +0800 Subject: [PATCH 01/14] Fix contentToString --- .../qqandroid/network/protocol/packet/login/LoginPacket.kt | 7 +------ .../net/mamoe/mirai/utils/cryptor/contentToString.kt | 2 +- .../kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt | 2 +- .../net/mamoe/mirai/utils/cryptor/contentToString.kt | 1 + 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt index 20a9c63b4..fe31b17d9 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt @@ -27,10 +27,6 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>() { this._id = PacketId(commandId = 0x0810, commandName = "wtlogin.login") } - fun hahahaha() { - - } - object SubCommand9 { private const val appId = 16L private const val subAppId = 537062845L @@ -61,8 +57,6 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>() { LoginType.PASSWORD ) - hahahaha() - /* // from GetStWithPasswd int mMiscBitmap = this.mMiscBitmap; if (t.uinDeviceToken) { @@ -199,6 +193,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>() { println("TLV KEYS: " + tlvMap.keys.joinToString { it.contentToString() }) tlvMap[0x150]?.let { client.analysisTlv150(it) } + tlvMap[0x305]?.let { println("TLV 0x305=${it.toUHexString()}") } tlvMap[0x161]?.let { client.analysisTlv161(it) } tlvMap[0x119]?.let { t119Data -> t119Data.decryptBy(client.tgtgtKey).toReadPacket().debugPrint("0x119data").apply { diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt index 6f33ae4c1..5e54798e7 100644 --- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt +++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt @@ -5,7 +5,7 @@ import kotlin.reflect.full.allSuperclasses actual fun Any.contentToStringReflectively(prefix: String): String { - val newPrefix = prefix + ProtoMap.indent + val newPrefix = prefix return (this::class.simpleName ?: "<UnnamedClass>") + "#" + this::class.hashCode() + " {\n" + this.allFieldsFromSuperClassesMatching { it.name.startsWith("net.mamoe.mirai") } .distinctBy { it.name } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt index afaf7279f..ad7905202 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt @@ -163,7 +163,7 @@ fun Any?.contentToString(prefix: String = ""): String = when (this) { is Iterable<*> -> this.joinToString(prefix = "[", postfix = "]") { it.contentToString() } is Iterator<*> -> this.asSequence().joinToString(prefix = "[", postfix = "]") { it.contentToString() } is Sequence<*> -> this.joinToString(prefix = "[", postfix = "]") { it.contentToString() } - is Map<*, *> -> this.entries.joinToString(prefix = "{", postfix = "}") { it.key.contentToString() + "=" + it.value.contentToString() } + is Map<*, *> -> this.entries.joinToString(prefix = "{", postfix = "}") { it.key.contentToString(prefix) + "=" + it.value.contentToString(prefix) } else -> { if (this == null) "null" else if (this::class.isData) this.toString() diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt index 117f0cf53..507b5e2e7 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt @@ -12,6 +12,7 @@ actual fun Any.contentToStringReflectively(prefix: String): String { this.allFieldsFromSuperClassesMatching { it.name.startsWith("net.mamoe.mirai") } .distinctBy { it.name } .filterNot { it.name.contains("$") || it.name == "Companion" || it.isSynthetic || it.name == "serialVersionUID" } + .filterNot { it.isEnumConstant } .joinToStringPrefixed( prefix = newPrefix ) { From 061f8fa89497d6072ac7c996e1cbb445a8838a72 Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Mon, 20 Jan 2020 20:19:41 +0800 Subject: [PATCH 02/14] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6596ecd43..bbd840913 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ [Mirai-Console](https://github.com/mamoe/mirai/tree/master/mirai-console) 插件支持, 在终端中启动Mirai并获得机器人服务 ## Use as a library -**把 Mirai 作为库内置于您的项目中使用.** +**mirai-core 为独立设计, 可以作为库内置于您的任意 Java/Android 项目中使用.** Mirai 只上传在 `jcenter`, 因此请确保在 `build.gradle` 添加 `jcenter()` 仓库 ```kotlin repositories{ @@ -120,4 +120,4 @@ bot.subscribeAlways<MemberPermissionChangedEvent> { ## Acknowledgement 特别感谢 [JetBrains](https://www.jetbrains.com/?from=mirai) 提供的免费 [IntelliJ IDEA](https://www.jetbrains.com/idea/?from=mirai) 等 IDE 授权 -[<img src=".github/jetbrains-variant-3.png" width="200"/>](https://www.jetbrains.com/?from=mirai) \ No newline at end of file +[<img src=".github/jetbrains-variant-3.png" width="200"/>](https://www.jetbrains.com/?from=mirai) From 767790f6e160e300b519565ca858c842190597b7 Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Mon, 20 Jan 2020 20:47:13 +0800 Subject: [PATCH 03/14] Introduce MiraiDebugAPI --- .../net.mamoe.mirai/utils/Annotations.kt | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt index 95cb38e13..ea99d5965 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt @@ -25,12 +25,24 @@ annotation class MiraiInternalAPI( * 这些 API 不具有稳定性, 且可能会在任意时刻更改. * 不建议在发行版本中使用这些 API. */ -@Experimental(level = Experimental.Level.ERROR) +@Experimental(level = Experimental.Level.WARNING) @Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR) annotation class MiraiExperimentalAPI( val message: String = "" ) +/** + * 标记这个类, 类型, 函数, 属性, 字段, 或构造器为仅供调试阶段使用的. + * + * 这些 API 不具有稳定性, 可能会在任意时刻更改, 并且效率非常低下. + * 非常不建议在发行版本中使用这些 API. + */ +@Experimental(level = Experimental.Level.WARNING) +@Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR) +annotation class MiraiDebugAPI( + val message: String = "" +) + /** * 标记这个 API 是自 Mirai 某个版本起才受支持. */ @@ -43,13 +55,13 @@ annotation class SinceMirai(val version: String) * 包的最后一次修改时间, 和分析时使用的 TIM 版本 */ @MustBeDocumented -@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.PROPERTY) +@Target(FUNCTION, CLASS, PROPERTY) @Retention(AnnotationRetention.SOURCE) annotation class PacketVersion(val date: String, val timVersion: String) /** * 带有这个注解的 [Packet] 将不会被记录在 log 中. */ -@Target(AnnotationTarget.CLASS) +@Target(CLASS) @Retention(AnnotationRetention.RUNTIME) annotation class NoLog \ No newline at end of file From 2ffd62c88301f0e029953ac9109131ccaf5c6002 Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Mon, 20 Jan 2020 20:47:46 +0800 Subject: [PATCH 04/14] (internal api) Rename debugPrintIfFail to debugIfFail --- .../network/protocol/packet/PacketFactory.kt | 8 +++----- .../network/protocol/packet/login/LoginPacket.kt | 4 ++++ .../network/packet/event/MemberMute.kt | 4 ++-- .../message/internal/MessageDataInternal.kt | 2 +- .../kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt | 16 +++++++++++++++- .../net.mamoe.mirai/utils/io/TypeConversion.kt | 14 ++++++++++++++ 6 files changed, 39 insertions(+), 9 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 ea37c371e..f095710a9 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 @@ -8,8 +8,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.login.NullPacketId import net.mamoe.mirai.qqandroid.network.protocol.packet.login.NullPacketId.commandName import net.mamoe.mirai.qqandroid.network.protocol.packet.login.PacketId -import net.mamoe.mirai.utils.cryptor.Decrypter -import net.mamoe.mirai.utils.cryptor.DecrypterType import net.mamoe.mirai.utils.cryptor.adjustToPublicKey import net.mamoe.mirai.utils.cryptor.decryptBy import net.mamoe.mirai.utils.io.* @@ -59,12 +57,12 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( // do not inline. Exceptions thrown will not be reported correctly suspend fun parseIncomingPacket(bot: QQAndroidBot, rawInput: ByteReadPacket, consumer: PacketConsumer) = - rawInput.debugPrintIfFail("Incoming packet") { + rawInput.debugIfFail("Incoming packet") { require(remaining < Int.MAX_VALUE) { "rawInput is too long" } val expectedLength = readUInt().toInt() - 4 if (expectedLength > 16e7) { bot.logger.warning("Detect incomplete packet, ignoring.") - return@debugPrintIfFail + return@debugIfFail } check(remaining.toInt() == expectedLength) { "Invalid packet length. Expected $expectedLength, got ${rawInput.remaining} Probably packets merged? " } // login @@ -87,7 +85,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( @UseExperimental(ExperimentalUnsignedTypes::class) private suspend fun parseLoginSsoPacket(bot: QQAndroidBot, rawInput: ByteReadPacket, consumer: PacketConsumer) = - rawInput.debugPrintIfFail("Login sso packet") { + rawInput.debugIfFail("Login sso packet") { val commandName: String val ssoSequenceId: Int readIoBuffer(readInt() - 4).withUse { diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt index fe31b17d9..c8e81b8f9 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt @@ -202,6 +202,10 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>() { // 01 08 00 10 A1 73 76 98 64 E0 38 C6 C8 18 73 FA D3 85 DA D6 01 6A 00 30 1D 99 4A 28 7E B3 B8 AC 74 B9 C4 BB 6D BB 41 72 F7 5C 9F 0F 79 8A 82 4F 1F 69 34 6D 10 D6 BB E8 A3 4A 2B 5D F1 C7 05 3C F8 72 EF CF 67 E4 3C 94 01 06 00 78 B4 ED 9F 44 ED 10 18 A8 85 0A 8A 85 79 45 47 7F 25 AA EE 2C 53 83 80 0A B3 B0 47 3E 95 51 A4 AE 3E CA A0 1D B4 91 F7 BB 2E 94 76 A8 C8 97 02 C4 5B 15 02 B7 03 9A FC C2 58 6D 17 92 46 AE EB 2F 6F 65 B8 69 6C D6 9D AC 18 6F 07 53 AC FE FA BC BD CE 57 13 10 2D 5A C6 50 AA C2 AE 18 D4 FD CD F2 E0 D1 25 29 56 21 35 8F 01 9D D6 69 44 8F 06 D0 23 26 D3 0E E6 E6 B7 01 0C 00 10 73 32 61 4E 2C 72 35 58 68 28 47 3E 2B 6E 52 62 01 0A 00 48 A4 DA 48 FB B4 8D DA 7B 86 D7 A7 FE 01 1B 70 6F 54 F8 55 38 B0 AD 1B 0C 0B B9 F6 94 24 F8 9E 30 32 22 99 0C 22 CD 44 B8 B0 8A A8 65 E1 B8 F0 49 EF E1 23 D7 0D A3 F1 BB 52 B7 4B AF BD 50 EA BF 15 02 78 2B 8B 10 FB 15 01 0D 00 10 29 75 38 72 21 5D 3F 24 37 46 67 79 2B 65 6D 34 01 14 00 60 00 01 5E 19 65 8C 00 58 93 DD 4D 2C 2D 01 44 99 62 B8 7A EF 04 C5 71 0B F1 BE 4C F4 21 F2 97 B0 14 67 0E 14 9F D8 A2 0B 93 40 90 80 F3 59 7A 69 45 D7 D4 53 4C 08 3A 56 1D C9 95 36 2C 7C 5E EE 36 47 5F AE 26 72 76 FD FD 69 E6 0C 2D 3A E8 CF D4 8D 76 C9 17 C3 E3 CD 21 AB 04 6B 70 C5 EC EC 01 0E 00 10 56 48 3E 29 3A 5A 21 74 55 6A 2C 72 58 73 79 71 01 03 00 30 9B A6 5D 85 5C 40 7C 28 E7 05 A9 25 CA F5 FC C0 51 40 85 F3 2F D2 37 F9 09 A6 E6 56 7F 7A 2E 7D 9F B9 1C 00 65 55 D2 A9 60 03 77 AB 6A F5 3F CE 01 33 00 30 F4 3A A7 08 E2 04 FA C8 9D 54 49 DE 63 EA F0 A5 1C C4 03 57 51 B6 AE 0B 55 41 F8 AB 22 F1 DC A3 B0 73 08 55 14 02 BF FF 55 87 42 4C 23 70 91 6A 01 34 00 10 61 C7 02 3F 1D BE A6 27 2F 24 D4 92 95 68 71 EF 05 28 00 1A 7B 22 51 49 4D 5F 69 6E 76 69 74 61 74 69 6F 6E 5F 62 69 74 22 3A 22 31 22 7D 03 22 00 10 CE 1E 2E DC 69 24 4F 9B FF 2F 52 D8 8F 69 DD 40 01 1D 00 76 5F 5E 10 E2 34 36 79 27 23 53 4D 65 6B 6A 33 6D 7D 4E 3C 5F 00 60 00 01 5E 19 65 8C 00 58 67 00 9C 02 E4 BC DB A3 93 98 A1 ED 4C 91 08 6F 0C 06 E0 12 6A DC 14 5B 4D 20 7C 82 83 AE 94 53 A2 4A A0 35 FF 59 9D F3 EF 82 42 61 67 2A 31 E7 87 7E 74 E7 A3 E7 5C A8 3C 87 CF 40 6A 9F E5 F7 20 4E 56 C6 4F 1C 98 3A 8B A9 4F 1D 10 35 C2 3B A1 08 7A 89 0B 25 0C 63 01 1F 00 0A 00 01 51 80 00 00 03 84 00 00 01 38 00 0E 00 00 00 01 01 0A 00 27 8D 00 00 00 00 00 01 1A 00 13 02 5B 06 01 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E 05 22 00 14 00 00 00 00 76 E4 B8 DD AB 53 02 9F 5E 19 65 8C 20 02 ED BD 05 37 00 17 01 01 00 00 00 00 76 E4 B8 DD 04 AB 53 02 9F 5E 19 65 8C 20 02 ED BD 01 20 00 0A 4D 39 50 57 50 6E 4C 31 65 4F 01 6D 00 2C 31 7A 50 7A 63 72 70 4D 30 43 6E 31 37 4C 32 32 6E 77 2D 36 7A 4E 71 48 48 59 41 35 48 71 77 41 37 6D 76 4F 63 2D 4A 56 77 47 51 5F 05 12 03 5D 00 0E 00 0A 74 65 6E 70 61 79 2E 63 6F 6D 00 2C 6E 4A 72 55 55 74 63 2A 34 7A 32 76 31 66 6A 75 77 6F 6A 65 73 72 76 4F 68 70 66 45 76 4A 75 55 4B 6D 34 43 2D 76 74 38 4D 77 38 5F 00 00 00 11 6F 70 65 6E 6D 6F 62 69 6C 65 2E 71 71 2E 63 6F 6D 00 2C 78 59 35 65 62 4D 74 48 44 6D 30 53 6F 68 56 71 68 33 43 79 79 34 6F 63 65 4A 46 6A 51 58 65 68 30 44 61 75 55 30 6C 78 65 52 6B 5F 00 00 00 0B 64 6F 63 73 2E 71 71 2E 63 6F 6D 00 2C 64 6A 62 79 47 57 45 4F 34 58 34 6A 36 4A 73 48 45 65 6B 73 69 74 72 78 79 62 57 69 77 49 68 46 45 70 72 4A 59 4F 2D 6B 36 47 6F 5F 00 00 00 0E 63 6F 6E 6E 65 63 74 2E 71 71 2E 63 6F 6D 00 2C 64 4C 31 41 79 32 41 31 74 33 58 36 58 58 2A 74 33 64 4E 70 2A 31 61 2D 50 7A 65 57 67 48 70 2D 65 47 78 6B 59 74 71 62 69 6C 55 5F 00 00 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 00 2C 75 6A 55 5A 4F 6A 4F 48 52 61 75 6B 32 55 50 38 77 33 34 68 36 69 46 38 2A 77 4E 50 35 2D 66 54 75 37 67 39 56 67 44 57 2A 6B 6F 5F 00 00 00 0A 76 69 70 2E 71 71 2E 63 6F 6D 00 2C 37 47 31 44 6F 54 2D 4D 57 50 63 2D 62 43 46 68 63 62 32 56 38 6E 77 4A 75 41 51 63 54 39 77 45 49 62 57 43 4A 4B 44 4D 6C 6D 34 5F 00 00 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 00 2C 7A 73 70 5A 56 43 59 45 7A 35 2A 4F 6B 4E 68 6E 74 79 61 69 6E 6F 68 4D 32 6B 41 6C 2A 74 31 63 7A 48 57 77 30 41 6A 4B 50 4B 6B 5F 00 00 00 0B 67 61 6D 65 2E 71 71 2E 63 6F 6D 00 2C 32 6F 2D 51 53 36 65 43 70 37 6A 43 4E 34 6A 74 6E 47 4F 4B 33 67 73 32 63 4A 6F 56 71 58 65 44 48 61 55 39 65 34 2D 32 34 64 30 5F 00 00 00 0C 71 71 77 65 62 2E 71 71 2E 63 6F 6D 00 2C 63 54 4D 79 64 51 43 35 50 74 43 45 51 72 6F 33 53 54 41 66 7A 56 2D 44 76 46 56 35 58 6D 56 6B 49 31 68 4C 55 48 4E 65 76 56 38 5F 00 00 00 0D 6F 66 66 69 63 65 2E 71 71 2E 63 6F 6D 00 2C 6F 73 72 54 36 32 69 37 66 76 6D 49 50 64 6F 58 4B 48 74 38 58 52 59 56 77 72 7A 6E 69 31 58 7A 57 4C 77 2A 71 36 33 44 74 73 6F 5F 00 00 00 09 74 69 2E 71 71 2E 63 6F 6D 00 2C 41 61 77 4D 78 4D 32 79 58 51 47 75 72 75 55 6C 66 53 58 79 5A 57 48 53 78 52 57 58 50 74 6B 6B 4F 78 6F 66 4A 59 47 6C 71 68 34 5F 00 00 00 0B 6D 61 69 6C 2E 71 71 2E 63 6F 6D 00 2C 67 72 57 68 58 77 34 4C 6E 4B 49 4F 67 63 78 45 71 70 33 61 45 67 37 38 46 7A 77 4E 6D 4B 48 56 6E 6F 50 4C 4F 32 6D 57 6D 6E 38 5F 00 00 00 09 71 7A 6F 6E 65 2E 63 6F 6D 00 2C 72 61 47 79 51 35 54 72 4D 55 7A 6E 74 31 4E 52 44 2D 50 72 74 72 41 55 43 35 6A 61 2D 49 47 2D 73 77 4C 6D 49 51 51 41 44 4C 41 5F 00 00 00 0A 6D 6D 61 2E 71 71 2E 63 6F 6D 00 2C 39 73 2D 4F 51 30 67 76 39 42 6A 37 58 71 52 49 4E 30 35 46 32 64 4D 47 67 47 43 58 57 4A 62 68 63 30 38 63 7A 4B 52 76 6B 78 6B 5F 00 00 03 05 00 10 77 75 6E 54 5F 7E 66 7A 72 40 3C 6E 35 50 53 46 01 43 00 40 3A AE 30 87 81 3D EE BA 31 9C EA 9D 0D D4 73 B1 81 12 E0 94 71 73 7A B0 47 3D 09 47 E5 1B E1 E2 06 1A CB A4 E3 71 9E A6 EA 2A 73 5C C8 D3 B1 2A B1 C7 DA 04 A6 6D 12 26 DF 6B 8B EC C7 12 F8 E1 01 18 00 05 00 00 00 01 00 01 63 00 10 67 6B 60 23 24 6A 55 39 4E 58 24 5E 39 2B 7A 69 01 38 00 5E 00 00 00 09 01 06 00 27 8D 00 00 00 00 00 01 0A 00 24 EA 00 00 00 00 00 01 1C 00 1A 5E 00 00 00 00 00 01 02 00 01 51 80 00 00 00 00 01 03 00 00 1C 20 00 00 00 00 01 20 00 01 51 80 00 00 00 00 01 36 00 1B AF 80 00 00 00 00 01 43 00 1B AF 80 00 00 00 00 01 64 00 1B AF 80 00 00 00 00 01 30 00 0E 00 00 5E 19 65 8C 9F 02 53 AB 00 00 00 00 val tlvMap119 = this.readTLVMap() println("tlvMap119 KEYS: " + tlvMap119.keys.joinToString { it.contentToString() }) + tlvMap119[0x305]?.let { println("TLV 0x305=${it.toUHexString()}") } + tlvMap119.filterValues { it.size == 16 }.forEach { + println(it.key.toUHexString("") + "=" + it.value.toUHexString()) + } // ??? tlvMap119[0x1c]?.read { diff --git a/mirai-core-timpc/src/commonMain/kotlin/net.mamoe.mirai.timpc/network/packet/event/MemberMute.kt b/mirai-core-timpc/src/commonMain/kotlin/net.mamoe.mirai.timpc/network/packet/event/MemberMute.kt index f1c42d740..e2bf6f2a5 100644 --- a/mirai-core-timpc/src/commonMain/kotlin/net.mamoe.mirai.timpc/network/packet/event/MemberMute.kt +++ b/mirai-core-timpc/src/commonMain/kotlin/net.mamoe.mirai.timpc/network/packet/event/MemberMute.kt @@ -10,7 +10,7 @@ import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Member import net.mamoe.mirai.data.* -import net.mamoe.mirai.utils.io.debugPrintIfFail +import net.mamoe.mirai.utils.io.debugIfFail import net.mamoe.mirai.utils.io.readQQ import net.mamoe.mirai.utils.io.readRemainingBytes import net.mamoe.mirai.utils.io.toUHexString @@ -62,7 +62,7 @@ internal object MemberMuteEventPacketParserAndHandler : KnownEventParserAndHandl Unknown0x02DCPacketFlag0x0EMaybeMutePacket(readRemainingBytes()) } - 0x11u -> debugPrintIfFail("解析禁言包(0x02DC)时"){ // 猜测这个失败是撤回?? + 0x11u -> debugIfFail("解析禁言包(0x02DC)时"){ // 猜测这个失败是撤回?? // 00 0A 00 04 01 00 00 00 00 0C 00 05 00 01 00 01 01 27 0B 60 E7 11 00 33 08 07 20 E7 C1 AD B8 02 5A 29 08 A6 FE C0 A4 0A 1A 19 08 BC 15 10 C1 95 BC F0 05 18 CA CA 8F DE 04 20 00 28 00 30 A6 FE C0 A4 0A 2A 02 08 00 30 00 38 00 // 失败 diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt index aca589b1c..cca576982 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt @@ -9,7 +9,7 @@ import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.unzip internal fun IoBuffer.parseMessageFace(): Face { - debugPrintIfFail("Analyzing Face") { + debugIfFail("Analyzing Face") { discardExact(1) //00 01 AF 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 F0 diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt index 80e9253fd..151b55b00 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt @@ -5,6 +5,9 @@ import kotlinx.io.pool.useInstance import net.mamoe.mirai.utils.DefaultLogger import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.withSwitch +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract object DebugLogger : MiraiLogger by DefaultLogger("Packet Debug").withSwitch() @@ -51,7 +54,18 @@ fun ByteReadPacket.debugPrint(name: String = ""): ByteReadPacket { } } -inline fun <R> Input.debugPrintIfFail(name: String = "", block: ByteReadPacket.() -> R): R { +/** + * 备份数据, 并在 [block] 失败后执行 [onFail]. + * + * 此方法非常低效. 请仅在测试环境使用. + */ +@MiraiDebugAPI +@UseExperimental(ExperimentalContracts::class) +inline fun <R> Input.debugIfFail(name: String = "", onFail: (ByteArray) -> ByteReadPacket = { it.toReadPacket() }, block: ByteReadPacket.() -> R): R { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + callsInPlace(onFail, InvocationKind.UNKNOWN) + } ByteArrayPool.useInstance { val count = this.readAvailable(it) try { diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConversion.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConversion.kt index 6cfe2ff68..8c65f1e4a 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConversion.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConversion.kt @@ -99,6 +99,8 @@ fun UByte.fixToUHex(): String = if (this.toInt() in 0..15) "0${this.toString(16) /** * 将无符号 Hex 转为 [ByteArray], 有根据 hex 的 [hashCode] 建立的缓存. + * + * 这个方法很累, 不建议经常使用. */ fun String.hexToBytes(): ByteArray = this.split(" ") @@ -110,12 +112,24 @@ fun String.hexToBytes(): ByteArray = /** * 每 2 char 为一组, 转换 Hex 为 [ByteArray] + * + * 这个方法很累, 不建议经常使用. */ fun String.chunkedHexToBytes(): ByteArray = this.asSequence().chunked(2).map { (it[0].toString() + it[1]).toUByte(16).toByte() }.toList().toByteArray() +/** + * 删掉全部空格和换行后每 2 char 为一组, 转换 Hex 为 [ByteArray]. + * + * 这个方法很累, 不建议经常使用. + */ +fun String.autoHexToBytes(): ByteArray = + this.replace("\n", "").replace(" ", "").asSequence().chunked(2).map { (it[0].toString() + it[1]).toUByte(16).toByte() }.toList().toByteArray() + /** * 将无符号 Hex 转为 [UByteArray], 有根据 hex 的 [hashCode] 建立的缓存. + * + * 这个方法很累, 不建议经常使用. */ fun String.hexToUBytes(): UByteArray = this.split(" ") From 621809b94b2fcf7724f0d932e6124a182462ea15 Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Mon, 20 Jan 2020 20:49:43 +0800 Subject: [PATCH 05/14] Add Retention --- .../src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt index ea99d5965..7efe84d8a 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt @@ -8,6 +8,7 @@ import kotlin.annotation.AnnotationTarget.* * 这些 API 可能会在任意时刻更改, 且不会发布任何预警. * 非常不建议在发行版本中使用这些 API. */ +@Retention(AnnotationRetention.SOURCE) @Experimental(level = Experimental.Level.ERROR) @Target( CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR, @@ -25,6 +26,7 @@ annotation class MiraiInternalAPI( * 这些 API 不具有稳定性, 且可能会在任意时刻更改. * 不建议在发行版本中使用这些 API. */ +@Retention(AnnotationRetention.SOURCE) @Experimental(level = Experimental.Level.WARNING) @Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR) annotation class MiraiExperimentalAPI( @@ -37,6 +39,7 @@ annotation class MiraiExperimentalAPI( * 这些 API 不具有稳定性, 可能会在任意时刻更改, 并且效率非常低下. * 非常不建议在发行版本中使用这些 API. */ +@Retention(AnnotationRetention.SOURCE) @Experimental(level = Experimental.Level.WARNING) @Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR) annotation class MiraiDebugAPI( From 2052159dc91a417eb692f7c86d2009736a65db1c Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Mon, 20 Jan 2020 20:56:43 +0800 Subject: [PATCH 06/14] Annotate with MiraiDebugAPI --- .../kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt index 151b55b00..4c25f58a2 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt @@ -2,9 +2,7 @@ package net.mamoe.mirai.utils.io import kotlinx.io.core.* import kotlinx.io.pool.useInstance -import net.mamoe.mirai.utils.DefaultLogger -import net.mamoe.mirai.utils.MiraiLogger -import net.mamoe.mirai.utils.withSwitch +import net.mamoe.mirai.utils.* import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -14,18 +12,22 @@ object DebugLogger : MiraiLogger by DefaultLogger("Packet Debug").withSwitch() fun Throwable.logStacktrace(message: String? = null) = DebugLogger.error(message, this) +@MiraiDebugAPI("Low efficiency.") fun debugPrintln(any: Any?) = DebugLogger.debug(any) +@MiraiDebugAPI("Low efficiency.") fun String.debugPrint(name: String): String { DebugLogger.debug("$name=$this") return this } +@MiraiDebugAPI("Low efficiency.") fun ByteArray.debugPrint(name: String): ByteArray { DebugLogger.debug(name + "=" + this.toUHexString()) return this } +@MiraiDebugAPI("Low efficiency.") fun IoBuffer.debugPrint(name: String): IoBuffer { ByteArrayPool.useInstance { val count = this.readAvailable(it) @@ -34,6 +36,7 @@ fun IoBuffer.debugPrint(name: String): IoBuffer { } } +@MiraiDebugAPI("Low efficiency.") inline fun IoBuffer.debugCopyUse(block: IoBuffer.() -> Unit): IoBuffer { ByteArrayPool.useInstance { val count = this.readAvailable(it) @@ -42,10 +45,12 @@ inline fun IoBuffer.debugCopyUse(block: IoBuffer.() -> Unit): IoBuffer { } } +@MiraiDebugAPI("Low efficiency.") fun Input.debugDiscardExact(n: Number, name: String = "") { DebugLogger.debug("Discarded($n) $name=" + this.readBytes(n.toInt()).toUHexString()) } +@MiraiDebugAPI("Low efficiency.") fun ByteReadPacket.debugPrint(name: String = ""): ByteReadPacket { ByteArrayPool.useInstance { val count = this.readAvailable(it) @@ -59,7 +64,7 @@ fun ByteReadPacket.debugPrint(name: String = ""): ByteReadPacket { * * 此方法非常低效. 请仅在测试环境使用. */ -@MiraiDebugAPI +@MiraiDebugAPI("Low efficiency") @UseExperimental(ExperimentalContracts::class) inline fun <R> Input.debugIfFail(name: String = "", onFail: (ByteArray) -> ByteReadPacket = { it.toReadPacket() }, block: ByteReadPacket.() -> R): R { contract { From 0345bfa4b3ee02a58a3c56bd604f32c0b3284e12 Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Mon, 20 Jan 2020 20:57:03 +0800 Subject: [PATCH 07/14] Use ByteArray to store ipAddress --- .../net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt | 2 +- .../net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt | 5 +++-- .../net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt b/mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt index 2db2d14bc..c9936f510 100644 --- a/mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt +++ b/mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt @@ -76,7 +76,7 @@ actual class SystemDeviceInfo actual constructor(context: Context) : DeviceInfo( (context.applicationContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager).deviceId } }.getOrElse { "" } - override val ipAddress: String get() = localIpAddress() + override val ipAddress: ByteArray get() = localIpAddress().split(".").map { it.toByte() }.takeIf { it.size == 4 }?.toByteArray() ?: byteArrayOf() override val androidId: ByteArray get() = Build.ID.toByteArray() override val apn: ByteArray get() = "wifi".toByteArray() diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt index 7c78eb00d..008c8f70c 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt @@ -16,14 +16,15 @@ import kotlin.random.Random */ inline class Tlv(val value: ByteArray) -fun BytePacketBuilder.t1(uin: Long, ip: String) { +fun BytePacketBuilder.t1(uin: Long, ip: ByteArray) { + require(ip.size == 4) { "ip.size must == 4" } writeShort(0x1) writeShortLVPacket { writeShort(1) // _ip_ver writeInt(Random.nextInt()) writeInt(uin.toInt()) writeTime() - writeFully(ByteArray(4)) + writeFully(ip) writeShort(0) } shouldEqualsTo 20 } diff --git a/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt b/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt index ed924c03a..8716e2b8c 100644 --- a/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt +++ b/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt @@ -27,7 +27,7 @@ actual class SystemDeviceInfo actual constructor(context: Context) : DeviceInfo( override val imsiMd5: ByteArray get() = ubyteArrayOf(0xD4u, 0x1Du, 0x8Cu, 0xD9u, 0x8Fu, 0x00u, 0xB2u, 0x04u, 0xE9u, 0x80u, 0x09u, 0x98u, 0xECu, 0xF8u, 0x42u, 0x7Eu).toByteArray() override val imei: String get() = "858414369211993" - override val ipAddress: String get() = localIpAddress() + override val ipAddress: ByteArray get() = localIpAddress().split(".").map { it.toByte() }.takeIf { it.size == 4 }?.toByteArray() ?: byteArrayOf() override val androidId: ByteArray get() = "QSR1.190920.001".toByteArray() override val apn: ByteArray get() = "wifi".toByteArray() From 5445a10e5fd7f75e7b4076d429dcbd78bc6a8b3f Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Mon, 20 Jan 2020 21:29:07 +0800 Subject: [PATCH 08/14] Fix --- .../src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt index 4c25f58a2..c7dc58de5 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt @@ -76,6 +76,7 @@ inline fun <R> Input.debugIfFail(name: String = "", onFail: (ByteArray) -> ByteR try { return block(it.toReadPacket(0, count)) } catch (e: Throwable) { + onFail(it.take(count).toByteArray()).readAvailable(it) DebugLogger.debug("Error in ByteReadPacket $name=" + it.toUHexString(offset = 0, length = count)) throw e } From 7c4263da0f7c4597d9ec884303500d29e669abf5 Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Mon, 20 Jan 2020 21:33:33 +0800 Subject: [PATCH 09/14] fix --- .../network/protocol/jce/SvcReqRegister.kt | 16 ++++++++-------- .../network/protocol/packet/login/LoginPacket.kt | 2 ++ .../mamoe/mirai/qqandroid/utils/DeviceInfo.kt | 4 +--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/SvcReqRegister.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/SvcReqRegister.kt index 727155432..3bea4715f 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/SvcReqRegister.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/SvcReqRegister.kt @@ -25,15 +25,15 @@ class SvcReqRegister( val lBid: Long = 0L, val lCpId: Long = 0L, val lUin: Long = 0L, - val sBuildVer: String? = "", - val sChannelNo: String? = "", + val sBuildVer: String? = null, + val sChannelNo: String? = null, val sOther: String = "", - val strDevName: String? = "", - val strDevType: String? = "", - val strIOSIdfa: String? = "", - val strOSVer: String? = "", - val strVendorName: String? = "", - val strVendorOSName: String? = "", + val strDevName: String? = null, + val strDevType: String? = null, + val strIOSIdfa: String? = null, + val strOSVer: String? = null, + val strVendorName: String? = null, + val strVendorOSName: String? = null, val timeStamp: Long = 0L, val uNewSSOIp: Long = 0L, val uOldSSOIp: Long = 0L, diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt index c8e81b8f9..87b748041 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt @@ -10,6 +10,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.* import net.mamoe.mirai.qqandroid.utils.GuidSource import net.mamoe.mirai.qqandroid.utils.MacOrAndroidIdChangeFlag import net.mamoe.mirai.qqandroid.utils.guidFlag +import net.mamoe.mirai.utils.MiraiDebugAPI import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.cryptor.contentToString import net.mamoe.mirai.utils.cryptor.decryptBy @@ -174,6 +175,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>() { object Success : LoginPacketResponse() } + @UseExperimental(MiraiDebugAPI::class) override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): LoginPacketResponse = this.debugPrint("login解析").run { // 00 09 sub cmd // 00 type diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/DeviceInfo.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/DeviceInfo.kt index 0094bea83..327a775a8 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/DeviceInfo.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/DeviceInfo.kt @@ -3,10 +3,8 @@ package net.mamoe.mirai.qqandroid.utils import kotlinx.serialization.SerialId import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoBuf -import net.mamoe.mirai.qqandroid.network.protocol.packet.oidb.oidb0x769.Oidb0x769 import net.mamoe.mirai.utils.cryptor.contentToString import net.mamoe.mirai.utils.getValue -import net.mamoe.mirai.utils.io.encodeToString import net.mamoe.mirai.utils.unsafeWeakRef abstract class DeviceInfo( @@ -42,7 +40,7 @@ abstract class DeviceInfo( abstract val imsiMd5: ByteArray abstract val imei: String - abstract val ipAddress: String + abstract val ipAddress: ByteArray abstract val androidId: ByteArray From 6b6499e929fb066fc89a2ac4fac38431c0174ac1 Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Mon, 20 Jan 2020 22:30:33 +0800 Subject: [PATCH 10/14] gradle init --- .../mirai/qqandroid/network/io/JceInput.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt new file mode 100644 index 000000000..f1dd872a1 --- /dev/null +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt @@ -0,0 +1,18 @@ +package net.mamoe.mirai.qqandroid.network.io + +import kotlinx.io.core.Input + +@UseExperimental(ExperimentalUnsignedTypes::class) +inline class JceHead(private val value: Long) { + val tag: Int get() = (value ushr 32).toInt() + val type: Int get() = value.toUInt().toInt() +} + +class JceInput( + private val input: Input +): Input by input { + + private fun readHead(): JceHead { + + } +} \ No newline at end of file From e44e7b395fdce5f5ee216df80b73ac614a029774 Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Tue, 21 Jan 2020 01:52:29 +0800 Subject: [PATCH 11/14] Daily QQA update: JceInput --- .../mirai/qqandroid/network/io/JceInput.kt | 322 +++++++++++++++++- .../mirai/qqandroid/network/io/JceStruct.kt | 6 +- .../network/protocol/jce/RequestPacket.kt | 7 + .../network/protocol/jce/SvcReqRegister.kt | 8 +- .../network/protocol/packet/PacketFactory.kt | 4 +- 5 files changed, 338 insertions(+), 9 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt index f1dd872a1..cabecf645 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt @@ -1,18 +1,330 @@ package net.mamoe.mirai.qqandroid.network.io -import kotlinx.io.core.Input +import kotlinx.io.core.* +import net.mamoe.mirai.utils.io.readString +import kotlin.experimental.and @UseExperimental(ExperimentalUnsignedTypes::class) inline class JceHead(private val value: Long) { + constructor(tag: Int, type: Byte) : this(tag.shl(32).toLong() and type.toLong()) + val tag: Int get() = (value ushr 32).toInt() - val type: Int get() = value.toUInt().toInt() + val type: Byte get() = value.toUInt().toByte() } +@Suppress("MemberVisibilityCanBePrivate") +@UseExperimental(ExperimentalUnsignedTypes::class) class JceInput( - private val input: Input -): Input by input { + @PublishedApi + internal val input: Input +) { - private fun readHead(): JceHead { + @PublishedApi + internal fun readHead(): JceHead = input.run { + val var2 = readByte() + val type = var2 and 15 + var tag = (var2.toInt() and 240) shr 4 + if (tag == 15) + tag = readByte().toInt() and 255 + return JceHead(tag = tag, type = type) + } + fun readBoolean(tag: Int): Boolean = readBooleanOrNull(tag) ?: error("cannot find tag $tag") + fun readByte(tag: Int): Byte = readByteOrNull(tag) ?: error("cannot find tag $tag") + fun readShort(tag: Int): Short = readShortOrNull(tag) ?: error("cannot find tag $tag") + fun readInt(tag: Int): Int = readIntOrNull(tag) ?: error("cannot find tag $tag") + fun readLong(tag: Int): Long = readLongOrNull(tag) ?: error("cannot find tag $tag") + fun readFloat(tag: Int): Float = readFloatOrNull(tag) ?: error("cannot find tag $tag") + fun readDouble(tag: Int): Double = readDoubleOrNull(tag) ?: error("cannot find tag $tag") + + fun readString(tag: Int): String = readStringOrNull(tag) ?: error("cannot find tag $tag") + + fun readByteArray(tag: Int): ByteArray = readByteArrayOrNull(tag) ?: error("cannot find tag $tag") + fun readShortArray(tag: Int): ShortArray = readShortArrayOrNull(tag) ?: error("cannot find tag $tag") + fun readLongArray(tag: Int): LongArray = readLongArrayOrNull(tag) ?: error("cannot find tag $tag") + fun readFloatArray(tag: Int): FloatArray = readFloatArrayOrNull(tag) ?: error("cannot find tag $tag") + fun readDoubleArray(tag: Int): DoubleArray = readDoubleArrayOrNull(tag) ?: error("cannot find tag $tag") + fun readIntArray(tag: Int): IntArray = readIntArrayOrNull(tag) ?: error("cannot find tag $tag") + fun readBooleanArray(tag: Int): BooleanArray = readBooleanArrayOrNull(tag) ?: error("cannot find tag $tag") + fun <K, V> readMap(defaultKey: K, defaultValue: V, tag: Int): Map<K, V> = readMapOrNull(defaultKey, defaultValue, tag) ?: error("cannot find tag $tag") + fun <T> readList(defaultElement: T, tag: Int): List<T> = readListOrNull(defaultElement, tag) ?: error("cannot find tag $tag") + fun <T> readSimpleArray(defaultElement: T, tag: Int): Array<T> = readArrayOrNull(defaultElement, tag) ?: error("cannot find tag $tag") + fun <J : JceStruct> readJceStruct(factory: JceStruct.Factory<J>, tag: Int): J = readJceStructOrNull(factory, tag) ?: error("cannot find tag $tag") + fun readStringArray(tag: Int): Array<String> = readArrayOrNull("", tag) ?: error("cannot find tag $tag") + + fun readLongOrNull(tag: Int): Long? = skipToTagOrNull(tag) { + return when (it.type.toInt()) { + 12 -> 0 + 0 -> input.readByte().toLong() + 1 -> input.readShort().toLong() + 3 -> input.readLong() + else -> error("type mismatch: ${it.type}") + } + } + + fun readShortOrNull(tag: Int): Short? = skipToTagOrNull(tag) { + return when (it.type.toInt()) { + 12 -> 0 + 0 -> input.readByte().toShort() + 1 -> input.readShort() + else -> error("type mismatch: ${it.type}") + } + } + + fun readIntOrNull(tag: Int): Int? = skipToTagOrNull(tag) { + return when (it.type.toInt()) { + 12 -> 0 + 0 -> input.readByte().toInt() + 1 -> input.readShort().toInt() + 2 -> input.readInt() + else -> error("type mismatch: ${it.type}") + } + } + + fun readByteOrNull(tag: Int): Byte? = skipToTagOrNull(tag) { + return when (it.type.toInt()) { + 12 -> 0 + 0 -> input.readByte() + else -> error("type mismatch") + } + } + + fun readFloatOrNull(tag: Int): Float? = skipToTagOrNull(tag) { + return when (it.type.toInt()) { + 12 -> 0f + 4 -> input.readFloat() + else -> error("type mismatch: ${it.type}") + } + } + + fun readDoubleOrNull(tag: Int): Double? = skipToTagOrNull(tag) { + return when (it.type.toInt()) { + 12 -> 0.0 + 4 -> input.readFloat().toDouble() + 5 -> input.readDouble() + else -> error("type mismatch: ${it.type}") + } + } + + fun readBooleanOrNull(tag: Int): Boolean? = this.readByteOrNull(tag)?.let { it.toInt() != 0 } + + fun readByteArrayOrNull(tag: Int): ByteArray? = skipToTagOrNull(tag) { + when (it.type.toInt()) { + 9 -> ByteArray(input.readInt()) { readByte(tag) } + 13 -> { + val head = readHead() + check(head.type.toInt() == 0) { "type mismatch" } + input.readBytes(input.readInt()) + } + else -> error("type mismatch") + } + } + + fun readShortArrayOrNull(tag: Int): ShortArray? = skipToTagOrNull(tag) { + require(it.type.toInt() == 9) { "type mismatch" } + ShortArray(input.readInt()) { readShort(tag) } + } + + fun readDoubleArrayOrNull(tag: Int): DoubleArray? = skipToTagOrNull(tag) { + require(it.type.toInt() == 9) { "type mismatch" } + DoubleArray(input.readInt()) { readDouble(tag) } + } + + fun readFloatArrayOrNull(tag: Int): FloatArray? = skipToTagOrNull(tag) { + require(it.type.toInt() == 9) { "type mismatch" } + FloatArray(input.readInt()) { readFloat(tag) } + } + + fun readIntArrayOrNull(tag: Int): IntArray? = skipToTagOrNull(tag) { + require(it.type.toInt() == 9) { "type mismatch" } + IntArray(input.readInt()) { readInt(tag) } + } + + fun readLongArrayOrNull(tag: Int): LongArray? = skipToTagOrNull(tag) { + require(it.type.toInt() == 9) { "type mismatch" } + LongArray(input.readInt()) { readLong(tag) } + } + + @Suppress("UNCHECKED_CAST") + inline fun <reified T> readArrayOrNull(tag: Int): Array<T>? = skipToTagOrNull(tag) { + require(it.type.toInt() == 9) { "type mismatch" } + Array(input.readInt()) { readSimpleObject<T>(tag) } + } + + @Suppress("UNCHECKED_CAST") + fun <T> readArrayOrNull(defaultElement: T, tag: Int): Array<T>? = skipToTagOrNull(tag) { + require(it.type.toInt() == 9) { "type mismatch" } + Array(input.readInt()) { readObject(defaultElement, tag) as Any } as Array<T> + } + + @Suppress("UNCHECKED_CAST") + fun <J : JceStruct> readJceStructArrayOrNull(factory: JceStruct.Factory<J>, tag: Int): Array<J>? = skipToTagOrNull(tag) { + require(it.type.toInt() == 9) { "type mismatch" } + Array(input.readInt()) { readObject(factory, tag) as Any } as Array<J> + } + + fun readBooleanArrayOrNull(tag: Int): BooleanArray? = skipToTagOrNull(tag) { + require(it.type.toInt() == 9) { "type mismatch" } + BooleanArray(input.readInt()) { readBoolean(tag) } + } + + fun readStringOrNull(tag: Int): String? = skipToTagOrNull(tag) { head -> + return when (head.type.toInt()) { + 6 -> input.readString(input.readUByte().toInt()) + 7 -> input.readString(input.readUInt().also { require(it.toInt() in 1 until 104857600) { "bad string length: $it" } }.toInt()) + else -> error("type mismatch: ${head.type}") + } + } + + fun <T : Map<K, V>, K, V> readMapOrNull(defaultKey: K, defaultValue: V, tag: Int): Map<K, V>? = skipToTagOrNull(tag) { + check(it.type.toInt() == 8) { "type mismatch" } + val size = readInt(0) + val map = HashMap<K, V>(size) + repeat(size) { + map[readObject(defaultKey, 0)] = readObject(defaultValue, 0) + } + return map + } + + fun <T> readListOrNull(defaultElement: T, tag: Int): List<T>? = skipToTagOrNull(tag) { head -> + check(head.type.toInt() == 9) { "type mismatch" } + val size = readInt(0) + val list = ArrayList<T>(size) + repeat(size) { + list[it] = readObject(defaultElement, tag) + } + return list + } + + fun <J : JceStruct> readJceStructOrNull(factory: JceStruct.Factory<J>, tag: Int): J? = skipToTagOrNull(tag) { head -> + readHead() + return factory.newInstanceFrom(this).also { skipToStructEnd() } + } + + @Suppress("UNCHECKED_CAST") + fun <T> readArrayOrNull(default: Array<T>, tag: Int): Array<T>? = skipToTagOrNull(tag) { head -> + val defaultElement = default[0] + check(head.type.toInt() == 9) { "type mismatch" } + return Array(readInt(0)) { readObject(defaultElement, tag) as Any } as Array<T> + } + + @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST") + fun <T> readObject(default: T, tag: Int): T = when (default) { + is Byte -> readByte(tag) + is Boolean -> readBoolean(tag) + is Short -> readShort(tag) + is Int -> readInt(tag) + is Long -> readLong(tag) + is Float -> readFloat(tag) + is Double -> readDouble(tag) + is String -> readString(tag) + is BooleanArray -> readBooleanArray(tag) + is ShortArray -> readShortArray(tag) + is IntArray -> readIntArray(tag) + is LongArray -> readLongArray(tag) + is ByteArray -> readByteArray(tag) + is FloatArray -> readByteArray(tag) + is DoubleArray -> readDoubleArrayOrNull(tag) + is JceStruct.Factory<JceStruct> -> readJceStruct(default, tag) as T + is List<*> -> { + readList(default[0], tag) + } + is Map<*, *> -> { + val entry = default.entries.first() + readMap(entry.key, entry.value, tag) + } + is Array<*> -> readSimpleArray(default, tag) + else -> error("unsupported type") + } as T + + @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST") + inline fun <reified T> readSimpleObject(tag: Int): T = when (T::class) { + Byte::class -> readByte(tag) + Boolean::class -> readBoolean(tag) + Short::class -> readShort(tag) + Int::class -> readInt(tag) + Long::class -> readLong(tag) + Float::class -> readFloat(tag) + Double::class -> readDouble(tag) + + String::class -> readString(tag) + + BooleanArray::class -> readBooleanArray(tag) + ShortArray::class -> readShortArray(tag) + IntArray::class -> readIntArray(tag) + LongArray::class -> readLongArray(tag) + ByteArray::class -> readByteArray(tag) + FloatArray::class -> readByteArray(tag) + DoubleArray::class -> readDoubleArrayOrNull(tag) + else -> error("Type is not supported: ${T::class.simpleName}") + } as T + + private fun skipField() { + skipField(readHead().type) + } + + private fun skipToStructEnd() { + var head: JceHead + do { + head = readHead() + skipField(head.type) + } while (head.type.toInt() != 11) + } + + @UseExperimental(ExperimentalUnsignedTypes::class) + @PublishedApi + internal fun skipField(type: Byte) = when (type.toInt()) { + 0 -> this.input.discardExact(1) + 1 -> this.input.discardExact(2) + 2 -> this.input.discardExact(4) + 3 -> this.input.discardExact(8) + 4 -> this.input.discardExact(4) + 5 -> this.input.discardExact(8) + 6 -> this.input.discardExact(this.input.readUByte().toInt()) + 7 -> this.input.discardExact(this.input.readInt()) + 8 -> { // map + val length = this.readInt(0) + var read = 0 + while (read < length * 2) { + skipField() + ++read + } + } + 9 -> { // list + val length = this.readInt(0) + var read = 0 + while (read < length) { + skipField() + ++read + } + } + 10 -> this.skipToStructEnd() + 11, 12 -> { + + } + 13 -> { + val head = readHead() + check(head.type.toInt() == 0) { "skipField with invalid type, type value: " + type + ", " + head.type } + this.input.discardExact(this.readInt(0)) + } + else -> error("invalid type.") + } +} + +private inline fun <R> JceInput.skipToTag(tag: Int, block: (JceHead) -> R): R { + return skipToTagOrNull(tag) { block(it) } ?: error("cannot find required tag $tag") +} + +@PublishedApi +internal inline fun <R> JceInput.skipToTagOrNull(tag: Int, block: (JceHead) -> R): R? { + while (true) { + if (this.input.endOfInput) + return null + + val head = readHead() + if (head.tag == tag) { + return block(head) + } + this.skipField(head.type) } } \ No newline at end of file diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceStruct.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceStruct.kt index 73ebe85b4..c02c493dc 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceStruct.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceStruct.kt @@ -1,7 +1,9 @@ package net.mamoe.mirai.qqandroid.network.io -import kotlinx.io.core.BytePacketBuilder - abstract class JceStruct { abstract fun writeTo(builder: JceOutput) + + interface Factory<out T : JceStruct> { + fun newInstanceFrom(input: JceInput): T + } } \ No newline at end of file diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/RequestPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/RequestPacket.kt index fc10c55fe..f34ad0887 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/RequestPacket.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/RequestPacket.kt @@ -1,5 +1,6 @@ package net.mamoe.mirai.qqandroid.network.protocol.jce +import net.mamoe.mirai.qqandroid.network.io.JceInput import net.mamoe.mirai.qqandroid.network.io.JceOutput import net.mamoe.mirai.qqandroid.network.io.JceStruct @@ -17,6 +18,12 @@ class RequestPacket( val sServantName: String = "", val status: Map<String, String> = EMPTY_MAP ) : JceStruct() { + companion object : Factory<RequestPacket> { + override fun newInstanceFrom(input: JceInput): RequestPacket { + TODO("not implemented") + } + } + override fun writeTo(builder: JceOutput) { builder.write(this.iVersion, 1) builder.write(this.cPacketType, 2) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/SvcReqRegister.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/SvcReqRegister.kt index 3bea4715f..31d1e8670 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/SvcReqRegister.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/SvcReqRegister.kt @@ -1,5 +1,6 @@ package net.mamoe.mirai.qqandroid.network.protocol.jce +import net.mamoe.mirai.qqandroid.network.io.JceInput import net.mamoe.mirai.qqandroid.network.io.JceOutput import net.mamoe.mirai.qqandroid.network.io.JceStruct @@ -41,7 +42,12 @@ class SvcReqRegister( val vecGuid: ByteArray? = null, val vecServerBuf: ByteArray? = null ) : JceStruct() { - + companion object : Factory<RequestPacket> { + override fun newInstanceFrom(input: JceInput): RequestPacket { + TODO("not implemented") + } + } + override fun writeTo(builder: JceOutput) { builder.write(lUin, 0) builder.write(lBid, 1) 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 f095710a9..54ed76d7c 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 @@ -8,6 +8,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.login.NullPacketId import net.mamoe.mirai.qqandroid.network.protocol.packet.login.NullPacketId.commandName import net.mamoe.mirai.qqandroid.network.protocol.packet.login.PacketId +import net.mamoe.mirai.qqandroid.network.protocol.packet.login.SvcReqRegisterPacket import net.mamoe.mirai.utils.cryptor.adjustToPublicKey import net.mamoe.mirai.utils.cryptor.decryptBy import net.mamoe.mirai.utils.io.* @@ -48,7 +49,8 @@ internal typealias PacketConsumer = suspend (packet: Packet, packetId: PacketId, @UseExperimental(ExperimentalUnsignedTypes::class) internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( - LoginPacket + LoginPacket, + SvcReqRegisterPacket ) { fun findPacketFactory(commandName: String): PacketFactory<*> = this.first { it.id.commandName == commandName } From a17f000052b841d895d6a6734d82615621b87147 Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Tue, 21 Jan 2020 14:49:42 +0800 Subject: [PATCH 12/14] f**k android --- gradle/android.gradle | 8 ++++++++ mirai-core-qqandroid/build.gradle.kts | 9 +-------- mirai-core-timpc/build.gradle.kts | 10 +--------- mirai-core/build.gradle.kts | 10 +--------- 4 files changed, 11 insertions(+), 26 deletions(-) create mode 100644 gradle/android.gradle diff --git a/gradle/android.gradle b/gradle/android.gradle new file mode 100644 index 000000000..731a77753 --- /dev/null +++ b/gradle/android.gradle @@ -0,0 +1,8 @@ +apply plugin: "com.android.library" + +android { + compileSdkVersion(29) + defaultConfig { + minSdkVersion(15) + } +} \ No newline at end of file diff --git a/mirai-core-qqandroid/build.gradle.kts b/mirai-core-qqandroid/build.gradle.kts index 2eab231a9..8ae9a0733 100644 --- a/mirai-core-qqandroid/build.gradle.kts +++ b/mirai-core-qqandroid/build.gradle.kts @@ -3,7 +3,6 @@ plugins { kotlin("multiplatform") id("kotlinx-atomicfu") - id("com.android.library") id("kotlinx-serialization") `maven-publish` id("com.jfrog.bintray") version "1.8.4-jetbrains-3" // DO NOT CHANGE THIS VERSION UNLESS YOU WANT TO WASTE YOUR TIME @@ -32,15 +31,9 @@ version = rootProject.ext.get("mirai_version")!!.toString() val isAndroidSDKAvailable: Boolean by project -android { - compileSdkVersion(29) - defaultConfig { - minSdkVersion(15) - } -} - kotlin { if (isAndroidSDKAvailable) { + apply(from = rootProject.file("gradle/android.gradle")) android("android") { publishAllLibraryVariants() } diff --git a/mirai-core-timpc/build.gradle.kts b/mirai-core-timpc/build.gradle.kts index 2ab19aa9f..3f28b9755 100644 --- a/mirai-core-timpc/build.gradle.kts +++ b/mirai-core-timpc/build.gradle.kts @@ -3,7 +3,6 @@ plugins { kotlin("multiplatform") id("kotlinx-atomicfu") - id("com.android.library") id("kotlinx-serialization") `maven-publish` id("com.jfrog.bintray") version "1.8.4-jetbrains-3" // DO NOT CHANGE THIS VERSION UNLESS YOU WANT TO WASTE YOUR TIME @@ -32,18 +31,11 @@ version = rootProject.ext.get("mirai_version")!!.toString() val isAndroidSDKAvailable: Boolean by project -android { - compileSdkVersion(29) - defaultConfig { - minSdkVersion(15) - } -} - kotlin { if (isAndroidSDKAvailable) { + apply(from = rootProject.file("gradle/android.gradle")) android("android") { publishAllLibraryVariants() - project.apply(plugin = "com.android.library") } } else { println( diff --git a/mirai-core/build.gradle.kts b/mirai-core/build.gradle.kts index cb327ba39..e8a073c18 100644 --- a/mirai-core/build.gradle.kts +++ b/mirai-core/build.gradle.kts @@ -3,7 +3,6 @@ plugins { kotlin("multiplatform") id("kotlinx-atomicfu") - id("com.android.library") id("kotlinx-serialization") `maven-publish` id("com.jfrog.bintray") version "1.8.4-jetbrains-3" // DO NOT CHANGE THIS VERSION UNLESS YOU WANT TO WASTE YOUR TIME @@ -31,16 +30,9 @@ description = "QQ protocol library" val isAndroidSDKAvailable: Boolean by project -android { - compileSdkVersion(29) - defaultConfig { - minSdkVersion(15) - } -} - kotlin { if (isAndroidSDKAvailable) { - project.apply(plugin = "com.android.library") + apply(from = rootProject.file("gradle/android.gradle")) android("android") { publishAllLibraryVariants() } From 1de776f09219003ee5487c5d1da60186530d6793 Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Tue, 21 Jan 2020 15:15:11 +0800 Subject: [PATCH 13/14] Add source project reference --- .../src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt index f67c52a75..f3c656ca1 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt @@ -14,6 +14,8 @@ import kotlin.jvm.JvmSynthetic * * Some code from http://wiki.vg/Protocol. * + * Source project: [Nukkit](http://github.com/nukkit/nukkit) + * * @author MagicDroidX of Nukkit Project * @author lmlstarqaq of Nukkit Project */ From 9353a5af7b0c61ed9e20dd32a16db7627ab6db7c Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Tue, 21 Jan 2020 21:30:43 +0800 Subject: [PATCH 14/14] QQA Debugging update --- .../mirai/qqandroid/network/io/JceInput.kt | 28 +++- .../mirai/qqandroid/network/io/JceOutput.kt | 22 +++- .../network/protocol/jce/RequestPacket.kt | 60 +++++++-- .../qqandroid/network/protocol/jce/uni.kt | 4 +- .../network/protocol/packet/PacketFactory.kt | 2 + .../packet/login/SvcReqRegisterPacket.kt | 121 ++++++++---------- .../mirai/qqandroid/utils/SystemDeviceInfo.kt | 4 +- 7 files changed, 152 insertions(+), 89 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt index cabecf645..2f931e376 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt @@ -29,6 +29,22 @@ class JceInput( return JceHead(tag = tag, type = type) } + fun read(default: Byte, tag: Int): Byte = readByteOrNull(tag) ?: default + fun read(default: Short, tag: Int): Short = readShortOrNull(tag) ?: default + fun read(default: Int, tag: Int): Int = readIntOrNull(tag) ?: default + fun read(default: Long, tag: Int): Long = readLongOrNull(tag) ?: default + fun read(default: Float, tag: Int): Float = readFloatOrNull(tag) ?: default + fun read(default: Double, tag: Int): Double = readDoubleOrNull(tag) ?: default + fun read(default: Boolean, tag: Int): Boolean = readBooleanOrNull(tag) ?: default + + fun read(default: ByteArray, tag: Int): ByteArray = readByteArrayOrNull(tag) ?: default + fun read(default: ShortArray, tag: Int): ShortArray = readShortArrayOrNull(tag) ?: default + fun read(default: IntArray, tag: Int): IntArray = readIntArrayOrNull(tag) ?: default + fun read(default: LongArray, tag: Int): LongArray = readLongArrayOrNull(tag) ?: default + fun read(default: FloatArray, tag: Int): FloatArray = readFloatArrayOrNull(tag) ?: default + fun read(default: DoubleArray, tag: Int): DoubleArray = readDoubleArrayOrNull(tag) ?: default + fun read(default: BooleanArray, tag: Int): BooleanArray = readBooleanArrayOrNull(tag) ?: default + fun readBoolean(tag: Int): Boolean = readBooleanOrNull(tag) ?: error("cannot find tag $tag") fun readByte(tag: Int): Byte = readByteOrNull(tag) ?: error("cannot find tag $tag") fun readShort(tag: Int): Short = readShortOrNull(tag) ?: error("cannot find tag $tag") @@ -176,7 +192,7 @@ class JceInput( } } - fun <T : Map<K, V>, K, V> readMapOrNull(defaultKey: K, defaultValue: V, tag: Int): Map<K, V>? = skipToTagOrNull(tag) { + fun <K, V> readMapOrNull(defaultKey: K, defaultValue: V, tag: Int): Map<K, V>? = skipToTagOrNull(tag) { check(it.type.toInt() == 8) { "type mismatch" } val size = readInt(0) val map = HashMap<K, V>(size) @@ -186,6 +202,16 @@ class JceInput( return map } + inline fun <reified K, reified V> readMapOrNull(tag: Int): Map<K, V>? = skipToTagOrNull(tag) { + check(it.type.toInt() == 8) { "type mismatch" } + val size = readInt(0) + val map = HashMap<K, V>(size) + repeat(size) { + map[readSimpleObject(0)] = readSimpleObject(0) + } + return map + } + fun <T> readListOrNull(defaultElement: T, tag: Int): List<T>? = skipToTagOrNull(tag) { head -> check(head.type.toInt() == 9) { "type mismatch" } val size = readInt(0) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceOutput.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceOutput.kt index ba352b960..9357a05a9 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceOutput.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceOutput.kt @@ -2,21 +2,31 @@ package net.mamoe.mirai.qqandroid.network.io import kotlinx.io.charsets.Charset import kotlinx.io.core.* -import kotlinx.io.pool.useInstance -import net.mamoe.mirai.utils.io.ByteArrayPool -import net.mamoe.mirai.utils.io.readRemainingBytes import kotlin.reflect.KClass -private val CharsetGBK = Charset.forName("GBK") +@PublishedApi +internal val CharsetGBK = Charset.forName("GBK") -fun buildJcePacket(stringCharset: Charset = CharsetGBK, block: JceOutput.() -> Unit): ByteReadPacket { +inline fun buildJcePacket(stringCharset: Charset = CharsetGBK, block: JceOutput.() -> Unit): ByteReadPacket { return JceOutput(stringCharset).apply(block).build() } -fun BytePacketBuilder.writeJcePacket(stringCharset: Charset = CharsetGBK, block: JceOutput.() -> Unit) { +inline fun BytePacketBuilder.writeJcePacket(stringCharset: Charset = CharsetGBK, block: JceOutput.() -> Unit) { return this.writePacket(buildJcePacket(stringCharset, block)) } +fun jceStruct(tag: Int, struct: JceStruct): ByteArray{ + return buildJcePacket { + writeJceStruct(struct, tag) + }.readBytes() +} + +fun <K, V> jceMap(tag: Int, vararg entries: Pair<K, V>): ByteArray { + return buildJcePacket { + writeMap(mapOf(*entries), tag) + }.readBytes() +} + /** * * From: com.qq.taf.jce.JceOutputStream diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/RequestPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/RequestPacket.kt index f34ad0887..649eae846 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/RequestPacket.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/RequestPacket.kt @@ -6,21 +6,55 @@ import net.mamoe.mirai.qqandroid.network.io.JceStruct private val EMPTY_MAP = mapOf<String, String>() -class RequestPacket( - val sBuffer: ByteArray, - val cPacketType: Byte = 0, - val iMessageType: Int = 0, - val iRequestId: Int = 0, - val iTimeout: Int = 3000, - val iVersion: Short = 3, - val context: Map<String, String> = EMPTY_MAP, - val sFuncName: String = "", - val sServantName: String = "", - val status: Map<String, String> = EMPTY_MAP -) : JceStruct() { +class RequestPacket() : JceStruct() { + lateinit var sBuffer: ByteArray + var cPacketType: Byte = 0 + var iMessageType: Int = 0 + var iRequestId: Int = 0 + var iTimeout: Int = 3000 + var iVersion: Short = 3 + var context: Map<String, String> = EMPTY_MAP + var sFuncName: String = "" + var sServantName: String = "" + var status: Map<String, String> = EMPTY_MAP + + constructor( + sBuffer: ByteArray, + cPacketType: Byte = 0, + iMessageType: Int = 0, + iRequestId: Int = 0, + iTimeout: Int = 3000, + iVersion: Short = 3, + context: Map<String, String> = EMPTY_MAP, + sFuncName: String = "", + sServantName: String = "", + status: Map<String, String> = EMPTY_MAP + ) : this() { + this.sBuffer = sBuffer + this.cPacketType = cPacketType + this.iMessageType = iMessageType + this.iRequestId = iRequestId + this.iTimeout = iTimeout + this.iVersion = iVersion + this.context = context + this.sFuncName = sFuncName + this.sServantName = sServantName + this.status = status + } + companion object : Factory<RequestPacket> { override fun newInstanceFrom(input: JceInput): RequestPacket { - TODO("not implemented") + val iVersion = input.readShort(1) + val cPacketType = input.readByte(2) + val iMessageType = input.readInt(3) + val iRequestId = input.readInt(4) + val sServantName = input.readString(5) + val sFuncName = input.readString(6) + val sBuffer = input.readByteArray(7) + val iTimeout = input.readInt(8) + val context = input.readMap("", "", 9) + val status = input.readMap("", "", 10) + return RequestPacket(sBuffer, cPacketType, iMessageType, iRequestId, iTimeout, iVersion, context, sFuncName, sServantName, status) } } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/uni.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/uni.kt index d68d3fb42..80d671141 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/uni.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/uni.kt @@ -5,8 +5,8 @@ import net.mamoe.mirai.qqandroid.network.io.JceOutput import net.mamoe.mirai.qqandroid.network.io.buildJcePacket import net.mamoe.mirai.qqandroid.network.io.writeJcePacket -fun BytePacketBuilder.writeUniRequestPacket(requestPacket: RequestPacket) { +inline fun BytePacketBuilder.writeUniRequestPacket(requestPacket: RequestPacket.() -> Unit) { writeJcePacket { - requestPacket.writeTo(this) + RequestPacket().apply(requestPacket).writeTo(this) } } \ No newline at end of file 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 54ed76d7c..e6f8553ae 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 @@ -81,6 +81,8 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( } else -> error("Illegal flag2. Expected 0x02, got $flag2") } + // 00 00 00 60 00 00 00 0B 02 00 00 00 00 0E 31 39 39 34 37 30 31 30 32 31 CE 35 53 19 84 A8 1A B8 5B 48 E3 7C D0 A6 BA 58 6A EB CE 50 B9 A0 98 D5 B9 D0 1C 72 E2 86 24 FC 55 44 6C 6E E3 F9 15 6C EC 6C 6B 94 40 F7 B4 45 CF B4 D0 79 84 FE 30 EA 98 84 44 84 02 32 70 DD D7 07 07 72 DE 87 59 AC + 0x0B -> else -> error("Illegal flag1. Expected 0x0A or 0x0B, got $flag1") } } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/SvcReqRegisterPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/SvcReqRegisterPacket.kt index ee3e17b3f..04dc823e9 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/SvcReqRegisterPacket.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/SvcReqRegisterPacket.kt @@ -1,13 +1,12 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.login import kotlinx.io.core.ByteReadPacket -import kotlinx.io.core.readBytes import kotlinx.serialization.protobuf.ProtoBuf import net.mamoe.mirai.data.Packet import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.network.QQAndroidClient -import net.mamoe.mirai.qqandroid.network.io.buildJcePacket -import net.mamoe.mirai.qqandroid.network.protocol.jce.RequestPacket +import net.mamoe.mirai.qqandroid.network.io.jceMap +import net.mamoe.mirai.qqandroid.network.io.jceStruct import net.mamoe.mirai.qqandroid.network.protocol.jce.SvcReqRegister import net.mamoe.mirai.qqandroid.network.protocol.jce.writeUniRequestPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket @@ -38,73 +37,63 @@ internal object SvcReqRegisterPacket : PacketFactory<SvcReqRegisterPacket.Respon operator fun invoke( client: QQAndroidClient, regPushReason: RegPushReason = RegPushReason.setOnlineStatus - ): OutgoingPacket = buildOutgingPacket(client, key = client.wLoginSigInfo.wtSessionTicketKey) { - writeUniRequestPacket( - RequestPacket( - sServantName = "PushService", - sFuncName = "SvcReqRegister", - sBuffer = buildJcePacket { - writeMap( - mapOf( - "SvcReqRegister" to buildJcePacket { - writeJceStruct( - SvcReqRegister( - cConnType = 0, - lBid = 1 or 2 or 4, - lUin = client.uin, - iStatus = client.onlineStatus.id, - bKikPC = 0, // 是否把 PC 踢下线 - bKikWeak = 0, - timeStamp = currentTimeSeconds, // millis or seconds?? - iLargeSeq = 0, - bRegType = - if (regPushReason == RegPushReason.appRegister || - regPushReason == RegPushReason.fillRegProxy || - regPushReason == RegPushReason.createDefaultRegInfo || - regPushReason == RegPushReason.setOnlineStatus - ) { - 0 - } else { - 1 - }.toByte(), - bIsSetStatus = if (regPushReason == RegPushReason.setOnlineStatus) 1 else 0, - iOSVersion = client.device.version.sdk.toLong(), - cNetType = if (client.networkType == NetworkType.WIFI) 1 else 0, - vecGuid = client.device.guid, - strDevName = client.device.model.encodeToString(), - strDevType = client.device.model.encodeToString(), - strOSVer = client.device.version.release.encodeToString(), + ): OutgoingPacket = buildOutgingPacket(client, key = client.wLoginSigInfo.d2Key) { + writeUniRequestPacket { + sServantName = "PushService" + sFuncName = "SvcReqRegister" + sBuffer = jceMap( + 0, + "SvcReqRegister" to jceStruct( + 0, + SvcReqRegister( + cConnType = 0, + lBid = 1 or 2 or 4, + lUin = client.uin, + iStatus = client.onlineStatus.id, + bKikPC = 0, // 是否把 PC 踢下线 + bKikWeak = 0, + timeStamp = currentTimeSeconds, // millis or seconds?? + iLargeSeq = 0, + bRegType = + (if (regPushReason == RegPushReason.appRegister || + regPushReason == RegPushReason.fillRegProxy || + regPushReason == RegPushReason.createDefaultRegInfo || + regPushReason == RegPushReason.setOnlineStatus + ) 0 else 1).toByte(), + bIsSetStatus = if (regPushReason == RegPushReason.setOnlineStatus) 1 else 0, + iOSVersion = client.device.version.sdk.toLong(), + cNetType = if (client.networkType == NetworkType.WIFI) 1 else 0, + vecGuid = client.device.guid, + strDevName = client.device.model.encodeToString(), + strDevType = client.device.model.encodeToString(), + strOSVer = client.device.version.release.encodeToString(), - // register 时还需要 - /* - var44.uNewSSOIp = field_127445; - var44.uOldSSOIp = field_127444; - var44.strVendorName = ROMUtil.getRomName(); - var44.strVendorOSName = ROMUtil.getRomVersion(20); - */ - bytes_0x769_reqbody = ProtoBuf.dump( - Oidb0x769.RequestBody.serializer(), Oidb0x769.RequestBody( - rpt_config_list = listOf( - Oidb0x769.ConfigSeq( - type = 46, - version = 4 - ), - Oidb0x769.ConfigSeq( - type = 283, - version = 0 - ) - ) - ) - ), - bSetMute = 0 - ), 0 + // register 时还需要 + /* + var44.uNewSSOIp = field_127445; + var44.uOldSSOIp = field_127444; + var44.strVendorName = ROMUtil.getRomName(); + var44.strVendorOSName = ROMUtil.getRomVersion(20); + */ + bytes_0x769_reqbody = ProtoBuf.dump( + Oidb0x769.RequestBody.serializer(), Oidb0x769.RequestBody( + rpt_config_list = listOf( + Oidb0x769.ConfigSeq( + type = 46, + version = 4 + ), + Oidb0x769.ConfigSeq( + type = 283, + version = 0 + ) ) - }.readBytes() - ), 0 + ) + ), + bSetMute = 0 ) - }.readBytes() + ) ) - ) + } } override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { diff --git a/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt b/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt index 8716e2b8c..e941522e9 100644 --- a/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt +++ b/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt @@ -27,7 +27,9 @@ actual class SystemDeviceInfo actual constructor(context: Context) : DeviceInfo( override val imsiMd5: ByteArray get() = ubyteArrayOf(0xD4u, 0x1Du, 0x8Cu, 0xD9u, 0x8Fu, 0x00u, 0xB2u, 0x04u, 0xE9u, 0x80u, 0x09u, 0x98u, 0xECu, 0xF8u, 0x42u, 0x7Eu).toByteArray() override val imei: String get() = "858414369211993" - override val ipAddress: ByteArray get() = localIpAddress().split(".").map { it.toByte() }.takeIf { it.size == 4 }?.toByteArray() ?: byteArrayOf() + @UseExperimental(ExperimentalUnsignedTypes::class) + override val ipAddress: ByteArray + get() = localIpAddress().split(".").map { it.toUByte().toByte() }.takeIf { it.size == 4 }?.toByteArray() ?: byteArrayOf() override val androidId: ByteArray get() = "QSR1.190920.001".toByteArray() override val apn: ByteArray get() = "wifi".toByteArray()