diff --git a/mirai-core/build.gradle.kts b/mirai-core/build.gradle.kts index fa49d77b8..0b8e087a4 100644 --- a/mirai-core/build.gradle.kts +++ b/mirai-core/build.gradle.kts @@ -121,7 +121,7 @@ kotlin { jvmTest { dependencies { implementation("org.pcap4j:pcap4j-distribution:1.8.2") - implementation("net.mamoe:mirai-login-solver-selenium:1.0-dev-14") + // implementation("net.mamoe:mirai-login-solver-selenium:1.0-dev-14") } } } diff --git a/mirai-core/src/commonMain/kotlin/network/QQAndroidBotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/network/QQAndroidBotNetworkHandler.kt index a11c2cf8b..ebbc2ea96 100644 --- a/mirai-core/src/commonMain/kotlin/network/QQAndroidBotNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/network/QQAndroidBotNetworkHandler.kt @@ -41,6 +41,9 @@ import net.mamoe.mirai.internal.network.protocol.packet.login.ConfigPushSvc import net.mamoe.mirai.internal.network.protocol.packet.login.Heartbeat import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin +import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.WtLogin2 +import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.WtLogin20 +import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.WtLogin9 import net.mamoe.mirai.internal.utils.* import net.mamoe.mirai.network.* import net.mamoe.mirai.utils.* @@ -175,12 +178,12 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo fun loginSolverNotNull() = bot.configuration.loginSolver.notnull() var response: WtLogin.Login.LoginPacketResponse = - WtLogin.Login.SubCommand9(bot.client, allowSlider).sendAndExpect() + WtLogin9(bot.client, allowSlider).sendAndExpect() mainloop@ while (true) { when (response) { is WtLogin.Login.LoginPacketResponse.UnsafeLogin -> { loginSolverNotNull().onSolveUnsafeDeviceLoginVerify(bot, response.url) - response = WtLogin.Login.SubCommand9(bot.client, allowSlider).sendAndExpect() + response = WtLogin9(bot.client, allowSlider).sendAndExpect() } is WtLogin.Login.LoginPacketResponse.Captcha -> when (response) { @@ -190,7 +193,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo //refresh captcha result = "ABCD" } - response = WtLogin.Login.SubCommand2.SubmitPictureCaptcha(bot.client, response.sign, result) + response = WtLogin2.SubmitPictureCaptcha(bot.client, response.sign, result) .sendAndExpect() continue@mainloop } @@ -224,7 +227,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo } throw error } - response = WtLogin.Login.SubCommand2.SubmitSliderCaptcha(bot.client, ticket).sendAndExpect() + response = WtLogin2.SubmitSliderCaptcha(bot.client, ticket).sendAndExpect() continue@mainloop } } @@ -243,7 +246,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo } is WtLogin.Login.LoginPacketResponse.DeviceLockLogin -> { - response = WtLogin.Login.SubCommand20( + response = WtLogin20( bot.client, response.t402 ).sendAndExpect() diff --git a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt index 66de9bf80..3edb7624c 100644 --- a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt +++ b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt @@ -307,6 +307,11 @@ internal open class QQAndroidClient( var reserveUinInfo: ReserveUinInfo? = null lateinit var wLoginSigInfo: WLoginSigInfo var tlv113: ByteArray? = null + + /** + * from tlvMap119 + */ + var tlv16a: ByteArray? = null lateinit var qrPushSig: ByteArray lateinit var mainDisplayName: ByteArray @@ -373,6 +378,9 @@ internal class LoginExtraData( internal class WLoginSigInfo( val uin: Long, val encryptA1: ByteArray?, // sigInfo[0] + /** + * WARNING, please check [QQAndroidClient.tlv16a] + */ val noPicSig: ByteArray?, // sigInfo[1] val G: ByteArray, // sigInfo[2] val dpwd: ByteArray, diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt index d23ae4498..6bb1b194a 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2020 Mamoe Technologies and contributors. + * Copyright 2019-2021 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -122,6 +122,7 @@ internal val PacketLogger: MiraiLoggerWithSwitch by lazy { internal object KnownPacketFactories { object OutgoingFactories : List> by mutableListOf( WtLogin.Login, + WtLogin.ExchangeEmp, StatSvc.Register, StatSvc.GetOnlineStatus, StatSvc.GetDevLoginInfo, diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt index 5b82aa74d..7aa646bbe 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2020 Mamoe Technologies and contributors. + * Copyright 2019-2021 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -15,10 +15,16 @@ import kotlinx.io.core.BytePacketBuilder import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.toByteArray import kotlinx.io.core.writeFully +import net.mamoe.mirai.internal.network.QQAndroidClient +import net.mamoe.mirai.internal.network.guid import net.mamoe.mirai.internal.network.protocol.LoginType +import net.mamoe.mirai.internal.utils.GuidSource +import net.mamoe.mirai.internal.utils.MacOrAndroidIdChangeFlag import net.mamoe.mirai.internal.utils.NetworkType +import net.mamoe.mirai.internal.utils.guidFlag import net.mamoe.mirai.internal.utils.io.* import net.mamoe.mirai.utils.currentTimeMillis +import net.mamoe.mirai.utils.generateDeviceInfoData import net.mamoe.mirai.utils.md5 import net.mamoe.mirai.utils.toByteArray import kotlin.random.Random @@ -79,6 +85,26 @@ internal fun BytePacketBuilder.t18( } shouldEqualsTo 22 } +internal fun BytePacketBuilder.t106( + appId: Long = 16L, + client: QQAndroidClient +) { + return t106( + appId, + client.subAppId /* maybe 1*/, + client.appClientVersion, + client.uin, + true, + client.account.passwordMd5, + 0, + client.uin.toByteArray(), + client.tgtgtKey, + true, + client.device.guid, + LoginType.PASSWORD, + client.ssoVersion + ) +} internal fun BytePacketBuilder.t106( appId: Long = 16L, @@ -291,6 +317,29 @@ internal fun BytePacketBuilder.t112( } } +internal fun BytePacketBuilder.t144( + client: QQAndroidClient +) { + return t144( + androidId = client.device.androidId, + androidDevInfo = client.device.generateDeviceInfoData(), + osType = client.device.osType, + osVersion = client.device.version.release, + networkType = client.networkType, + simInfo = client.device.simInfo, + unknown = byteArrayOf(), + apn = client.device.apn, + isGuidFromFileNull = false, + isGuidAvailable = true, + isGuidChanged = false, + guidFlag = guidFlag(GuidSource.FROM_STORAGE, MacOrAndroidIdChangeFlag(0)), + buildModel = client.device.model, + guid = client.device.guid, + buildBrand = client.device.brand, + tgtgtKey = client.tgtgtKey + ) +} + internal fun BytePacketBuilder.t144( // t109 androidId: ByteArray, @@ -499,7 +548,22 @@ internal fun BytePacketBuilder.t141( } internal fun BytePacketBuilder.t511( - domains: List + domains: List = listOf( + "tenpay.com", + "openmobile.qq.com", + "docs.qq.com", + "connect.qq.com", + "qzone.qq.com", + "vip.qq.com", + "gamecenter.qq.com", + "qun.qq.com", + "game.qq.com", + "qqweb.qq.com", + "office.qq.com", + "ti.qq.com", + "mail.qq.com", + "mma.qq.com", + ) ) { writeShort(0x511) writeShortLVPacket { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt index 0be733a5e..f90ed3195 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2020 Mamoe Technologies and contributors. + * Copyright 2019-2021 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -13,14 +13,12 @@ package net.mamoe.mirai.internal.network.protocol.packet.login import kotlinx.io.core.* import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.network.* -import net.mamoe.mirai.internal.network.protocol.LoginType import net.mamoe.mirai.internal.network.protocol.packet.* -import net.mamoe.mirai.internal.utils.GuidSource -import net.mamoe.mirai.internal.utils.MacOrAndroidIdChangeFlag +import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.WtLoginExt +import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.analysisTlv0x531 import net.mamoe.mirai.internal.utils._miraiContentToString import net.mamoe.mirai.internal.utils.crypto.TEA -import net.mamoe.mirai.internal.utils.guidFlag -import net.mamoe.mirai.internal.utils.io.writeShortLVByteArray +import net.mamoe.mirai.internal.utils.soutv import net.mamoe.mirai.utils.* internal class WtLogin { @@ -28,63 +26,7 @@ internal class WtLogin { * OicqRequest */ @Suppress("FunctionName") - internal object Login : OutgoingPacketFactory("wtlogin.login") { - /** - * 提交验证码 - */ - object SubCommand2 { - fun SubmitSliderCaptcha( - client: QQAndroidClient, - ticket: String - ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> - writeSsoPacket(client, client.subAppId, commandName, sequenceId = sequenceId) { - writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) { - writeShort(2) // subCommand - writeShort(4) // count of TLVs - t193(ticket) - t8(2052) - t104(client.t104) - t116(client.miscBitMap, client.subSigMap) - } - } - } - - fun SubmitPictureCaptcha( - client: QQAndroidClient, - captchaSign: ByteArray, - captchaAnswer: String - ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> - writeSsoPacket(client, client.subAppId, commandName, sequenceId = sequenceId) { - writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) { - writeShort(2) // subCommand - writeShort(4) // count of TLVs - t2(captchaAnswer, captchaSign, 0) - t8(2052) - t104(client.t104) - t116(client.miscBitMap, client.subSigMap) - } - } - } - } - - object SubCommand20 { - - operator fun invoke( - client: QQAndroidClient, - t402: ByteArray - ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> - writeSsoPacket(client, client.subAppId, commandName, sequenceId = sequenceId) { - writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) { - writeShort(20) // subCommand - writeShort(4) // count of TLVs, probably ignored by server? - t8(2052) - t104(client.t104) - t116(client.miscBitMap, client.subSigMap) - t401((client.device.guid + "stMNokHgxZUGhsYp".toByteArray() + t402).md5()) - } - } - } - } + internal object Login : OutgoingPacketFactory("wtlogin.login"), WtLoginExt { /** * 提交 SMS @@ -150,153 +92,6 @@ internal class WtLogin { } } - /** - * 密码登录 - */ - object SubCommand9 { - private const val appId = 16L - - operator fun invoke( - client: QQAndroidClient, - allowSlider: Boolean - ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> - writeSsoPacket(client, client.subAppId, commandName, sequenceId = sequenceId) { - writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) { - writeShort(9) // subCommand - writeShort(if (allowSlider) 0x18 else 0x17) // count of TLVs, probably ignored by server? - //writeShort(LoginType.PASSWORD.value.toShort()) - - t18(appId, client.appClientVersion, client.uin) - t1(client.uin, client.device.ipAddress) - t106( - appId, - client.subAppId /* maybe 1*/, - client.appClientVersion, - client.uin, - true, - client.account.passwordMd5, - 0, - client.uin.toByteArray(), - client.tgtgtKey, - true, - client.device.guid, - LoginType.PASSWORD, - client.ssoVersion - ) - - /* // from GetStWithPasswd - int mMiscBitmap = this.mMiscBitmap; - if (t.uinDeviceToken) { - mMiscBitmap = (this.mMiscBitmap | 0x2000000); - } - - - // defaults true - if (ConfigManager.get_loginWithPicSt()) appIdList = longArrayOf(1600000226L) - */ - t116(client.miscBitMap, client.subSigMap) - t100(appId, client.subAppId, client.appClientVersion, client.ssoVersion, client.mainSigMap) - t107(0) - t108(client.device.imei.toByteArray()) - - // t108(byteArrayOf()) - // ignored: t104() - t142(client.apkId) - - // if login with non-number uin - // t112() - t144( - androidId = client.device.androidId, - androidDevInfo = client.device.generateDeviceInfoData(), - osType = client.device.osType, - osVersion = client.device.version.release, - networkType = client.networkType, - simInfo = client.device.simInfo, - unknown = byteArrayOf(), - apn = client.device.apn, - isGuidFromFileNull = false, - isGuidAvailable = true, - isGuidChanged = false, - guidFlag = guidFlag(GuidSource.FROM_STORAGE, MacOrAndroidIdChangeFlag(0)), - buildModel = client.device.model, - guid = client.device.guid, - buildBrand = client.device.brand, - tgtgtKey = client.tgtgtKey - ) - - //this.build().debugPrint("傻逼") - t145(client.device.guid) - t147(appId, client.apkVersionName, client.apkSignatureMd5) - - /* - if (client.miscBitMap and 0x80 != 0) { - t166(1) - } - */ - - // ignored t16a because array5 is null - - t154(sequenceId) - t141(client.device.simInfo, client.networkType, client.device.apn) - t8(2052) - - t511( - listOf( - "tenpay.com", - "openmobile.qq.com", - "docs.qq.com", - "connect.qq.com", - "qzone.qq.com", - "vip.qq.com", - "gamecenter.qq.com", - "qun.qq.com", - "game.qq.com", - "qqweb.qq.com", - "office.qq.com", - "ti.qq.com", - "mail.qq.com", - "mma.qq.com", - ) - ) - - // ignored t172 because rollbackSig is null - // ignored t185 because loginType is not SMS - // ignored t400 because of first login - - t187(client.device.macAddress) - t188(client.device.androidId) - t194(client.device.imsiMd5) - if (allowSlider) { - t191() - } - - /* - t201(N = byteArrayOf())*/ - - t202(client.device.wifiBSSID, client.device.wifiSSID) - - t177( - buildTime = client.buildTime, - buildVersion = client.sdkVersion, - ) - t516() - t521() - - t525(buildPacket { - t536(buildPacket { - //com.tencent.loginsecsdk.ProtocolDet#packExtraData - writeByte(1) // const - writeByte(0) // data count - }.readBytes()) - }) - // this.build().debugPrint("傻逼") - - // ignored t318 because not logging in by QR - } - } - } - } - sealed class LoginPacketResponse : Packet { object Success : LoginPacketResponse() { @@ -376,26 +171,6 @@ internal class WtLogin { return LoginPacketResponse.UnsafeLogin(tlvMap.getOrFail(0x204).encodeToString()) } - private fun onErrorMessage(tlvMap: TlvMap): LoginPacketResponse.Error? { - return tlvMap[0x149]?.read { - discardExact(2) //type - val title: String = readUShortLVString() - val content: String = readUShortLVString() - val otherInfo: String = readUShortLVString() - - LoginPacketResponse.Error(title, content, otherInfo) - } ?: tlvMap[0x146]?.read { - discardExact(2) // ver - discardExact(2) // code - - val title = readUShortLVString() - val message = readUShortLVString() - val errorInfo = readUShortLVString() - - LoginPacketResponse.Error(title, message, errorInfo) - } - } - private fun onSolveLoginCaptcha(tlvMap: TlvMap, bot: QQAndroidBot): LoginPacketResponse.Captcha { /* java.lang.IllegalStateException: UNKNOWN CAPTCHA QUESTION: @@ -433,7 +208,7 @@ internal class WtLogin { error("UNKNOWN CAPTCHA, tlvMap=" + tlvMap._miraiContentToString()) } - private fun onLoginSuccess(tlvMap: TlvMap, bot: QQAndroidBot): LoginPacketResponse.Success { + fun onLoginSuccess(tlvMap: TlvMap, bot: QQAndroidBot): LoginPacketResponse.Success { val client = bot.client //println("TLV KEYS: " + tlvMap.keys.joinToString { it.contentToString() }) @@ -447,6 +222,9 @@ internal class WtLogin { // 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() + // added for exchange_emp sub command 15 + tlvMap119[0x16a]?.let { bot.client.tlv16a = it } + // ??? tlvMap119[0x1c]?.read { val bytes = readBytes() @@ -637,141 +415,28 @@ internal class WtLogin { return LoginPacketResponse.Success } + } - private fun TlvMap.getOrEmpty(key: Int): ByteArray { - return this[key] ?: byteArrayOf() - } + internal object ExchangeEmp : OutgoingPacketFactory("wtlogin.exchange_emp"), WtLoginExt { + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Login.LoginPacketResponse { + discardExact(2) // subCommand + // println("subCommand=$subCommand") + val type = readUByte() + // println("type=$type") - private inline fun analysisTlv0x531(t531: ByteArray, handler: (a1: ByteArray, noPicSig: ByteArray) -> Unit) { - val map = t531.toReadPacket().withUse { _readTLVMap() } - - val t106 = map[0x106] - val t16a = map[0x16a] - val t113 = map[0x113] - val t10c = map[0x10c] - - if (t106 != null && t16a != null && t113 != null && t10c != null) { - handler(t106 + t10c, t16a) - } - } - - /** - * @throws error - */ - private fun QQAndroidClient.parseWFastLoginInfoDataOutA1(t169: ByteArray): ByteReadPacket { - val map = t169.toReadPacket().withUse { _readTLVMap() } - - val t106 = map[0x106] - val t10c = map[0x10c] - val t16a = map[0x16a] - - check(t106 != null) { "getWFastLoginInfoDataOutA1: Cannot find tlv 0x106!!" } - check(t10c != null) { "getWFastLoginInfoDataOutA1: Cannot find tlv 0x10c!!" } - check(t16a != null) { "getWFastLoginInfoDataOutA1: Cannot find tlv 0x16a!!" } - - return buildPacket { - writeByte(64) - writeShort(4) - - // TLV - writeShort(0x106) - writeShortLVByteArray(t106) - - writeShort(0x10c) - writeShortLVByteArray(t10c) - - writeShort(0x16a) - writeShortLVByteArray(t16a) - - t145(device.guid) - } - } - - /** - * login extra data - */ - private fun QQAndroidClient.analysisTlv537(t537: ByteArray) = t537.read { - //discardExact(2) - loginExtraData = LoginExtraData( // args are to correct order - uin = readUInt().toLong(), - ip = readBytes(readByte().toInt() and 0xff), - time = readInt(), // correct - version = readInt() - ) - } - - /** - * pwd flag - */ - private fun QQAndroidClient.analysisTlv186(t186: ByteArray) = t186.read { - discardExact(1) - pwdFlag = readByte().toInt() == 1 - } - - /** - * 设置 [QQAndroidClient.uin] - */ - private fun QQAndroidClient.analysisTlv113(t113: ByteArray) = t113.read { - _uin = readUInt().toLong() - - /* - // nothing to do - - if (!asyncContext.ifQQLoginInQim(class_1048.productType)) { - this.field_61436.method_62330(this.field_61436.field_63973, this.field_61436.uin); - } - */ - } - - /** - * 设置 [QQAndroidClient.timeDifference] 和 [QQAndroidClient.ipFromT149] - */ - private fun QQAndroidClient.analysisTlv130(t130: ByteArray) = t130.read { discardExact(2) - timeDifference = readUInt().toLong() - currentTimeSeconds() - ipFromT149 = readBytes(4) - } - - private fun QQAndroidClient.analysisTlv150(t150: ByteArray) { - this.t150 = Tlv(t150) - } - - private fun QQAndroidClient.analysisTlv161(t161: ByteArray) { - val tlv = t161.toReadPacket().apply { discardExact(2) }.withUse { _readTLVMap() } - - tlv[0x173]?.let { analysisTlv173(it) } - tlv[0x17f]?.let { analysisTlv17f(it) } - tlv[0x172]?.let { rollbackSig = it } - } - - /** - * server host - */ - private fun QQAndroidClient.analysisTlv173(t173: ByteArray) { - t173.read { - val type = readByte() - val host = readUShortLVString() - val port = readShort() - - bot.logger.warning("服务器: host=$host, port=$port, type=$type") - // SEE oicq_request.java at method analysisT173 + val tlvMap: TlvMap = this._readTLVMap() + // tlvMap.printTLVMap() + tlvMap[0x161]?.let { bot.client.analysisTlv161(it) } + tlvMap.soutv("RESP") + return when (type.toInt()) { + 0 -> Login.onLoginSuccess(tlvMap, bot) // todo + else -> { + onErrorMessage(tlvMap) + ?: error("Cannot find error message, unknown login result type: $type, TLVMap = ${tlvMap._miraiContentToString()}") + } } } - /** - * ipv6 address - */ - private fun QQAndroidClient.analysisTlv17f(t17f: ByteArray) { - t17f.read { - val type = readByte() - val host = readUShortLVString() - val port = readShort() - - bot.logger.warning("服务器 ipv6: host=$host, port=$port, type=$type") - // SEE oicq_request.java at method analysisT17f - } - } } } - -private fun Input.readUShortLVString(): String = String(this.readUShortLVByteArray()) \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin15.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin15.kt new file mode 100644 index 000000000..1aeff7d3b --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin15.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2019-2021 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin + +import net.mamoe.mirai.internal.network.QQAndroidClient +import net.mamoe.mirai.internal.network.guid +import net.mamoe.mirai.internal.network.protocol.packet.* +import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin + +internal object WtLogin15 : WtLoginExt { + private const val subCommand = 15.toShort() + + private const val appId = 16L + + operator fun invoke( + client: QQAndroidClient, + ) = WtLogin.ExchangeEmp.buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> + writeSsoPacket(client, client.subAppId, WtLogin.ExchangeEmp.commandName, sequenceId = sequenceId) { + writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) { + writeShort(subCommand) // subCommand + writeShort(21) + + t18(16, uin = client.uin) + t1(client.uin, client.device.ipAddress) + t106(appId, client) + t116(client.miscBitMap, client.subSigMap) + t100(appId, client.subAppId, client.appClientVersion, client.ssoVersion, client.mainSigMap) + t107(0) + t142(client.apkId) + t144(client) + t145(client.device.guid) + t16a(client.tlv16a ?: byteArrayOf()) // new + t154(sequenceId) + t141(client.device.simInfo, client.networkType, client.device.apn) + t8(2052) + t511() + t147(appId, client.apkVersionName, client.apkSignatureMd5) + t177(buildTime = client.buildTime, buildVersion = client.sdkVersion) + t187(client.device.macAddress) + t188(client.device.androidId) + t194(client.device.imsiMd5) + t202(client.device.wifiBSSID, client.device.wifiSSID) + t516() + } + } + } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin2.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin2.kt new file mode 100644 index 000000000..ee12d8779 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin2.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin + +import net.mamoe.mirai.internal.network.QQAndroidClient +import net.mamoe.mirai.internal.network.protocol.packet.* +import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin + + +internal object WtLogin2 : WtLoginExt { + fun SubmitSliderCaptcha( + client: QQAndroidClient, + ticket: String + ): OutgoingPacket = WtLogin.Login.buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> + writeSsoPacket(client, client.subAppId, WtLogin.Login.commandName, sequenceId = sequenceId) { + writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) { + writeShort(2) // subCommand + writeShort(4) // count of TLVs + t193(ticket) + t8(2052) + t104(client.t104) + t116(client.miscBitMap, client.subSigMap) + } + } + } + + fun SubmitPictureCaptcha( + client: QQAndroidClient, + captchaSign: ByteArray, + captchaAnswer: String + ): OutgoingPacket = WtLogin.Login.buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> + writeSsoPacket(client, client.subAppId, WtLogin.Login.commandName, sequenceId = sequenceId) { + writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) { + writeShort(2) // subCommand + writeShort(4) // count of TLVs + t2(captchaAnswer, captchaSign, 0) + t8(2052) + t104(client.t104) + t116(client.miscBitMap, client.subSigMap) + } + } + } +} diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin20.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin20.kt new file mode 100644 index 000000000..26bb2e5a6 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin20.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin + +import kotlinx.io.core.toByteArray +import net.mamoe.mirai.internal.network.QQAndroidClient +import net.mamoe.mirai.internal.network.guid +import net.mamoe.mirai.internal.network.protocol.packet.* +import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin +import net.mamoe.mirai.utils.md5 + +internal object WtLogin20 : WtLoginExt { + operator fun invoke( + client: QQAndroidClient, + t402: ByteArray + ): OutgoingPacket = WtLogin.Login.buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> + writeSsoPacket(client, client.subAppId, WtLogin.Login.commandName, sequenceId = sequenceId) { + writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) { + writeShort(20) // subCommand + writeShort(4) // count of TLVs, probably ignored by server? + t8(2052) + t104(client.t104) + t116(client.miscBitMap, client.subSigMap) + t401((client.device.guid + "stMNokHgxZUGhsYp".toByteArray() + t402).md5()) + } + } + } +} diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin9.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin9.kt new file mode 100644 index 000000000..ff67f7f84 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin9.kt @@ -0,0 +1,114 @@ +/* + * Copyright 2019-2021 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin + +import kotlinx.io.core.buildPacket +import kotlinx.io.core.readBytes +import kotlinx.io.core.toByteArray +import net.mamoe.mirai.internal.network.QQAndroidClient +import net.mamoe.mirai.internal.network.guid +import net.mamoe.mirai.internal.network.protocol.packet.* +import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin + +internal object WtLogin9 : WtLoginExt { + private const val appId = 16L + + operator fun invoke( + client: QQAndroidClient, + allowSlider: Boolean + ): OutgoingPacket = WtLogin.Login.buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> + writeSsoPacket(client, client.subAppId, WtLogin.Login.commandName, sequenceId = sequenceId) { + writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) { + writeShort(9) // subCommand + writeShort(if (allowSlider) 0x18 else 0x17) // count of TLVs, probably ignored by server? + //writeShort(LoginType.PASSWORD.value.toShort()) + + t18(appId, client.appClientVersion, client.uin) + t1(client.uin, client.device.ipAddress) + t106(appId, client) + + /* // from GetStWithPasswd + int mMiscBitmap = this.mMiscBitmap; + if (t.uinDeviceToken) { + mMiscBitmap = (this.mMiscBitmap | 0x2000000); + } + + + // defaults true + if (ConfigManager.get_loginWithPicSt()) appIdList = longArrayOf(1600000226L) + */ + t116(client.miscBitMap, client.subSigMap) + t100(appId, client.subAppId, client.appClientVersion, client.ssoVersion, client.mainSigMap) + t107(0) + t108(client.device.imei.toByteArray()) + + // t108(byteArrayOf()) + // ignored: t104() + t142(client.apkId) + + // if login with non-number uin + // t112() + t144(client) + + //this.build().debugPrint("傻逼") + t145(client.device.guid) + t147(appId, client.apkVersionName, client.apkSignatureMd5) + + /* + if (client.miscBitMap and 0x80 != 0) { + t166(1) + } + */ + + // ignored t16a because array5 is null + + t154(sequenceId) + t141(client.device.simInfo, client.networkType, client.device.apn) + t8(2052) + + t511() + + // ignored t172 because rollbackSig is null + // ignored t185 because loginType is not SMS + // ignored t400 because of first login + + t187(client.device.macAddress) + t188(client.device.androidId) + t194(client.device.imsiMd5) + if (allowSlider) { + t191() + } + + /* + t201(N = byteArrayOf())*/ + + t202(client.device.wifiBSSID, client.device.wifiSSID) + + t177( + buildTime = client.buildTime, + buildVersion = client.sdkVersion, + ) + t516() + t521() + + t525(buildPacket { + t536(buildPacket { + //com.tencent.loginsecsdk.ProtocolDet#packExtraData + writeByte(1) // const + writeByte(0) // data count + }.readBytes()) + }) + // this.build().debugPrint("傻逼") + + // ignored t318 because not logging in by QR + } + } + } +} diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLoginExt.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLoginExt.kt new file mode 100644 index 000000000..b81b62fc8 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLoginExt.kt @@ -0,0 +1,184 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin + +import kotlinx.io.core.* +import net.mamoe.mirai.internal.network.LoginExtraData +import net.mamoe.mirai.internal.network.QQAndroidClient +import net.mamoe.mirai.internal.network.guid +import net.mamoe.mirai.internal.network.protocol.packet.Tlv +import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin +import net.mamoe.mirai.internal.network.protocol.packet.t145 +import net.mamoe.mirai.internal.network.readUShortLVByteArray +import net.mamoe.mirai.internal.utils.io.writeShortLVByteArray +import net.mamoe.mirai.utils.* + + +internal inline fun WtLoginExt.analysisTlv0x531( + t531: ByteArray, + handler: (a1: ByteArray, noPicSig: ByteArray) -> Unit +) { + val map = t531.toReadPacket().withUse { _readTLVMap() } + + val t106 = map[0x106] + val t16a = map[0x16a] + val t113 = map[0x113] + val t10c = map[0x10c] + + if (t106 != null && t16a != null && t113 != null && t10c != null) { + handler(t106 + t10c, t16a) + } +} + +internal interface WtLoginExt { // so as not to register to global extension + + fun onErrorMessage(tlvMap: TlvMap): WtLogin.Login.LoginPacketResponse.Error? { + return tlvMap[0x149]?.read { + discardExact(2) //type + val title: String = readUShortLVString() + val content: String = readUShortLVString() + val otherInfo: String = readUShortLVString() + + WtLogin.Login.LoginPacketResponse.Error(title, content, otherInfo) + } ?: tlvMap[0x146]?.read { + discardExact(2) // ver + discardExact(2) // code + + val title = readUShortLVString() + val message = readUShortLVString() + val errorInfo = readUShortLVString() + + WtLogin.Login.LoginPacketResponse.Error(title, message, errorInfo) + } + } + + fun TlvMap.getOrEmpty(key: Int): ByteArray { + return this[key] ?: byteArrayOf() + } + + /** + * @throws error + */ + fun QQAndroidClient.parseWFastLoginInfoDataOutA1(t169: ByteArray): ByteReadPacket { + val map = t169.toReadPacket().withUse { _readTLVMap() } + + val t106 = map[0x106] + val t10c = map[0x10c] + val t16a = map[0x16a] + + check(t106 != null) { "getWFastLoginInfoDataOutA1: Cannot find tlv 0x106!!" } + check(t10c != null) { "getWFastLoginInfoDataOutA1: Cannot find tlv 0x10c!!" } + check(t16a != null) { "getWFastLoginInfoDataOutA1: Cannot find tlv 0x16a!!" } + + return buildPacket { + writeByte(64) + writeShort(4) + + // TLV + writeShort(0x106) + writeShortLVByteArray(t106) + + writeShort(0x10c) + writeShortLVByteArray(t10c) + + writeShort(0x16a) + writeShortLVByteArray(t16a) + + t145(device.guid) + } + } + + /** + * login extra data + */ + fun QQAndroidClient.analysisTlv537(t537: ByteArray) = t537.read { + //discardExact(2) + loginExtraData = LoginExtraData( // args are to correct order + uin = readUInt().toLong(), + ip = readBytes(readByte().toInt() and 0xff), + time = readInt(), // correct + version = readInt() + ) + } + + /** + * pwd flag + */ + fun QQAndroidClient.analysisTlv186(t186: ByteArray) = t186.read { + discardExact(1) + pwdFlag = readByte().toInt() == 1 + } + + /** + * 设置 [QQAndroidClient.uin] + */ + fun QQAndroidClient.analysisTlv113(t113: ByteArray) = t113.read { + _uin = readUInt().toLong() + + /* + // nothing to do + + if (!asyncContext.ifQQLoginInQim(class_1048.productType)) { + this.field_61436.method_62330(this.field_61436.field_63973, this.field_61436.uin); + } + */ + } + + /** + * 设置 [QQAndroidClient.timeDifference] 和 [QQAndroidClient.ipFromT149] + */ + fun QQAndroidClient.analysisTlv130(t130: ByteArray) = t130.read { + discardExact(2) + timeDifference = readUInt().toLong() - currentTimeSeconds() + ipFromT149 = readBytes(4) + } + + fun QQAndroidClient.analysisTlv150(t150: ByteArray) { + this.t150 = Tlv(t150) + } + + fun QQAndroidClient.analysisTlv161(t161: ByteArray) { + val tlv = t161.toReadPacket().apply { discardExact(2) }.withUse { _readTLVMap() } + + tlv[0x173]?.let { analysisTlv173(it) } + tlv[0x17f]?.let { analysisTlv17f(it) } + tlv[0x172]?.let { rollbackSig = it } + } + + /** + * server host + */ + fun QQAndroidClient.analysisTlv173(t173: ByteArray) { + t173.read { + val type = readByte() + val host = readUShortLVString() + val port = readShort() + + bot.logger.warning("服务器: host=$host, port=$port, type=$type") + // SEE oicq_request.java at method analysisT173 + } + } + + /** + * ipv6 address + */ + fun QQAndroidClient.analysisTlv17f(t17f: ByteArray) { + t17f.read { + val type = readByte() + val host = readUShortLVString() + val port = readShort() + + bot.logger.warning("服务器 ipv6: host=$host, port=$port, type=$type") + // SEE oicq_request.java at method analysisT17f + } + } + + fun Input.readUShortLVString(): String = String(this.readUShortLVByteArray()) +} \ No newline at end of file