diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt index c40a49b7e..f33678d94 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt @@ -34,6 +34,7 @@ import net.mamoe.mirai.console.internal.command.CommandManagerImpl import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig import net.mamoe.mirai.console.internal.data.builtins.ConsoleDataScope import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl +import net.mamoe.mirai.console.internal.util.autoHexToBytes import net.mamoe.mirai.console.permission.BuiltInPermissionService import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.PermissionService @@ -172,14 +173,19 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI phase `auto-login bots`@{ runBlocking { - for ((id, password) in AutoLoginConfig.plainPasswords) { + for ((id, password) in AutoLoginConfig.plainPasswords.filterNot { it.key == 123456654321L }) { mainLogger.info { "Auto-login $id" } MiraiConsole.addBot(id, password).alsoLogin() } - for ((id, password) in AutoLoginConfig.md5Passwords) { + for ((id, password) in AutoLoginConfig.md5Passwords.filterNot { it.key == 123456654321L }) { mainLogger.info { "Auto-login $id" } - MiraiConsole.addBot(id, password).alsoLogin() + val x = runCatching { + password.autoHexToBytes() + }.getOrElse { + error("Bad auto-login md5: '$password'") + } + MiraiConsole.addBot(id, x).alsoLogin() } } } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/AutoLoginConfig.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/AutoLoginConfig.kt index 0e435677c..3fee86f5b 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/AutoLoginConfig.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/AutoLoginConfig.kt @@ -3,6 +3,8 @@ package net.mamoe.mirai.console.internal.data.builtins import net.mamoe.mirai.console.data.AutoSavePluginConfig import net.mamoe.mirai.console.data.ValueDescription import net.mamoe.mirai.console.data.value +import net.mamoe.mirai.console.internal.util.md5 +import net.mamoe.mirai.console.internal.util.toUHexString internal object AutoLoginConfig : AutoSavePluginConfig() { override val saveName: String @@ -13,7 +15,7 @@ internal object AutoLoginConfig : AutoSavePluginConfig() { 账号和明文密码列表 """ ) - val plainPasswords: MutableMap by value(mutableMapOf()) + val plainPasswords: MutableMap by value(mutableMapOf(123456654321L to "example")) @ValueDescription( @@ -21,5 +23,9 @@ internal object AutoLoginConfig : AutoSavePluginConfig() { 账号和 MD5 密码列表 """ ) - val md5Passwords: MutableMap by value(mutableMapOf()) + val md5Passwords: MutableMap by value( + mutableMapOf( + 123456654321L to "example".toByteArray().md5().toUHexString() + ) + ) } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/util/ByteUtils.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/util/ByteUtils.kt new file mode 100644 index 000000000..e72dba09f --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/util/ByteUtils.kt @@ -0,0 +1,54 @@ +/* + * 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.console.internal.util + +import java.security.MessageDigest + + +@Suppress("DuplicatedCode") // false positive. `this` is not the same for `List` and `ByteArray` +internal fun ByteArray.checkOffsetAndLength(offset: Int, length: Int) { + require(offset >= 0) { "offset shouldn't be negative: $offset" } + require(length >= 0) { "length shouldn't be negative: $length" } + require(offset + length <= this.size) { "offset ($offset) + length ($length) > array.size (${this.size})" } +} + +internal fun String.autoHexToBytes(): ByteArray = + this.trim(Char::isWhitespace).asSequence().chunked(2).map { + (it[0].toString() + it[1]).toUByte(16).toByte() + }.toList().toByteArray() + +internal fun ByteArray.md5(offset: Int = 0, length: Int = this.size - offset): ByteArray { + this.checkOffsetAndLength(offset, length) + return MessageDigest.getInstance("MD5").apply { update(this@md5, offset, length) }.digest() +} + +@JvmOverloads +@Suppress("DuplicatedCode") // false positive. foreach is not common to UByteArray and ByteArray +internal fun ByteArray.toUHexString( + separator: String = " ", + offset: Int = 0, + length: Int = this.size - offset +): String { + this.checkOffsetAndLength(offset, length) + if (length == 0) { + return "" + } + val lastIndex = offset + length + return buildString(length * 2) { + this@toUHexString.forEachIndexed { index, it -> + if (index in offset until lastIndex) { + var ret = it.toUByte().toString(16).toUpperCase() + if (ret.length == 1) ret = "0$ret" + append(ret) + if (index < lastIndex - 1) append(separator) + } + } + } +} diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt index e65719740..695b8cc97 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt @@ -46,36 +46,67 @@ public sealed class AbstractPermissibleIdentifier( public companion object { @JvmStatic public fun parseFromString(string: String): AbstractPermissibleIdentifier { - val str = string.trim() - objects.find { it.toString() == str }?.let { return it as AbstractPermissibleIdentifier } - for ((regex, block) in regexes) { - val result = regex.find(str) ?: continue - if (result.range.last != str.lastIndex) continue - if (result.range.first != 0) continue - return result.destructured.run(block) + val str = string.trim { it.isWhitespace() }.toLowerCase() + if (str == "console") return Console + if (str.isNotEmpty()) { + when (str[0]) { + 'g' -> { + val arg = str.substring(1) + if (arg == "*") return AnyGroup + else arg.toLongOrNull()?.let(::ExactGroup)?.let { return it } + } + 'f' -> { + val arg = str.substring(1) + if (arg == "*") return AnyFriend + else arg.toLongOrNull()?.let(::ExactFriend)?.let { return it } + } + 'u' -> { + val arg = str.substring(1) + if (arg == "*") return AnyUser + else arg.toLongOrNull()?.let(::ExactUser)?.let { return it } + } + 'c' -> { + val arg = str.substring(1) + if (arg == "*") return AnyContact + } + 'm' -> kotlin.run { + val arg = str.substring(1) + if (arg == "*") return AnyMemberFromAnyGroup + else { + val components = arg.split('.') + + if (components.size == 2) { + val groupId = components[0].toLongOrNull() ?: return@run + + if (components[1] == "*") return AnyMember(groupId) + else { + val memberId = components[1].toLongOrNull() ?: return@run + return ExactMember(groupId, memberId) + } + } + } + } + 't' -> kotlin.run { + val arg = str.substring(1) + if (arg == "*") return AnyTempFromAnyGroup + else { + val components = arg.split('.') + + if (components.size == 2) { + val groupId = components[0].toLongOrNull() ?: return@run + + if (components[1] == "*") return AnyTemp(groupId) + else { + val memberId = components[1].toLongOrNull() ?: return@run + return ExactTemp(groupId, memberId) + } + } + } + } + } } error("Cannot deserialize '$str' as AbstractPermissibleIdentifier") } - - internal val objects by lazy { - // https://youtrack.jetbrains.com/issue/KT-41782 - AbstractPermissibleIdentifier::class.nestedClasses.mapNotNull { it.objectInstance } - } - - internal val regexes: List AbstractPermissibleIdentifier>> = - listOf( - Regex("""ExactGroup\(\s*([0-9]+)\s*\)""") to { (id) -> ExactGroup(id.toLong()) }, - Regex("""ExactFriend\(\s*([0-9]+)\s*\)""") to { (id) -> ExactFriend(id.toLong()) }, - Regex("""ExactUser\(\s*([0-9]+)\s*\)""") to { (id) -> ExactUser(id.toLong()) }, - Regex("""AnyMember\(\s*([0-9]+)\s*\)""") to { (id) -> AnyMember(id.toLong()) }, - Regex("""ExactMember\(\s*([0-9]+)\s*([0-9]+)\s*\)""") to { (a, b) -> - ExactMember( - a.toLong(), - b.toLong() - ) - }, - Regex("""ExactTemp\(\s*([0-9]+)\s*([0-9]+)\s*\)""") to { (a, b) -> ExactTemp(a.toLong(), b.toLong()) }, - ) } @ConsoleExperimentalAPI @@ -86,54 +117,70 @@ public sealed class AbstractPermissibleIdentifier( ) public object AnyGroup : AbstractPermissibleIdentifier(AnyContact) { - override fun toString(): String = "AnyGroup" + override fun toString(): String = "g*" } - public data class ExactGroup(public val groupId: Long) : AbstractPermissibleIdentifier(AnyGroup) + public data class ExactGroup(public val groupId: Long) : AbstractPermissibleIdentifier(AnyGroup) { + override fun toString(): String = "g$groupId" + } - public data class AnyMember(public val groupId: Long) : AbstractPermissibleIdentifier(AnyMemberFromAnyGroup) + public data class AnyMember(public val groupId: Long) : AbstractPermissibleIdentifier(AnyMemberFromAnyGroup) { + override fun toString(): String = "m$groupId.*" + } public object AnyMemberFromAnyGroup : AbstractPermissibleIdentifier(AnyUser) { - override fun toString(): String = "AnyMemberFromAnyGroup" + override fun toString(): String = "m*" + } + + public object AnyTempFromAnyGroup : AbstractPermissibleIdentifier(AnyUser) { + override fun toString(): String = "t*" } public data class ExactMember( public val groupId: Long, public val memberId: Long - ) : AbstractPermissibleIdentifier(AnyMember(groupId), ExactUser(memberId)) + ) : AbstractPermissibleIdentifier(AnyMember(groupId), ExactUser(memberId)) { + override fun toString(): String = "m$groupId.$memberId" + } public object AnyFriend : AbstractPermissibleIdentifier(AnyUser) { - override fun toString(): String = "AnyFriend" + override fun toString(): String = "f*" } public data class ExactFriend( public val id: Long ) : AbstractPermissibleIdentifier(ExactUser(id)) { - override fun toString(): String = "ExactFriend" + override fun toString(): String = "f$id" } - public object AnyTemp : AbstractPermissibleIdentifier(AnyUser) { - override fun toString(): String = "AnyTemp" + public data class AnyTemp( + public val groupId: Long, + ) : AbstractPermissibleIdentifier(AnyUser, AnyMember(groupId)) { + override fun toString(): String = "t$groupId.*" } public data class ExactTemp( public val groupId: Long, - public val id: Long - ) : AbstractPermissibleIdentifier(ExactUser(groupId)) // TODO: 2020/9/8 ExactMember ? + public val memberId: Long + ) : AbstractPermissibleIdentifier(ExactUser(groupId), ExactMember(groupId, memberId)) { + override fun toString(): String = "t$groupId.$memberId" + } public object AnyUser : AbstractPermissibleIdentifier(AnyContact) { - override fun toString(): String = "AnyUser" + override fun toString(): String = "u*" } public data class ExactUser( public val id: Long - ) : AbstractPermissibleIdentifier(AnyUser) + ) : AbstractPermissibleIdentifier(AnyUser) { + override fun toString(): String = "u$id" + } public object AnyContact : AbstractPermissibleIdentifier() { - override fun toString(): String = "AnyContact" + override fun toString(): String = "*" } public object Console : AbstractPermissibleIdentifier() { - override fun toString(): String = "Console" + override fun toString(): String = "console" } } \ No newline at end of file