From fa364b4b4524caf7508beb91bd15aadffeb73b33 Mon Sep 17 00:00:00 2001
From: Karlatemp <karlatemp@vip.qq.com>
Date: Sun, 7 Nov 2021 18:55:02 +0800
Subject: [PATCH] Reject sending `FileMessage` (#1657)

* Reject sending `FileMessage`

* Update docs

* Update mirai-core-api/src/commonMain/kotlin/contact/file/AbsoluteFile.kt

Co-authored-by: Him188 <Him188@mamoe.net>

Co-authored-by: Him188 <Him188@mamoe.net>
---
 .../commonMain/kotlin/contact/file/AbsoluteFile.kt |  4 ++--
 .../commonMain/kotlin/message/data/FileMessage.kt  |  8 +++++---
 .../src/commonMain/kotlin/utils/RemoteFile.kt      |  2 +-
 .../kotlin/contact/SendMessageHandler.kt           |  1 +
 .../kotlin/contact/file/AbsoluteFolderImpl.kt      |  5 +++--
 mirai-core/src/commonMain/kotlin/contact/util.kt   | 14 ++++++++++++++
 .../kotlin/message/ForceAsLongMessage.kt           |  9 +++++++++
 .../src/commonMain/kotlin/utils/RemoteFileImpl.kt  |  4 ++--
 8 files changed, 37 insertions(+), 10 deletions(-)

diff --git a/mirai-core-api/src/commonMain/kotlin/contact/file/AbsoluteFile.kt b/mirai-core-api/src/commonMain/kotlin/contact/file/AbsoluteFile.kt
index fe3a9bbb5..50bb566b1 100644
--- a/mirai-core-api/src/commonMain/kotlin/contact/file/AbsoluteFile.kt
+++ b/mirai-core-api/src/commonMain/kotlin/contact/file/AbsoluteFile.kt
@@ -65,9 +65,9 @@ public interface AbsoluteFile : AbsoluteFileFolder {
     public suspend fun getUrl(): String?
 
     /**
-     * 得到表示远程文件的可以发送的 [FileMessage].
+     * 得到 [AbsoluteFile] 所对应的 [FileMessage].
      *
-     * 在 [上传文件][RemoteFiles.uploadNewFile] 时就已经发送了文件消息. [toMessage] 可供之后再次发送使用.
+     * 注: 在 [上传文件][RemoteFiles.uploadNewFile] 时就已经发送了文件消息, [FileMessage] 不可手动发送
      */
     public fun toMessage(): FileMessage
 
diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/FileMessage.kt b/mirai-core-api/src/commonMain/kotlin/message/data/FileMessage.kt
index 91a440d13..a42bd0b7b 100644
--- a/mirai-core-api/src/commonMain/kotlin/message/data/FileMessage.kt
+++ b/mirai-core-api/src/commonMain/kotlin/message/data/FileMessage.kt
@@ -30,10 +30,12 @@ import net.mamoe.mirai.utils.*
  *
  * [name] 与 [size] 只供本地使用, 发送消息时只会使用 [id] 和 [internalId].
  *
- * ### 文件操作
- * 要下载这个文件, 可通过 [toRemoteFile] 获取到 [RemoteFile] 然后操作.
+ * 注: [FileMessage] 不可二次发送
  *
- * 要获取到 [FileMessage], 可以通过 [MessageEvent.message] 获取, 或通过 [RemoteFile.upload] 上传.
+ * ### 文件操作
+ * 要下载这个文件, 可通过 [toAbsoluteFile] 获取到 [AbsoluteFile] 然后操作.
+ *
+ * 要获取到 [FileMessage], 可以通过 [MessageEvent.message] 获取, 或通过 [AbsoluteFile.toMessage] 得到.
  *
  * @since 2.5
  * @suppress [FileMessage] 的使用是稳定的, 但自行实现不稳定.
diff --git a/mirai-core-api/src/commonMain/kotlin/utils/RemoteFile.kt b/mirai-core-api/src/commonMain/kotlin/utils/RemoteFile.kt
index 572597499..39114f386 100644
--- a/mirai-core-api/src/commonMain/kotlin/utils/RemoteFile.kt
+++ b/mirai-core-api/src/commonMain/kotlin/utils/RemoteFile.kt
@@ -337,7 +337,7 @@ public interface RemoteFile {
     public suspend fun listFilesCollection(): List<RemoteFile> = listFiles().toList()
 
     /**
-     * 得到相应文件消息, 可以发送. 当 [RemoteFile] 表示一个目录或文件不存在时返回 `null`.
+     * 得到相应文件消息. 当 [RemoteFile] 表示一个目录或文件不存在时返回 `null`.
      */
     public suspend fun toMessage(): FileMessage?
 
diff --git a/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt b/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt
index f65a2c1fc..d8c228fb2 100644
--- a/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt
+++ b/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt
@@ -331,6 +331,7 @@ private suspend fun <C : Contact> SendMessageHandler<C>.sendMessageImpl(
     transformedMessage: MessageChain,
     step: SendMessageStep,
 ): MessageReceipt<C> { // Result cannot be in interface.
+    transformedMessage.verifySendingValid()
     val chain = transformedMessage.convertToLongMessageIfNeeded(step)
 
     chain.findIsInstance<QuoteReply>()?.source?.ensureSequenceIdAvailable()
diff --git a/mirai-core/src/commonMain/kotlin/contact/file/AbsoluteFolderImpl.kt b/mirai-core/src/commonMain/kotlin/contact/file/AbsoluteFolderImpl.kt
index b478f2ce9..3ab2f3afd 100644
--- a/mirai-core/src/commonMain/kotlin/contact/file/AbsoluteFolderImpl.kt
+++ b/mirai-core/src/commonMain/kotlin/contact/file/AbsoluteFolderImpl.kt
@@ -18,6 +18,7 @@ import net.mamoe.mirai.contact.file.AbsoluteFolder
 import net.mamoe.mirai.contact.isOperator
 import net.mamoe.mirai.internal.contact.GroupImpl
 import net.mamoe.mirai.internal.contact.file.RemoteFilesImpl.Companion.findFileByPath
+import net.mamoe.mirai.internal.message.MiraiInternalMessageFlag
 import net.mamoe.mirai.internal.network.QQAndroidClient
 import net.mamoe.mirai.internal.network.components.ClockHolder.Companion.clock
 import net.mamoe.mirai.internal.network.highway.Highway
@@ -171,7 +172,7 @@ internal class AbsoluteFolderImpl(
                     //      当为 true 时跳过上传, 但仍然需要完成 `sendMessage(FileMessage)` 才是正常逻辑
                     callback?.onBegin(file, content)
                     val result = kotlin.runCatching {
-                        folder.contact.sendMessage(file.toMessage())
+                        folder.contact.sendMessage(file.toMessage() + MiraiInternalMessageFlag)
                     }.map { content.size }
                     callback?.onFinished(file, content, result)
                     return file
@@ -235,7 +236,7 @@ internal class AbsoluteFolderImpl(
                     )
                 }.let { result0 ->
                     val result = result0.onSuccessCatching {
-                        folder.contact.sendMessage(file.toMessage())
+                        folder.contact.sendMessage(file.toMessage() + MiraiInternalMessageFlag)
                     }
                     callback?.onFinished(file, content, result.map { content.size })
                 }
diff --git a/mirai-core/src/commonMain/kotlin/contact/util.kt b/mirai-core/src/commonMain/kotlin/contact/util.kt
index 2f9a8793c..3d0f24540 100644
--- a/mirai-core/src/commonMain/kotlin/contact/util.kt
+++ b/mirai-core/src/commonMain/kotlin/contact/util.kt
@@ -14,6 +14,7 @@ package net.mamoe.mirai.internal.contact
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.contact.*
 import net.mamoe.mirai.internal.message.LongMessageInternal
+import net.mamoe.mirai.internal.message.MiraiInternalMessageFlag
 import net.mamoe.mirai.internal.utils.estimateLength
 import net.mamoe.mirai.message.data.*
 import net.mamoe.mirai.utils.cast
@@ -33,6 +34,19 @@ internal fun Contact.logMessageSent(message: Message) {
 
 internal fun MessageChain.countImages(): Int = this.count { it is Image }
 
+internal fun Message.verifySendingValid() {
+    fun fail(msg: String): Nothing = throw IllegalArgumentException(msg)
+    when (this) {
+        is MessageChain -> {
+            if (contains(MiraiInternalMessageFlag)) {
+                return
+            }
+            this.forEach { it.verifySendingValid() }
+        }
+        is FileMessage -> fail("Sending FileMessage is not in support")
+    }
+}
+
 internal fun MessageChain.verifyLength(
     originalMessage: Message, target: Contact,
 ): Int {
diff --git a/mirai-core/src/commonMain/kotlin/message/ForceAsLongMessage.kt b/mirai-core/src/commonMain/kotlin/message/ForceAsLongMessage.kt
index f8f426917..d78794359 100644
--- a/mirai-core/src/commonMain/kotlin/message/ForceAsLongMessage.kt
+++ b/mirai-core/src/commonMain/kotlin/message/ForceAsLongMessage.kt
@@ -41,6 +41,15 @@ internal object IgnoreLengthCheck : MessageMetadata, ConstrainSingle, InternalFl
     override fun toString(): String = ""
 }
 
+/**
+ * 代表来自 mirai 内部
+ */
+internal object MiraiInternalMessageFlag : MessageMetadata, ConstrainSingle, InternalFlagOnlyMessage,
+    AbstractMessageKey<MiraiInternalMessageFlag>({ it.safeCast() }) {
+    override val key: MessageKey<MiraiInternalMessageFlag> get() = this
+    override fun toString(): String = ""
+}
+
 /**
  * Ignore on transformation
  */
diff --git a/mirai-core/src/commonMain/kotlin/utils/RemoteFileImpl.kt b/mirai-core/src/commonMain/kotlin/utils/RemoteFileImpl.kt
index a7b779400..bff53a6dc 100644
--- a/mirai-core/src/commonMain/kotlin/utils/RemoteFileImpl.kt
+++ b/mirai-core/src/commonMain/kotlin/utils/RemoteFileImpl.kt
@@ -19,6 +19,7 @@ import net.mamoe.mirai.contact.isOperator
 import net.mamoe.mirai.internal.asQQAndroidBot
 import net.mamoe.mirai.internal.contact.groupCode
 import net.mamoe.mirai.internal.message.FileMessageImpl
+import net.mamoe.mirai.internal.message.MiraiInternalMessageFlag
 import net.mamoe.mirai.internal.network.highway.Highway
 import net.mamoe.mirai.internal.network.highway.ResourceKind
 import net.mamoe.mirai.internal.network.protocol
@@ -29,7 +30,6 @@ import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
 import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
 import net.mamoe.mirai.message.MessageReceipt
 import net.mamoe.mirai.message.data.FileMessage
-import net.mamoe.mirai.message.data.sendTo
 import net.mamoe.mirai.utils.*
 import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource
 import net.mamoe.mirai.utils.RemoteFile.Companion.ROOT_PATH
@@ -560,7 +560,7 @@ internal class RemoteFileImpl(
 
     override suspend fun uploadAndSend(resource: ExternalResource): MessageReceipt<Contact> {
         @Suppress("DEPRECATION")
-        return upload(resource).sendTo(contact)
+        return contact.sendMessage(upload(resource) + MiraiInternalMessageFlag)
     }
 
     // compiler bug