From f2b236341ab62ecfd60c0a58d534d87dee17adfe Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 16 Apr 2021 23:25:33 +0800 Subject: [PATCH] Decouple SSO login processor --- .../src/commonMain/kotlin/AbstractBot.kt | 10 +- .../src/commonMain/kotlin/QQAndroidBot.kt | 13 +- .../kotlin/network/AccountSecrets.kt | 4 +- .../kotlin/network/AccountSecretsManager.kt | 122 +++++++++ .../kotlin/network/QQAndroidClient.kt | 6 +- .../kotlin/network/handler/NetworkHandler.kt | 20 +- .../network/handler/SelectorNetworkHandler.kt | 4 +- .../handler/impl/NetworkHandlerSupport.kt | 11 +- .../handler/impl/netty/NettyNetworkHandler.kt | 24 +- .../src/commonMain/kotlin/network/keys.kt | 68 +---- .../network/net/protocol/PacketDecoder.kt | 24 +- .../network/net/protocol/SsoController.kt | 229 ----------------- .../network/net/protocol/SsoProcessor.kt | 233 ++++++++++++++++++ .../{LoginSessionAware.kt => SsoSession.kt} | 4 +- .../network/protocol/packet/PacketFactory.kt | 16 +- .../network/protocol/packet/chat/MultiMsg.kt | 4 +- .../kotlin/network/handler/testUtils.kt | 7 +- .../commonTest/kotlin/network/sessionUtils.kt | 8 +- 18 files changed, 450 insertions(+), 357 deletions(-) create mode 100644 mirai-core/src/commonMain/kotlin/network/AccountSecretsManager.kt delete mode 100644 mirai-core/src/commonMain/kotlin/network/net/protocol/SsoController.kt create mode 100644 mirai-core/src/commonMain/kotlin/network/net/protocol/SsoProcessor.kt rename mirai-core/src/commonMain/kotlin/network/net/protocol/{LoginSessionAware.kt => SsoSession.kt} (85%) diff --git a/mirai-core/src/commonMain/kotlin/AbstractBot.kt b/mirai-core/src/commonMain/kotlin/AbstractBot.kt index 2fed1e8bb..38aa77a28 100644 --- a/mirai-core/src/commonMain/kotlin/AbstractBot.kt +++ b/mirai-core/src/commonMain/kotlin/AbstractBot.kt @@ -108,7 +108,7 @@ internal abstract class AbstractBot constructor( ) { // Close network to avoid endless reconnection while network is ok // https://github.com/mamoe/mirai/issues/894 - kotlin.runCatching { network.close() } + kotlin.runCatching { network.close(null) } return@subscribeAlways } /* @@ -121,7 +121,7 @@ internal abstract class AbstractBot constructor( val cause = event.cause val msg = if (cause == null) "" else " with exception: $cause" bot.logger.info("Bot is closed manually $msg", cause) - network.close() + network.close(null) } is BotOfflineEvent.Force -> { bot.logger.info { "Connection occupied by another android device: ${event.message}" } @@ -131,7 +131,7 @@ internal abstract class AbstractBot constructor( bot.logger.info { "Reconnecting..." } // delay(3000) } else { - network.close() + network.close(null) } } is BotOfflineEvent.MsfOffline, @@ -200,7 +200,7 @@ internal abstract class AbstractBot constructor( logger.info { "Bot cancelled" + throwable?.message?.let { ": $it" }.orEmpty() } kotlin.runCatching { - network.close() + network.close(throwable) } offlineListener.cancel(CancellationException("Bot cancelled", throwable)) @@ -220,7 +220,7 @@ internal abstract class AbstractBot constructor( return } - this.network.close() + this.network.close(cause) if (supervisorJob.isActive) { if (cause == null) { diff --git a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt b/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt index 0c65aca9e..6affc54b7 100644 --- a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt +++ b/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt @@ -17,11 +17,11 @@ import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.OtherClientInfo import net.mamoe.mirai.internal.contact.OtherClientImpl import net.mamoe.mirai.internal.contact.checkIsGroupImpl -import net.mamoe.mirai.internal.contact.uin import net.mamoe.mirai.internal.network.* import net.mamoe.mirai.internal.network.handler.* import net.mamoe.mirai.internal.network.handler.impl.netty.NettyNetworkHandlerFactory -import net.mamoe.mirai.internal.network.net.protocol.SsoContext +import net.mamoe.mirai.internal.network.net.protocol.SsoProcessor +import net.mamoe.mirai.internal.network.net.protocol.SsoProcessorContextImpl import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketWithRespType import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc @@ -53,8 +53,7 @@ internal fun QQAndroidBot.createOtherClient( internal class QQAndroidBot constructor( internal val account: BotAccount, configuration: BotConfiguration -) : AbstractBot(configuration, account.id), SsoContext { - override lateinit var client: QQAndroidClient +) : AbstractBot(configuration, account.id) { override val bot: QQAndroidBot get() = this val bdhSyncer: BdhSessionSyncer = BdhSessionSyncer(this) @@ -66,12 +65,16 @@ internal class QQAndroidBot constructor( // TODO: 2021/4/14 bdhSyncer.loadFromCache() when login + private val ssoProcessor: SsoProcessor by lazy { SsoProcessor(SsoProcessorContextImpl(this)) } + + val client get() = ssoProcessor.client + override suspend fun sendLogout() { network.sendWithoutExpect(StatSvc.Register.offline(client)) } override fun createNetworkHandler(coroutineContext: CoroutineContext): NetworkHandler { - val context = NetworkHandlerContextImpl(this, this) + val context = NetworkHandlerContextImpl(this, ssoProcessor, configuration.networkLoggerSupplier(this)) return SelectorNetworkHandler( context, FactoryKeepAliveNetworkHandlerSelector(NettyNetworkHandlerFactory, serverListNew, context) diff --git a/mirai-core/src/commonMain/kotlin/network/AccountSecrets.kt b/mirai-core/src/commonMain/kotlin/network/AccountSecrets.kt index 41660da19..a2d4adc4f 100644 --- a/mirai-core/src/commonMain/kotlin/network/AccountSecrets.kt +++ b/mirai-core/src/commonMain/kotlin/network/AccountSecrets.kt @@ -20,7 +20,9 @@ import net.mamoe.mirai.utils.md5 import net.mamoe.mirai.utils.toByteArray import java.util.concurrent.CopyOnWriteArraySet - +/** + * Secrets for authentication with server. (login) + */ internal interface AccountSecrets { var wLoginSigInfoField: WLoginSigInfo? diff --git a/mirai-core/src/commonMain/kotlin/network/AccountSecretsManager.kt b/mirai-core/src/commonMain/kotlin/network/AccountSecretsManager.kt new file mode 100644 index 000000000..3d4765b97 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/network/AccountSecretsManager.kt @@ -0,0 +1,122 @@ +/* + * 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 + +import net.mamoe.mirai.Bot +import net.mamoe.mirai.internal.BotAccount +import net.mamoe.mirai.internal.utils.actualCacheDir +import net.mamoe.mirai.internal.utils.crypto.TEA +import net.mamoe.mirai.internal.utils.io.serialization.loadAs +import net.mamoe.mirai.internal.utils.io.serialization.toByteArray +import net.mamoe.mirai.utils.BotConfiguration +import net.mamoe.mirai.utils.DeviceInfo +import net.mamoe.mirai.utils.MiraiLogger +import net.mamoe.mirai.utils.info +import java.io.File + +/** + * For a [Bot]. + * + * @see MemoryAccountSecretsManager + * @see FileCacheAccountSecretsManager + * @see CombinedAccountSecretsManager + */ +internal interface AccountSecretsManager { + fun saveSecrets(account: BotAccount, secrets: AccountSecrets) + fun getSecrets(account: BotAccount): AccountSecrets? +} + +internal fun AccountSecretsManager.getSecretsOrCreate(account: BotAccount, device: DeviceInfo): AccountSecrets { + var secrets = getSecrets(account) + if (secrets == null) { + secrets = AccountSecretsImpl(device, account) + saveSecrets(account, secrets) + } + return secrets +} + +internal class MemoryAccountSecretsManager : AccountSecretsManager { + @Volatile + private var instance: AccountSecrets? = null + + @Synchronized + override fun saveSecrets(account: BotAccount, secrets: AccountSecrets) { + instance = secrets + } + + @Synchronized + override fun getSecrets(account: BotAccount): AccountSecrets? = this.instance +} + + +internal class FileCacheAccountSecretsManager( + val file: File, + val logger: MiraiLogger, +) : AccountSecretsManager { + override fun saveSecrets(account: BotAccount, secrets: AccountSecrets) { + if (secrets.wLoginSigInfoField == null) return + + file.writeBytes( + TEA.encrypt( + AccountSecretsImpl(secrets).toByteArray(AccountSecretsImpl.serializer()), + account.passwordMd5 + ) + ) + + logger.info { "Saved account secrets to local cache for fast login." } + + TEA.encrypt(file.readBytes(), account.passwordMd5).loadAs(AccountSecretsImpl.serializer()) + } + + override fun getSecrets(account: BotAccount): AccountSecrets? { + return getSecretsImpl(account) + } + + private fun getSecretsImpl(account: BotAccount): AccountSecrets? { + if (!file.exists()) return null + val loaded = kotlin.runCatching { + TEA.decrypt(file.readBytes(), account.passwordMd5).loadAs(AccountSecretsImpl.serializer()) + }.getOrElse { e -> + logger.error("Failed to load account secrets from local cache. Invalidating cache...", e) + file.delete() + return null + } + + logger.info { "Loaded account secrets from local cache." } + return loaded + } +} + +internal class CombinedAccountSecretsManager( + private val primary: AccountSecretsManager, + private val alternative: AccountSecretsManager, +) : AccountSecretsManager { + override fun saveSecrets(account: BotAccount, secrets: AccountSecrets) { + primary.saveSecrets(account, secrets) + alternative.saveSecrets(account, secrets) + } + + override fun getSecrets(account: BotAccount): AccountSecrets? { + return primary.getSecrets(account) ?: alternative.getSecrets(account) + } +} + +/** + * Create a [CombinedAccountSecretsManager] with [MemoryAccountSecretsManager] as primary and [FileCacheAccountSecretsManager] as an alternative. + */ +internal fun BotConfiguration.createAccountsSecretsManager(logger: MiraiLogger): AccountSecretsManager { + return CombinedAccountSecretsManager( + MemoryAccountSecretsManager(), + FileCacheAccountSecretsManager( + actualCacheDir().resolve("account.secrets"), + logger + ) + ) +} diff --git a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt index 6b5baad56..dfa216dba 100644 --- a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt +++ b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt @@ -22,7 +22,7 @@ import kotlinx.serialization.Serializable import net.mamoe.mirai.data.OnlineStatus import net.mamoe.mirai.internal.BotAccount import net.mamoe.mirai.internal.QQAndroidBot -import net.mamoe.mirai.internal.network.net.protocol.LoginSessionAware +import net.mamoe.mirai.internal.network.net.protocol.SsoSession import net.mamoe.mirai.internal.network.protocol.SyncingCacheList import net.mamoe.mirai.internal.network.protocol.data.jce.FileStoragePushFSSvcList import net.mamoe.mirai.internal.network.protocol.packet.Tlv @@ -73,15 +73,13 @@ internal open class QQAndroidClient( override val ecdh: ECDH = ECDH(), val device: DeviceInfo, accountSecrets: AccountSecrets -) : AccountSecrets by accountSecrets, LoginSessionAware { +) : AccountSecrets by accountSecrets, SsoSession { lateinit var _bot: QQAndroidBot val bot: QQAndroidBot get() = _bot internal var strangerSeq: Int = 0 - val keys: Map by lazy { allKeys() } - var onlineStatus: OnlineStatus = OnlineStatus.ONLINE diff --git a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandler.kt b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandler.kt index aae54607e..9fc1bbeb1 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandler.kt @@ -13,10 +13,9 @@ import net.mamoe.mirai.Bot import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.internal.network.handler.NetworkHandler.State -import net.mamoe.mirai.internal.network.net.protocol.SsoContext +import net.mamoe.mirai.internal.network.net.protocol.SsoProcessor import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketWithRespType -import net.mamoe.mirai.utils.BotConfiguration import net.mamoe.mirai.utils.MiraiLogger import java.net.InetAddress import java.net.InetSocketAddress @@ -27,23 +26,18 @@ import java.util.concurrent.CancellationException * Immutable context for [NetworkHandler] */ internal interface NetworkHandlerContext { - val bot: QQAndroidBot // // TODO: 2021/4/16 this is bad, making it difficult to write unit tests. + val bot: QQAndroidBot // however migration requires a major change. val logger: MiraiLogger - val ssoContext: SsoContext - val configuration: BotConfiguration + val ssoProcessor: SsoProcessor } internal class NetworkHandlerContextImpl( override val bot: QQAndroidBot, - override val ssoContext: SsoContext, -) : NetworkHandlerContext { - override val configuration: BotConfiguration - get() = bot.configuration - - override val logger: MiraiLogger by lazy { configuration.networkLoggerSupplier(bot) } -} + override val ssoProcessor: SsoProcessor, + override val logger: MiraiLogger, +) : NetworkHandlerContext /** * Basic interface available to application. Usually wrapped with [SelectorNetworkHandler]. @@ -116,7 +110,7 @@ internal interface NetworkHandler { /** * Closes this handler gracefully. */ - fun close() + fun close(cause: Throwable?) /////////////////////////////////////////////////////////////////////////// // compatibility diff --git a/mirai-core/src/commonMain/kotlin/network/handler/SelectorNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/network/handler/SelectorNetworkHandler.kt index 509d2895a..d58638cad 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/SelectorNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/SelectorNetworkHandler.kt @@ -39,8 +39,8 @@ internal class SelectorNetworkHandler( instance().sendAndExpect(packet, timeout, attempts) override suspend fun sendWithoutExpect(packet: OutgoingPacket) = instance().sendWithoutExpect(packet) - override fun close() { - selector.getResumedInstance()?.close() + override fun close(cause: Throwable?) { + selector.getResumedInstance()?.close(cause) } } diff --git a/mirai-core/src/commonMain/kotlin/network/handler/impl/NetworkHandlerSupport.kt b/mirai-core/src/commonMain/kotlin/network/handler/impl/NetworkHandlerSupport.kt index bad42b0e0..072153e88 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/impl/NetworkHandlerSupport.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/impl/NetworkHandlerSupport.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.* import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.internal.network.handler.NetworkHandler import net.mamoe.mirai.internal.network.handler.NetworkHandlerContext +import net.mamoe.mirai.internal.network.handler.logger import net.mamoe.mirai.internal.network.net.protocol.RawIncomingPacket import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacket import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket @@ -81,7 +82,8 @@ internal abstract class NetworkHandlerSupport( sendPacketImpl(packet) } - override fun close() { + override fun close(cause: Throwable?) { + logger.info { "NetworkHandler closed: $cause" } coroutineContext.job.cancel("NetworkHandler closed.") } @@ -138,7 +140,11 @@ internal abstract class NetworkHandlerSupport( private set final override val state: NetworkHandler.State get() = _state.correspondingState - protected inline fun setState(crossinline new: () -> BaseStateImpl) = synchronized(this) { + + /** + * You may need to call [BaseStateImpl.resumeConnection] since state is lazy. + */ + protected inline fun setState(crossinline new: () -> S): S = synchronized(this) { // we can add hooks here for debug. val impl = new() @@ -147,6 +153,7 @@ internal abstract class NetworkHandlerSupport( check(old !== impl) { "Old and new states cannot be the same." } old.cancel() _state = impl + return impl } final override suspend fun resumeConnection() { diff --git a/mirai-core/src/commonMain/kotlin/network/handler/impl/netty/NettyNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/network/handler/impl/netty/NettyNetworkHandler.kt index dd04eef8f..225c601bd 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/impl/netty/NettyNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/impl/netty/NettyNetworkHandler.kt @@ -29,7 +29,7 @@ import net.mamoe.mirai.internal.network.handler.NetworkHandlerContext import net.mamoe.mirai.internal.network.handler.impl.NetworkHandlerSupport import net.mamoe.mirai.internal.network.net.protocol.PacketCodec import net.mamoe.mirai.internal.network.net.protocol.RawIncomingPacket -import net.mamoe.mirai.internal.network.net.protocol.SsoController +import net.mamoe.mirai.internal.network.net.protocol.SsoProcessor import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.utils.childScope import java.net.SocketAddress @@ -39,11 +39,11 @@ internal class NettyNetworkHandler( context: NetworkHandlerContext, private val address: SocketAddress, ) : NetworkHandlerSupport(context) { - override fun close() { - setState { StateClosed(null) } + override fun close(cause: Throwable?) { + setState { StateClosed(cause) } } - private fun closeSuper() = super.close() + private fun closeSuper(cause: Throwable?) = super.close(cause) override suspend fun sendPacketImpl(packet: OutgoingPacket) { val state = _state as NettyState @@ -57,7 +57,7 @@ internal class NettyNetworkHandler( private inner class ByteBufToIncomingPacketDecoder : SimpleChannelInboundHandler(ByteBuf::class.java) { override fun channelRead0(ctx: ChannelHandlerContext, msg: ByteBuf) { ctx.fireChannelRead(msg.toReadPacket().use { packet -> - PacketCodec.decodeRaw(context.bot.client, packet) + PacketCodec.decodeRaw(context.ssoProcessor.ssoSession, packet) }) } } @@ -143,19 +143,24 @@ internal class NettyNetworkHandler( override suspend fun resumeConnection() { setState { StateConnecting(PacketDecodePipeline(this@NettyNetworkHandler.coroutineContext)) } + .resumeConnection() } } + /** + * 1. Connect to server. + * 2. Perform SSO login with [SsoProcessor] + * 3. If failure, set state to [StateClosed] + * 4. If success, set state to [StateOK] + */ private inner class StateConnecting( val decodePipeline: PacketDecodePipeline, ) : NettyState(NetworkHandler.State.CONNECTING) { - private val ssoController = SsoController(context.ssoContext, this@NettyNetworkHandler) - private val connection = async { createConnection(decodePipeline) } private val connectResult = async { val connection = connection.await() - ssoController.login() + context.ssoProcessor.login(this@NettyNetworkHandler) setState { StateOK(connection) } }.apply { invokeOnCompletion { error -> @@ -191,6 +196,7 @@ internal class NettyNetworkHandler( override suspend fun resumeConnection() { setState { StateConnecting(PacketDecodePipeline(this@NettyNetworkHandler.coroutineContext)) } + .resumeConnection() // the user wil } // noop } @@ -198,7 +204,7 @@ internal class NettyNetworkHandler( val exception: Throwable? ) : NettyState(NetworkHandler.State.OK) { init { - closeSuper() + closeSuper(exception) } override suspend fun sendPacketImpl(packet: OutgoingPacket) = error("NetworkHandler is already closed.") diff --git a/mirai-core/src/commonMain/kotlin/network/keys.kt b/mirai-core/src/commonMain/kotlin/network/keys.kt index bc38a595c..b222fbbb0 100644 --- a/mirai-core/src/commonMain/kotlin/network/keys.kt +++ b/mirai-core/src/commonMain/kotlin/network/keys.kt @@ -12,11 +12,6 @@ package net.mamoe.mirai.internal.network import kotlinx.io.core.ByteReadPacket import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import net.mamoe.mirai.internal.network.getRandomByteArray -import net.mamoe.mirai.internal.network.protocol.packet.PacketLogger -import net.mamoe.mirai.internal.utils.crypto.TEA -import net.mamoe.mirai.network.LoginFailedException -import net.mamoe.mirai.network.NoServerAvailableException import net.mamoe.mirai.utils.* @@ -174,65 +169,4 @@ internal open class KeyWithCreationTime( override fun toString(): String { return "KeyWithCreationTime(data=${data.toUHexString()}, creationTime=$creationTime)" } -} - -internal suspend inline fun QQAndroidClient.useNextServers(crossinline block: suspend (host: String, port: Int) -> Unit) { - if (bot.serverList.isEmpty()) { - bot.bdhSyncer.loadServerListFromCache() - if (bot.serverList.isEmpty()) { - bot.serverList.addAll(DefaultServerList) - } - } - retryCatchingExceptions(bot.serverList.size, except = LoginFailedException::class) l@{ - val pair = bot.serverList[0] - runCatchingExceptions { - block(pair.first, pair.second) - return@l - }.getOrElse { - bot.serverList.remove(pair) - if (it !is LoginFailedException) { - // 不要重复打印. - bot.logger.warning(it) - } - throw it - } - }.getOrElse { - if (it is LoginFailedException) { - throw it - } - bot.serverList.addAll(DefaultServerList) - throw NoServerAvailableException(it) - } -} - - -@Suppress("RemoveRedundantQualifierName") // bug -internal fun generateTgtgtKey(guid: ByteArray): ByteArray = - (getRandomByteArray(16) + guid).md5() - -internal inline fun QQAndroidClient.tryDecryptOrNull( - data: ByteArray, - size: Int = data.size, - mapper: (ByteArray) -> R -): R? { - keys.forEach { (key, value) -> - kotlin.runCatching { - return mapper(TEA.decrypt(data, value, size).also { PacketLogger.verbose { "成功使用 $key 解密" } }) - } - } - return null -} - -internal fun QQAndroidClient.allKeys() = mapOf( - "16 zero" to ByteArray(16), - "D2 key" to wLoginSigInfo.d2Key, - "wtSessionTicketKey" to wLoginSigInfo.wtSessionTicketKey, - "userStKey" to wLoginSigInfo.userStKey, - "tgtgtKey" to tgtgtKey, - "tgtKey" to wLoginSigInfo.tgtKey, - "deviceToken" to wLoginSigInfo.deviceToken, - "shareKeyCalculatedByConstPubKey" to ecdh.keyPair.initialShareKey - //"t108" to wLoginSigInfo.t1, - //"t10c" to t10c, - //"t163" to t163 -) +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/network/net/protocol/PacketDecoder.kt b/mirai-core/src/commonMain/kotlin/network/net/protocol/PacketDecoder.kt index f006bec9c..31a2c30e1 100644 --- a/mirai-core/src/commonMain/kotlin/network/net/protocol/PacketDecoder.kt +++ b/mirai-core/src/commonMain/kotlin/network/net/protocol/PacketDecoder.kt @@ -17,13 +17,26 @@ import net.mamoe.mirai.internal.utils.crypto.adjustToPublicKey import net.mamoe.mirai.utils.* import kotlin.io.use +/** + * Packet decoders. + * + * - Transforms [ByteReadPacket] to [RawIncomingPacket] + */ internal object PacketCodec { + /** + * 数据包相关的调试输出. + * 它默认是关闭的. + */ + internal val PacketLogger: MiraiLoggerWithSwitch by lazy { + MiraiLogger.create("Packet").withSwitch(false) + } + /** * It's caller's responsibility to close [input] * @param input received from sockets. * @return decoded */ - fun decodeRaw(client: LoginSessionAware, input: ByteReadPacket): RawIncomingPacket = input.run { + fun decodeRaw(client: SsoSession, input: ByteReadPacket): RawIncomingPacket = input.run { // login val flag1 = readInt() @@ -74,7 +87,7 @@ internal object PacketCodec { val body: ByteReadPacket, ) - private fun parseSsoFrame(client: LoginSessionAware, bytes: ByteArray): DecodeResult = + private fun parseSsoFrame(client: SsoSession, bytes: ByteArray): DecodeResult = bytes.toReadPacket().use { input -> val commandName: String val ssoSequenceId: Int @@ -139,7 +152,7 @@ internal object PacketCodec { } private fun ByteReadPacket.parseOicqResponse( - client: LoginSessionAware, + client: SsoSession, ): ByteArray { check(readByte().toInt() == 2) this.discardExact(2) @@ -215,7 +228,10 @@ internal object PacketCodec { } } -internal open class RawIncomingPacket constructor( +/** + * Represents a packet that has just been decrypted. Subsequent operation is normally passing it to a responsible [PacketFactory] according to [commandName] from [KnownPacketFactories]. + */ +internal class RawIncomingPacket constructor( val commandName: String, val sequenceId: Int, /** diff --git a/mirai-core/src/commonMain/kotlin/network/net/protocol/SsoController.kt b/mirai-core/src/commonMain/kotlin/network/net/protocol/SsoController.kt deleted file mode 100644 index 23e8bdccf..000000000 --- a/mirai-core/src/commonMain/kotlin/network/net/protocol/SsoController.kt +++ /dev/null @@ -1,229 +0,0 @@ -/* - * 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.net.protocol - -import net.mamoe.mirai.internal.network.AccountSecrets -import net.mamoe.mirai.internal.network.AccountSecretsImpl -import net.mamoe.mirai.internal.network.Packet -import net.mamoe.mirai.internal.network.QQAndroidClient -import net.mamoe.mirai.internal.network.handler.NetworkHandler -import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketWithRespType -import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin.Login.LoginPacketResponse -import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin.Login.LoginPacketResponse.Captcha -import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.WtLogin10 -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.network.protocol.packet.sendAndExpect -import net.mamoe.mirai.internal.utils.crypto.TEA -import net.mamoe.mirai.internal.utils.io.serialization.loadAs -import net.mamoe.mirai.network.* -import net.mamoe.mirai.utils.* -import net.mamoe.mirai.utils.BotConfiguration.MiraiProtocol -import java.io.File - -internal interface SsoContext { - var client: QQAndroidClient - val configuration: BotConfiguration - val loginSessionAware: LoginSessionAware get() = client - val accountSecrets: AccountSecrets get() = client -} - -internal class SsoController( - private val ssoContext: SsoContext, - private val handler: NetworkHandler, -) { - @Throws(LoginFailedException::class) - suspend fun login() = withExceptionCollector { - if (ssoContext.accountSecrets.wLoginSigInfoInitialized) { - kotlin.runCatching { - fastLogin() - }.onFailure { e -> - collectException(e) - slowLogin() - } - } else { - slowLogin() - } - } - - /////////////////////////////////////////////////////////////////////////// - // impl - /////////////////////////////////////////////////////////////////////////// - - private val configuration get() = handler.context.configuration - private val context get() = handler.context - private val bot get() = context.bot - private val logger get() = bot.logger - private val account get() = bot.account - - - private suspend fun fastLogin() { - val login10 = WtLogin10(bot.client).sendAndExpect(bot) - check(login10 is LoginPacketResponse.Success) { "Fast login failed: $login10" } - } - - private fun loginSolverNotNull(): LoginSolver { - fun LoginSolver?.notnull(): LoginSolver { - checkNotNull(this) { - "No LoginSolver found. Please provide by BotConfiguration.loginSolver. " + - "For example use `BotFactory.newBot(...) { loginSolver = yourLoginSolver}` in Kotlin, " + - "use `BotFactory.newBot(..., new BotConfiguration() {{ setLoginSolver(yourLoginSolver) }})` in Java." - } - return this - } - - return bot.configuration.loginSolver.notnull() - } - - private val sliderSupported get() = bot.configuration.loginSolver?.isSliderCaptchaSupported ?: false - - private fun createUnsupportedSliderCaptchaException(allowSlider: Boolean): UnsupportedSliderCaptchaException { - return UnsupportedSliderCaptchaException( - buildString { - append("Mirai 无法完成滑块验证.") - if (allowSlider) { - append(" 使用协议 ") - append(configuration.protocol) - append(" 强制要求滑块验证, 请更换协议后重试.") - } - append(" 另请参阅: https://github.com/project-mirai/mirai-login-solver-selenium") - } - ) - } - - private suspend fun slowLogin() = withExceptionCollector { - - var allowSlider = sliderSupported || bot.configuration.protocol == MiraiProtocol.ANDROID_PHONE - - var response: LoginPacketResponse = WtLogin9(bot.client, allowSlider).sendAndExpect() - - mainloop@ while (true) { - when (response) { - is LoginPacketResponse.Success -> { - logger.info { "Login successful" } - break@mainloop - } - is LoginPacketResponse.DeviceLockLogin -> { - response = WtLogin20(bot.client).sendAndExpect(bot) - } - - is LoginPacketResponse.UnsafeLogin -> { - loginSolverNotNull().onSolveUnsafeDeviceLoginVerify(bot, response.url) - response = WtLogin9(bot.client, allowSlider).sendAndExpect() - } - - is Captcha.Picture -> { - var result = loginSolverNotNull().onSolvePicCaptcha(bot, response.data) - if (result == null || result.length != 4) { - //refresh captcha - result = "ABCD" - } - response = WtLogin2.SubmitPictureCaptcha(bot.client, response.sign, result).sendAndExpect() - } - - is Captcha.Slider -> { - if (sliderSupported) { - // use solver - val ticket = try { - loginSolverNotNull().onSolveSliderCaptcha(bot, response.url)?.takeIf { it.isNotEmpty() } - } catch (e: LoginFailedException) { - throw e - } catch (error: Throwable) { - if (allowSlider) { - collectException(error) - allowSlider = false - response = WtLogin9(bot.client, allowSlider).sendAndExpect() - continue@mainloop - } - throw error - } - response = if (ticket == null) { - WtLogin9(bot.client, allowSlider).sendAndExpect() - } else { - WtLogin2.SubmitSliderCaptcha(bot.client, ticket).sendAndExpect() - } - } else { - // retry once - if (!allowSlider) throw createUnsupportedSliderCaptchaException(allowSlider) - allowSlider = false - response = WtLogin9(bot.client, allowSlider).sendAndExpect() - } - } - - is LoginPacketResponse.Error -> { - if (response.message.contains("0x9a")) { //Error(title=登录失败, message=请你稍后重试。(0x9a), errorInfo=) - throw RetryLaterException().initCause(IllegalStateException("Login failed: $response")) - } - val msg = response.toString() - throw WrongPasswordException(buildString(capacity = msg.length) { - append(msg) - if (msg.contains("当前上网环境异常")) { // Error(title=禁止登录, message=当前上网环境异常,请更换网络环境或在常用设备上登录或稍后再试。, errorInfo=) - append(", tips=若频繁出现, 请尝试开启设备锁") - } - }) - } - - is LoginPacketResponse.SMSVerifyCodeNeeded -> { - val message = "SMS required: $response, which isn't yet supported" - logger.error(message) - throw UnsupportedSMSLoginException(message) - } - } - } - - } - - @Suppress("unused") // false positive - internal fun initClient() { - val device = configuration.deviceInfo?.invoke(bot) ?: DeviceInfo.random() - ssoContext.client = QQAndroidClient( - bot.account, - device = device, - accountSecrets = loadSecretsFromCacheOrCreate(device) - ).apply { - _bot = this@SsoController.bot - } - } - - private suspend inline fun OutgoingPacketWithRespType.sendAndExpect(): R = sendAndExpect(bot) - - /////////////////////////////////////////////////////////////////////////// - // cache - /////////////////////////////////////////////////////////////////////////// - - // TODO: 2021/4/14 extract a cache service - - private val cacheDir: File by lazy { - configuration.workingDir.resolve(ssoContext.configuration.cacheDir).apply { mkdirs() } - } - private val accountSecretsFile: File by lazy { - cacheDir.resolve("account.secrets") - } - - private fun loadSecretsFromCacheOrCreate(deviceInfo: DeviceInfo): AccountSecrets { - val loaded = if (configuration.loginCacheEnabled && accountSecretsFile.exists()) { - kotlin.runCatching { - TEA.decrypt(accountSecretsFile.readBytes(), account.passwordMd5).loadAs(AccountSecretsImpl.serializer()) - }.getOrElse { e -> - logger.error("Failed to load account secrets from local cache. Invalidating cache...", e) - accountSecretsFile.delete() - null - } - } else null - if (loaded != null) { - logger.info { "Loaded account secrets from local cache." } - return loaded - } - - return AccountSecretsImpl(deviceInfo, account) // wLoginSigInfoField is null, no need to save. - } - -} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/network/net/protocol/SsoProcessor.kt b/mirai-core/src/commonMain/kotlin/network/net/protocol/SsoProcessor.kt new file mode 100644 index 000000000..0e2827123 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/network/net/protocol/SsoProcessor.kt @@ -0,0 +1,233 @@ +/* + * 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.net.protocol + +import net.mamoe.mirai.internal.BotAccount +import net.mamoe.mirai.internal.QQAndroidBot +import net.mamoe.mirai.internal.network.* +import net.mamoe.mirai.internal.network.handler.NetworkHandler +import net.mamoe.mirai.internal.network.handler.impl.netty.NettyNetworkHandler +import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketWithRespType +import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin.Login.LoginPacketResponse +import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin.Login.LoginPacketResponse.Captcha +import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.WtLogin10 +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.network.protocol.packet.sendAndExpect +import net.mamoe.mirai.network.* +import net.mamoe.mirai.utils.* +import net.mamoe.mirai.utils.BotConfiguration.MiraiProtocol + +/** + * Provides the information needed by the [SsoProcessor]. + */ +internal interface SsoProcessorContext { + val bot: QQAndroidBot + + val account: BotAccount + val device: DeviceInfo + + val protocol: MiraiProtocol + + val accountSecretsManager: AccountSecretsManager + + val configuration: BotConfiguration +} + +internal class SsoProcessorContextImpl( + override val bot: QQAndroidBot +) : SsoProcessorContext { + override val account: BotAccount get() = bot.account + override val device: DeviceInfo = configuration.deviceInfo?.invoke(bot) ?: DeviceInfo.random() + override val protocol: MiraiProtocol get() = configuration.protocol + override val accountSecretsManager: AccountSecretsManager get() = configuration.createAccountsSecretsManager(bot.logger) + override val configuration: BotConfiguration get() = bot.configuration +} + +/** + * Strategy that performs the process of single sing-on (SSO). (login) + * + * And allows to retire the [session][ssoSession] after success. + * + * Used by [NettyNetworkHandler.StateConnecting]. + */ +internal class SsoProcessor( + private val ssoContext: SsoProcessorContext, +) { + @Volatile + internal var client = createClient(ssoContext.bot) + + internal val ssoSession: SsoSession get() = client + + /** + * Do login. Throws [LoginFailedException] if failed + */ + @Throws(LoginFailedException::class) + suspend fun login(handler: NetworkHandler) = withExceptionCollector { + if (client.wLoginSigInfoInitialized) { + kotlin.runCatching { + FastLoginImpl(handler).doLogin() + }.onFailure { e -> + collectException(e) + SlowLoginImpl(handler).doLogin() + } + } else { + client = createClient(ssoContext.bot) + SlowLoginImpl(handler).doLogin() + } + } + + private fun createClient(bot: QQAndroidBot): QQAndroidClient { + val device = ssoContext.device + return QQAndroidClient( + ssoContext.account, + device = device, + accountSecrets = ssoContext.accountSecretsManager.getSecretsOrCreate(ssoContext.account, device) + ).apply { + _bot = bot + } + } + + // we have exactly two methods----slow and fast. + + private abstract inner class LoginStrategy( + val handler: NetworkHandler, + ) { + protected val context get() = handler.context + protected val bot get() = context.bot + protected val logger get() = bot.logger + + protected suspend fun OutgoingPacketWithRespType.sendAndExpect(): R = sendAndExpect(handler) + + abstract suspend fun doLogin() + } + + private inner class SlowLoginImpl(handler: NetworkHandler) : LoginStrategy(handler) { + + private fun loginSolverNotNull(): LoginSolver { + fun LoginSolver?.notnull(): LoginSolver { + checkNotNull(this) { + "No LoginSolver found. Please provide by BotConfiguration.loginSolver. " + + "For example use `BotFactory.newBot(...) { loginSolver = yourLoginSolver}` in Kotlin, " + + "use `BotFactory.newBot(..., new BotConfiguration() {{ setLoginSolver(yourLoginSolver) }})` in Java." + } + return this + } + + return bot.configuration.loginSolver.notnull() + } + + private val sliderSupported get() = bot.configuration.loginSolver?.isSliderCaptchaSupported ?: false + + private fun createUnsupportedSliderCaptchaException(allowSlider: Boolean): UnsupportedSliderCaptchaException { + return UnsupportedSliderCaptchaException( + buildString { + append("Mirai 无法完成滑块验证.") + if (allowSlider) { + append(" 使用协议 ") + append(ssoContext.protocol) + append(" 强制要求滑块验证, 请更换协议后重试.") + } + append(" 另请参阅: https://github.com/project-mirai/mirai-login-solver-selenium") + } + ) + } + + override suspend fun doLogin() = withExceptionCollector { + + var allowSlider = sliderSupported || bot.configuration.protocol == MiraiProtocol.ANDROID_PHONE + + var response: LoginPacketResponse = WtLogin9(client, allowSlider).sendAndExpect() + + mainloop@ while (true) { + when (response) { + is LoginPacketResponse.Success -> { + logger.info { "Login successful" } + break@mainloop + } + is LoginPacketResponse.DeviceLockLogin -> { + response = WtLogin20(client).sendAndExpect() + } + + is LoginPacketResponse.UnsafeLogin -> { + loginSolverNotNull().onSolveUnsafeDeviceLoginVerify(bot, response.url) + response = WtLogin9(client, allowSlider).sendAndExpect() + } + + is Captcha.Picture -> { + var result = loginSolverNotNull().onSolvePicCaptcha(bot, response.data) + if (result == null || result.length != 4) { + //refresh captcha + result = "ABCD" + } + response = WtLogin2.SubmitPictureCaptcha(client, response.sign, result).sendAndExpect() + } + + is Captcha.Slider -> { + if (sliderSupported) { + // use solver + val ticket = try { + loginSolverNotNull().onSolveSliderCaptcha(bot, response.url)?.takeIf { it.isNotEmpty() } + } catch (e: LoginFailedException) { + throw e + } catch (error: Throwable) { + if (allowSlider) { + collectException(error) + allowSlider = false + response = WtLogin9(client, allowSlider).sendAndExpect() + continue@mainloop + } + throw error + } + response = if (ticket == null) { + WtLogin9(client, allowSlider).sendAndExpect() + } else { + WtLogin2.SubmitSliderCaptcha(client, ticket).sendAndExpect() + } + } else { + // retry once + if (!allowSlider) throw createUnsupportedSliderCaptchaException(allowSlider) + allowSlider = false + response = WtLogin9(client, allowSlider).sendAndExpect() + } + } + + is LoginPacketResponse.Error -> { + if (response.message.contains("0x9a")) { //Error(title=登录失败, message=请你稍后重试。(0x9a), errorInfo=) + throw RetryLaterException().initCause(IllegalStateException("Login failed: $response")) + } + val msg = response.toString() + throw WrongPasswordException(buildString(capacity = msg.length) { + append(msg) + if (msg.contains("当前上网环境异常")) { // Error(title=禁止登录, message=当前上网环境异常,请更换网络环境或在常用设备上登录或稍后再试。, errorInfo=) + append(", tips=若频繁出现, 请尝试开启设备锁") + } + }) + } + + is LoginPacketResponse.SMSVerifyCodeNeeded -> { + val message = "SMS required: $response, which isn't yet supported" + logger.error(message) + throw UnsupportedSMSLoginException(message) + } + } + } + + } + } + + private inner class FastLoginImpl(handler: NetworkHandler) : LoginStrategy(handler) { + override suspend fun doLogin() { + val login10 = WtLogin10(client).sendAndExpect(handler) + check(login10 is LoginPacketResponse.Success) { "Fast login failed: $login10" } + } + } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/network/net/protocol/LoginSessionAware.kt b/mirai-core/src/commonMain/kotlin/network/net/protocol/SsoSession.kt similarity index 85% rename from mirai-core/src/commonMain/kotlin/network/net/protocol/LoginSessionAware.kt rename to mirai-core/src/commonMain/kotlin/network/net/protocol/SsoSession.kt index 3d6fdf2a3..f3e4748fb 100644 --- a/mirai-core/src/commonMain/kotlin/network/net/protocol/LoginSessionAware.kt +++ b/mirai-core/src/commonMain/kotlin/network/net/protocol/SsoSession.kt @@ -14,9 +14,11 @@ import net.mamoe.mirai.internal.network.WLoginSigInfo import net.mamoe.mirai.internal.utils.crypto.ECDH /** + * Contains secrets for encryption and decryption during a session created by [SsoProcessor] and [PacketCodec]. + * * @see AccountSecrets */ -internal interface LoginSessionAware { +internal interface SsoSession { var outgoingPacketSessionId: 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 006856f92..1d29af0f2 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt @@ -13,6 +13,7 @@ import kotlinx.io.core.ByteReadPacket import net.mamoe.mirai.event.Event import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.network.Packet +import net.mamoe.mirai.internal.network.net.protocol.PacketCodec import net.mamoe.mirai.internal.network.protocol.packet.chat.* import net.mamoe.mirai.internal.network.protocol.packet.chat.image.ImgStore import net.mamoe.mirai.internal.network.protocol.packet.chat.image.LongConn @@ -26,9 +27,7 @@ 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.summarycard.SummaryCard -import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.MiraiLoggerWithSwitch -import net.mamoe.mirai.utils.withSwitch internal sealed class PacketFactory { /** @@ -112,11 +111,18 @@ internal suspend inline fun

IncomingPacketFactory

.decode( * 数据包相关的调试输出. * 它默认是关闭的. */ +@Deprecated( + "Kept for binary compatibility.", + ReplaceWith("PacketCodec.PacketLogger", "net.mamoe.mirai.internal.network.net.protocol.PacketCodec"), + level = DeprecationLevel.ERROR, +) @PublishedApi -internal val PacketLogger: MiraiLoggerWithSwitch by lazy { - MiraiLogger.create("Packet").withSwitch(false) -} +internal val PacketLogger: MiraiLoggerWithSwitch + get() = PacketCodec.PacketLogger +/** + * Registered factories. + */ internal object KnownPacketFactories { object OutgoingFactories : List> by mutableListOf( WtLogin.Login, diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/MultiMsg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/MultiMsg.kt index e5331d4f0..51151dd01 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/MultiMsg.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/MultiMsg.kt @@ -19,13 +19,13 @@ import net.mamoe.mirai.internal.message.contextualBugReportException import net.mamoe.mirai.internal.message.toRichTextElems import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.internal.network.QQAndroidClient +import net.mamoe.mirai.internal.network.net.protocol.PacketCodec import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm import net.mamoe.mirai.internal.network.protocol.data.proto.MsgTransmit import net.mamoe.mirai.internal.network.protocol.data.proto.MultiMsg import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketFactory -import net.mamoe.mirai.internal.network.protocol.packet.PacketLogger import net.mamoe.mirai.internal.network.protocol.packet.buildOutgoingUniPacket import net.mamoe.mirai.internal.utils._miraiContentToString import net.mamoe.mirai.internal.utils.io.serialization.readProtoBuf @@ -118,7 +118,7 @@ internal class MultiMsg { val proto: MultiMsg.MultiMsgApplyUpRsp ) : Response() { override fun toString(): String { - if (PacketLogger.isEnabled) { + if (PacketCodec.PacketLogger.isEnabled) { return _miraiContentToString() } return "MultiMsg.ApplyUp.Response.RequireUpload" diff --git a/mirai-core/src/commonTest/kotlin/network/handler/testUtils.kt b/mirai-core/src/commonTest/kotlin/network/handler/testUtils.kt index 970574074..9e9b8c68d 100644 --- a/mirai-core/src/commonTest/kotlin/network/handler/testUtils.kt +++ b/mirai-core/src/commonTest/kotlin/network/handler/testUtils.kt @@ -13,9 +13,9 @@ import kotlinx.coroutines.CompletableDeferred import net.mamoe.mirai.internal.MockBot import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.network.handler.impl.NetworkHandlerSupport -import net.mamoe.mirai.internal.network.net.protocol.SsoContext +import net.mamoe.mirai.internal.network.net.protocol.SsoProcessor +import net.mamoe.mirai.internal.network.net.protocol.SsoProcessorContextImpl import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket -import net.mamoe.mirai.utils.BotConfiguration import net.mamoe.mirai.utils.MiraiLogger import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.atomic.AtomicInteger @@ -24,8 +24,7 @@ import java.util.concurrent.atomic.AtomicInteger internal class TestNetworkHandlerContext( override val bot: QQAndroidBot = MockBot(), override val logger: MiraiLogger = MiraiLogger.create("Test"), - override val ssoContext: SsoContext = bot, - override val configuration: BotConfiguration = bot.configuration + override val ssoProcessor: SsoProcessor = SsoProcessor(SsoProcessorContextImpl(bot)), ) : NetworkHandlerContext internal open class TestNetworkHandler( diff --git a/mirai-core/src/commonTest/kotlin/network/sessionUtils.kt b/mirai-core/src/commonTest/kotlin/network/sessionUtils.kt index 6c226f20f..3a4e36138 100644 --- a/mirai-core/src/commonTest/kotlin/network/sessionUtils.kt +++ b/mirai-core/src/commonTest/kotlin/network/sessionUtils.kt @@ -11,7 +11,7 @@ package net.mamoe.mirai.internal.network import net.mamoe.mirai.event.events.BotOnlineEvent import net.mamoe.mirai.internal.QQAndroidBot -import net.mamoe.mirai.internal.network.net.protocol.LoginSessionAware +import net.mamoe.mirai.internal.network.net.protocol.SsoSession import net.mamoe.mirai.internal.utils.crypto.ECDH import net.mamoe.mirai.internal.utils.io.serialization.loadAs import net.mamoe.mirai.internal.utils.io.serialization.toByteArray @@ -20,12 +20,12 @@ import net.mamoe.mirai.utils.debug import net.mamoe.mirai.utils.withUse import java.io.File -internal class TestLoginSessionAware( +internal class TestSsoSession( private val accountSecrets: AccountSecrets, override var outgoingPacketSessionId: ByteArray = byteArrayOf(1, 2, 3, 4), override var loginState: Int = 0, override val ecdh: ECDH = ECDH(), -) : LoginSessionAware { +) : SsoSession { override var wLoginSigInfo: WLoginSigInfo by accountSecrets::wLoginSigInfo override val randomKey: ByteArray by accountSecrets::randomKey } @@ -39,7 +39,7 @@ internal fun loadSession( } /** - * secure to share with others. + * Secure to share with others. Designed to save real data for tests. */ internal fun QQAndroidClient.dumpSessionSafe(): ByteArray { val secrets =