diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt index b73860daa..d67a04053 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt @@ -78,6 +78,7 @@ internal class QQImpl( return MessageReceipt(source, this, null) } + @OptIn(MiraiInternalAPI::class) override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = try { if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) { throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup") @@ -111,7 +112,7 @@ internal class QQImpl( ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast() } is LongConn.OffPicUp.Response.RequireUpload -> { - Http.postImage( + MiraiPlatformUtils.Http.postImage( "0x6ff0070", bot.uin, null, diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt index af7b7b02f..b87883f51 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt @@ -222,7 +222,7 @@ internal abstract class QQAndroidBotBase constructor( } override suspend fun openChannel(image: Image): ByteReadChannel { - return Http.get(queryImageUrl(image)).content.toKotlinByteReadChannel() + return MiraiPlatformUtils.Http.get(queryImageUrl(image)).content.toKotlinByteReadChannel() } } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt index 7a64a5a6e..51a8e7451 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt @@ -9,18 +9,14 @@ package net.mamoe.mirai.qqandroid.message -import kotlinx.io.core.buildPacket -import kotlinx.io.core.discardExact -import kotlinx.io.core.readBytes -import kotlinx.io.core.readUInt +import kotlinx.io.core.* import net.mamoe.mirai.LowLevelAPI import net.mamoe.mirai.contact.Member import net.mamoe.mirai.message.data.* import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm -import net.mamoe.mirai.utils.ExternalImage -import net.mamoe.mirai.utils.MiraiDebugAPI -import net.mamoe.mirai.utils.MiraiInternalAPI +import net.mamoe.mirai.utils.* +import net.mamoe.mirai.utils.io.encodeToString import net.mamoe.mirai.utils.io.hexToBytes import net.mamoe.mirai.utils.io.read import net.mamoe.mirai.utils.io.toByteArray @@ -222,7 +218,7 @@ private val atAllData = ImMsgBody.Elem( ) ) -@OptIn(MiraiInternalAPI::class) +@OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class) internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList { val elements = mutableListOf() @@ -243,6 +239,14 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList elements.add( + ImMsgBody.Elem( + richMsg = ImMsgBody.RichMsg( + serviceId = it.serviceId, + template1 = byteArrayOf(1) + MiraiPlatformUtils.zip(it.content.toByteArray()) + ) + ) + ) is OfflineGroupImage -> elements.add(ImMsgBody.Elem(customFace = it.toJceData())) is OnlineGroupImageImpl -> elements.add(ImMsgBody.Elem(customFace = it.delegate)) is OnlineFriendImageImpl -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate)) @@ -400,6 +404,28 @@ internal fun List.joinToMessageChain(message: MessageChainBuilde } } } + it.richMsg != null -> { + when (it.richMsg.serviceId) { + 60 -> message.add( + XMLMessage( + content = MiraiPlatformUtils.unzip(it.richMsg.template1, 1).encodeToString() + ) + ) + else -> { + @Suppress("DEPRECATION") + MiraiLogger.debug { + "unknown richMsg.serviceId: ${it.richMsg.serviceId}, content=${it.richMsg.template1.contentToString()}, \ntryUnzip=${ + kotlin.runCatching { + MiraiPlatformUtils.unzip(it.richMsg.template1, 1).encodeToString() + }.getOrElse { "" } + }" + } + } + } + } + else -> { + println(it._miraiContentToString()) + } } } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt index 8cf12fb40..3abcf94ee 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt @@ -191,8 +191,9 @@ internal open class QQAndroidClient( lateinit var t104: ByteArray } +@OptIn(MiraiInternalAPI::class) internal fun generateTgtgtKey(guid: ByteArray): ByteArray = - md5(getRandomByteArray(16) + guid) + MiraiPlatformUtils.md5(getRandomByteArray(16) + guid) internal class ReserveUinInfo( diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/highway.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/highway.kt index b14d0627a..64c570031 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/highway.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/highway.kt @@ -26,6 +26,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.io.* import kotlinx.serialization.InternalSerializationApi +import net.mamoe.mirai.utils.MiraiPlatformUtils @OptIn(MiraiInternalAPI::class, InternalSerializationApi::class) internal fun createImageDataPacketSequence( // RequestDataTrans @@ -77,7 +78,7 @@ internal fun createImageDataPacketSequence( // RequestDataTrans dataoffset = offset, filesize = dataSize.toLong(), serviceticket = uKey, - md5 = net.mamoe.mirai.utils.md5(chunkedInput.buffer, 0, chunkedInput.bufferSize), + md5 = MiraiPlatformUtils.md5(chunkedInput.buffer, 0, chunkedInput.bufferSize), fileMd5 = fileMd5, flag = 0, rtcode = 0 diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt index e260813a4..dfb85b6b3 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt @@ -342,7 +342,7 @@ internal object KnownPacketFactories { 1 -> { input.discardExact(4) input.useBytes { data, length -> - data.unzip(length = length).let { + MiraiPlatformUtils.unzip(data, 0, length).let { val size = it.toInt() if (size == it.size || size == it.size + 4) { it.toReadPacket(offset = 4) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt index 7f5d44ac1..e527fe5ce 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt @@ -15,9 +15,10 @@ import kotlinx.io.core.toByteArray import kotlinx.io.core.writeFully import net.mamoe.mirai.qqandroid.network.protocol.LoginType import net.mamoe.mirai.qqandroid.utils.NetworkType +import net.mamoe.mirai.utils.MiraiInternalAPI +import net.mamoe.mirai.utils.MiraiPlatformUtils import net.mamoe.mirai.utils.currentTimeMillis import net.mamoe.mirai.utils.io.* -import net.mamoe.mirai.utils.md5 import kotlin.random.Random /** @@ -76,6 +77,7 @@ fun BytePacketBuilder.t18( } shouldEqualsTo 22 } +@OptIn(MiraiInternalAPI::class) fun BytePacketBuilder.t106( appId: Long = 16L, subAppId: Long = 537062845L, @@ -96,7 +98,7 @@ fun BytePacketBuilder.t106( guid?.requireSize(16) writeShortLVPacket { - encryptAndWrite(md5(passwordMd5 + ByteArray(4) + (salt.takeIf { it != 0L } ?: uin).toInt().toByteArray())) { + encryptAndWrite(MiraiPlatformUtils.md5(passwordMd5 + ByteArray(4) + (salt.takeIf { it != 0L } ?: uin).toInt().toByteArray())) { writeShort(4)//TGTGTVer writeInt(Random.nextInt()) writeInt(5)//ssoVer @@ -321,12 +323,13 @@ fun BytePacketBuilder.t144( } } +@OptIn(MiraiInternalAPI::class) fun BytePacketBuilder.t109( androidId: ByteArray ) { writeShort(0x109) writeShortLVPacket { - writeFully(md5(androidId)) + writeFully(MiraiPlatformUtils.md5(androidId)) } shouldEqualsTo 16 } @@ -556,21 +559,23 @@ fun BytePacketBuilder.t400( } } +@OptIn(MiraiInternalAPI::class) fun BytePacketBuilder.t187( macAddress: ByteArray ) { writeShort(0x187) writeShortLVPacket { - writeFully(md5(macAddress)) // may be md5 + writeFully(MiraiPlatformUtils.md5(macAddress)) // may be md5 } } +@OptIn(MiraiInternalAPI::class) fun BytePacketBuilder.t188( androidId: ByteArray ) { writeShort(0x188) writeShortLVPacket { - writeFully(md5(androidId)) + writeFully(MiraiPlatformUtils.md5(androidId)) } shouldEqualsTo 16 } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt index 67c21ca29..d4f65ae25 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt @@ -23,9 +23,10 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory import net.mamoe.mirai.qqandroid.network.protocol.packet.buildLoginOutgoingPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.writeSsoPacket import net.mamoe.mirai.qqandroid.utils.NetworkType +import net.mamoe.mirai.utils.MiraiInternalAPI +import net.mamoe.mirai.utils.MiraiPlatformUtils import net.mamoe.mirai.utils.io.encodeToString import net.mamoe.mirai.utils.io.toReadPacket -import net.mamoe.mirai.utils.localIpAddress @Suppress("EnumEntryName") internal enum class RegPushReason { @@ -89,6 +90,7 @@ internal class StatSvc { private const val subAppId = 537062845L + @OptIn(MiraiInternalAPI::class) operator fun invoke( client: QQAndroidClient, regPushReason: RegPushReason = RegPushReason.appRegister @@ -138,7 +140,7 @@ internal class StatSvc { strOSVer = client.device.version.release.encodeToString(), uOldSSOIp = 0, - uNewSSOIp = localIpAddress().split(".").foldIndexed(0L) { index: Int, acc: Long, s: String -> + uNewSSOIp = MiraiPlatformUtils.localIpAddress().split(".").foldIndexed(0L) { index: Int, acc: Long, s: String -> acc or ((s.toLong() shl (index * 16))) }, strVendorName = "MIUI", diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt index a08646b24..5b7365394 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt @@ -84,7 +84,7 @@ internal class WtLogin { t8(2052) t104(client.t104) t116(150470524, 66560) - t401(md5(client.device.guid + "stMNokHgxZUGhsYp".toByteArray() + t402)) + t401(MiraiPlatformUtils.md5(client.device.guid + "stMNokHgxZUGhsYp".toByteArray() + t402)) } } } diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/ExternalImageAndroid.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/ExternalImageAndroid.kt index 71d3764e2..2f433f224 100644 --- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/ExternalImageAndroid.kt +++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/ExternalImageAndroid.kt @@ -44,6 +44,7 @@ fun Bitmap.toExternalImage(formatName: String = "gif"): ExternalImage { /** * 读取文件头识别图片属性, 然后构造 [ExternalImage] */ +@OptIn(MiraiInternalAPI::class) @Throws(IOException::class) fun File.toExternalImage(): ExternalImage { val input = BitmapFactory.decodeFile(this.absolutePath) @@ -52,7 +53,7 @@ fun File.toExternalImage(): ExternalImage { return ExternalImage( width = input.width, height = input.height, - md5 = this.inputStream().use { it.md5() }, + md5 = this.inputStream().use { MiraiPlatformUtils.md5(it) }, imageFormat = this.nameWithoutExtension, input = this.inputStream().asInput(IoBuffer.Pool), inputSize = this.length(), diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/SystemDeviceInfo.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/SystemDeviceInfo.kt index 7cc35df53..3466ec9b6 100644 --- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/SystemDeviceInfo.kt +++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/SystemDeviceInfo.kt @@ -21,6 +21,8 @@ import kotlinx.serialization.Transient import kotlinx.serialization.UnstableDefault import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration +import net.mamoe.mirai.utils.MiraiPlatformUtils.localIpAddress +import net.mamoe.mirai.utils.MiraiPlatformUtils.md5 import java.io.File /** @@ -102,6 +104,7 @@ actual open class SystemDeviceInfo actual constructor() : DeviceInfo() { (context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager).connectionInfo.ssid.toByteArray() }.getOrElse { byteArrayOf() } + @OptIn(MiraiInternalAPI::class) override val imsiMd5: ByteArray @SuppressLint("HardwareIds") get() = md5(kotlin.runCatching { @@ -117,6 +120,8 @@ actual open class SystemDeviceInfo actual constructor() : DeviceInfo() { (context.applicationContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager).deviceId } }.getOrElse { "" } + + @OptIn(MiraiInternalAPI::class) override val ipAddress: ByteArray get() = localIpAddress().split(".").map { it.toByte() }.takeIf { it.size == 4 }?.toByteArray() ?: byteArrayOf() override val androidId: ByteArray get() = Build.ID.toByteArray() override val apn: ByteArray get() = "wifi".toByteArray() diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDHAndroid.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDHAndroid.kt index 5fe233778..c5b1df78a 100644 --- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDHAndroid.kt +++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDHAndroid.kt @@ -10,7 +10,8 @@ package net.mamoe.mirai.utils.cryptor import android.annotation.SuppressLint -import net.mamoe.mirai.utils.md5 +import net.mamoe.mirai.utils.MiraiInternalAPI +import net.mamoe.mirai.utils.MiraiPlatformUtils.md5 import java.security.* import java.security.spec.ECGenParameterSpec import java.security.spec.X509EncodedKeySpec @@ -71,6 +72,7 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) { .genKeyPair()) } + @OptIn(MiraiInternalAPI::class) actual fun calculateShareKey( privateKey: ECDHPrivateKey, publicKey: ECDHPublicKey diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt index 8cb421f09..e8a1157dd 100644 --- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt +++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt @@ -7,6 +7,8 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ +@file:Suppress("NOTHING_TO_INLINE") + package net.mamoe.mirai.utils import io.ktor.client.HttpClient @@ -15,88 +17,93 @@ import io.ktor.util.KtorExperimentalAPI import kotlinx.io.pool.useInstance import net.mamoe.mirai.utils.io.ByteArrayPool import java.io.ByteArrayOutputStream -import java.io.DataInput -import java.io.EOFException import java.io.InputStream import java.net.InetAddress import java.security.MessageDigest +import java.util.zip.Deflater import java.util.zip.Inflater -/** - * Ktor HttpClient. 不同平台使用不同引擎. - */ -@OptIn(KtorExperimentalAPI::class) -actual val Http: HttpClient - get() = HttpClient(CIO) - -/** - * Localhost 解析 - */ -actual fun localIpAddress(): String = runCatching { - InetAddress.getLocalHost().hostAddress -}.getOrElse { "192.168.1.123" } - -/** - * MD5 算法 - * - * @return 16 bytes - */ -actual fun md5(byteArray: ByteArray, offset: Int, length: Int): ByteArray = - MessageDigest.getInstance("MD5").apply { update(byteArray, offset, length) }.digest() - -fun InputStream.md5(): ByteArray { - val digest = MessageDigest.getInstance("md5") - digest.reset() - this.readInSequence { - digest.update(it.toByte()) - } - return digest.digest() -} - -fun DataInput.md5(): ByteArray { - val digest = MessageDigest.getInstance("md5") - digest.reset() - val buffer = byteArrayOf(1) - while (true) { - try { - this.readFully(buffer) - } catch (e: EOFException) { - break - } - digest.update(buffer[0]) - } - return digest.digest() -} - -private inline fun InputStream.readInSequence(block: (Int) -> Unit) { - var read: Int - while (this.read().also { read = it } != -1) { - block(read) - } -} - -@OptIn(MiraiInternalAPI::class) -actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray { - this.checkOffsetAndLength(offset, length) - if (length == 0) return ByteArray(0) - - val inflater = Inflater() - inflater.reset() - ByteArrayOutputStream().use { output -> - inflater.setInput(this, offset, length) - ByteArrayPool.useInstance { - while (!inflater.finished()) { - output.write(it, 0, inflater.inflate(it)) - } - } - - inflater.end() - return output.toByteArray() - } -} - /** * 时间戳 */ -actual val currentTimeMillis: Long get() = System.currentTimeMillis() \ No newline at end of file +actual val currentTimeMillis: Long get() = System.currentTimeMillis() + +@MiraiInternalAPI +actual object MiraiPlatformUtils { + actual fun unzip(data: ByteArray, offset: Int, length: Int): ByteArray { + data.checkOffsetAndLength(offset, length) + if (length == 0) return ByteArray(0) + + val inflater = Inflater() + inflater.reset() + ByteArrayOutputStream().use { output -> + inflater.setInput(data, offset, length) + ByteArrayPool.useInstance { + while (!inflater.finished()) { + output.write(it, 0, inflater.inflate(it)) + } + } + + inflater.end() + return output.toByteArray() + } + } + + actual fun zip(data: ByteArray, offset: Int, length: Int): ByteArray { + data.checkOffsetAndLength(offset, length) + if (length == 0) return ByteArray(0) + + val inflater = Deflater() + inflater.reset() + ByteArrayOutputStream().use { output -> + inflater.setInput(data, offset, length) + ByteArrayPool.useInstance { + while (!inflater.finished()) { + output.write(it, 0, inflater.deflate(it)) + } + } + + inflater.end() + return output.toByteArray() + } + } + + actual fun md5(data: ByteArray, offset: Int, length: Int): ByteArray { + data.checkOffsetAndLength(offset, length) + return MessageDigest.getInstance("MD5").apply { update(data, offset, length) }.digest() + } + + actual inline fun md5(str: String): ByteArray = md5(str.toByteArray()) + + /** + * Ktor HttpClient. 不同平台使用不同引擎. + */ + @OptIn(KtorExperimentalAPI::class) + actual val Http: HttpClient + get() = HttpClient(CIO) + + /** + * Localhost 解析 + */ + actual fun localIpAddress(): String = runCatching { + InetAddress.getLocalHost().hostAddress + }.getOrElse { "192.168.1.123" } + + fun md5(stream: InputStream): ByteArray { + val digest = MessageDigest.getInstance("md5") + digest.reset() + stream.readInSequence { + digest.update(it.toByte()) + } + return digest.digest() + } + + + private inline fun InputStream.readInSequence(block: (Int) -> Unit) { + var read: Int + while (this.read().also { read = it } != -1) { + block(read) + } + } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAccount.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAccount.kt index 01968d154..c8a87b302 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAccount.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAccount.kt @@ -14,7 +14,7 @@ package net.mamoe.mirai import kotlinx.io.core.toByteArray import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiInternalAPI -import net.mamoe.mirai.utils.md5 +import net.mamoe.mirai.utils.MiraiPlatformUtils import kotlin.annotation.AnnotationTarget.* @MiraiInternalAPI @@ -28,7 +28,7 @@ data class BotAccount( @MiraiInternalAPI val passwordMd5: ByteArray // md5 ) { - constructor(id: Long, passwordPlainText: String) : this(id, md5(passwordPlainText.toByteArray())) + constructor(id: Long, passwordPlainText: String) : this(id, MiraiPlatformUtils.md5(passwordPlainText.toByteArray())) override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/DeviceInfo.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/DeviceInfo.kt index b44ed6cbb..2000b3889 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/DeviceInfo.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/DeviceInfo.kt @@ -94,6 +94,7 @@ abstract class DeviceInfo { } } +@OptIn(MiraiInternalAPI::class) @Serializable class DeviceInfoData( override val display: ByteArray, @@ -122,7 +123,8 @@ class DeviceInfoData( @OptIn(ExperimentalUnsignedTypes::class) override val ipAddress: ByteArray - get() = localIpAddress().split(".").map { it.toUByte().toByte() }.takeIf { it.size == 4 }?.toByteArray() + get() = MiraiPlatformUtils.localIpAddress().split(".").map { it.toUByte().toByte() }.takeIf { it.size == 4 } + ?.toByteArray() ?: byteArrayOf() override val androidId: ByteArray get() = display @@ -138,7 +140,9 @@ class DeviceInfoData( /** * Defaults "%4;7t>;28` and `ByteArray` internal fun ByteArray.checkOffsetAndLength(offset: Int, length: Int) { diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ExternalImageJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ExternalImageJvm.kt index 20a53c769..47bfd47db 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ExternalImageJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ExternalImageJvm.kt @@ -11,8 +11,8 @@ package net.mamoe.mirai.utils -import kotlinx.coroutines.io.ByteReadChannel import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.io.ByteReadChannel import kotlinx.coroutines.withContext import kotlinx.io.core.Input import kotlinx.io.core.buildPacket @@ -60,6 +60,7 @@ suspend inline fun BufferedImage.suspendToExternalImage(): ExternalImage = withC /** * 读取文件头识别图片属性, 然后构造 [ExternalImage] */ +@OptIn(MiraiInternalAPI::class) @Throws(IOException::class) fun File.toExternalImage(): ExternalImage { val input = ImageIO.createImageInputStream(this) @@ -71,7 +72,7 @@ fun File.toExternalImage(): ExternalImage { return ExternalImage( width = image.getWidth(0), height = image.getHeight(0), - md5 = this.inputStream().md5(), // dont change + md5 = MiraiPlatformUtils.md5(this.inputStream()), // dont change imageFormat = image.formatName, input = this.inputStream(), filename = this.name diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt index 494cfbe05..1b316fe72 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt @@ -7,78 +7,103 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ -@file:Suppress("EXPERIMENTAL_API_USAGE") +@file:Suppress("EXPERIMENTAL_API_USAGE", "NOTHING_TO_INLINE") package net.mamoe.mirai.utils import io.ktor.client.HttpClient import io.ktor.client.engine.cio.CIO +import io.ktor.util.KtorExperimentalAPI import kotlinx.io.pool.useInstance import net.mamoe.mirai.utils.io.ByteArrayPool -import java.io.* +import java.io.ByteArrayOutputStream +import java.io.InputStream +import java.io.OutputStream import java.net.InetAddress import java.security.MessageDigest +import java.util.zip.Deflater import java.util.zip.Inflater -actual fun md5(byteArray: ByteArray, offset: Int, length: Int): ByteArray = - MessageDigest.getInstance("MD5").apply { update(byteArray, offset, length) }.digest() - -fun InputStream.md5(): ByteArray = this.use { - val digest = MessageDigest.getInstance("md5") - digest.reset() - this.use { input -> - object : OutputStream() { - override fun write(b: Int) { - digest.update(b.toByte()) - } - }.use { output -> - input.copyTo(output) - } - } - return digest.digest() -} - -fun DataInput.md5(): ByteArray { - val digest = MessageDigest.getInstance("md5") - digest.reset() - val buffer = byteArrayOf(1) - while (true) { - try { - this.readFully(buffer) - } catch (e: EOFException) { - break - } - digest.update(buffer[0]) - } - return digest.digest() -} - -actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress - -actual val Http: HttpClient get() = HttpClient(CIO) - -@OptIn(MiraiInternalAPI::class) -actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray { - this.checkOffsetAndLength(offset, length) - if (length == 0) return ByteArray(0) - - val inflater = Inflater() - inflater.reset() - ByteArrayOutputStream().use { output -> - inflater.setInput(this, offset, length) - ByteArrayPool.useInstance { - while (!inflater.finished()) { - output.write(it, 0, inflater.inflate(it)) - } - } - - inflater.end() - return output.toByteArray() - } -} - /** * 时间戳 */ actual val currentTimeMillis: Long - get() = System.currentTimeMillis() \ No newline at end of file + get() = System.currentTimeMillis() + +@MiraiInternalAPI +actual object MiraiPlatformUtils { + actual fun unzip(data: ByteArray, offset: Int, length: Int): ByteArray { + data.checkOffsetAndLength(offset, length) + if (length == 0) return ByteArray(0) + + val inflater = Inflater() + inflater.reset() + ByteArrayOutputStream().use { output -> + inflater.setInput(data, offset, length) + ByteArrayPool.useInstance { + while (!inflater.finished()) { + output.write(it, 0, inflater.inflate(it)) + } + } + + inflater.end() + return output.toByteArray() + } + } + + actual fun zip(data: ByteArray, offset: Int, length: Int): ByteArray { + data.checkOffsetAndLength(offset, length) + if (length == 0) return ByteArray(0) + + val inflater = Deflater() + inflater.reset() + ByteArrayOutputStream().use { output -> + inflater.setInput(data, offset, length) + ByteArrayPool.useInstance { + while (!inflater.finished()) { + output.write(it, 0, inflater.deflate(it)) + } + } + + inflater.end() + return output.toByteArray() + } + } + + actual fun md5(data: ByteArray, offset: Int, length: Int): ByteArray { + data.checkOffsetAndLength(offset, length) + return MessageDigest.getInstance("MD5").apply { update(data, offset, length) }.digest() + } + + actual inline fun md5(str: String): ByteArray = md5(str.toByteArray()) + + /** + * Ktor HttpClient. 不同平台使用不同引擎. + */ + @OptIn(KtorExperimentalAPI::class) + actual val Http: HttpClient + get() = HttpClient(CIO) + + /** + * Localhost 解析 + */ + actual fun localIpAddress(): String = runCatching { + InetAddress.getLocalHost().hostAddress + }.getOrElse { "192.168.1.123" } + + fun md5(stream: InputStream): ByteArray { + val digest = MessageDigest.getInstance("md5") + digest.reset() + stream.use { input -> + object : OutputStream() { + override fun write(b: Int) { + digest.update(b.toByte()) + } + }.use { output -> + input.copyTo(output) + } + } + return digest.digest() + } + +} \ No newline at end of file diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/SystemDeviceInfo.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/SystemDeviceInfo.kt index efb5cfd47..c9c6729aa 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/SystemDeviceInfo.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/SystemDeviceInfo.kt @@ -15,6 +15,8 @@ import kotlinx.serialization.Transient import kotlinx.serialization.UnstableDefault import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration +import net.mamoe.mirai.utils.MiraiPlatformUtils.localIpAddress +import net.mamoe.mirai.utils.MiraiPlatformUtils.md5 import net.mamoe.mirai.utils.io.getRandomByteArray import net.mamoe.mirai.utils.io.getRandomString import java.io.File @@ -37,7 +39,7 @@ fun File.loadAsDeviceInfo(context: Context = ContextImpl()): DeviceInfo { private val JSON = Json(JsonConfiguration.Stable) @Serializable -@OptIn(ExperimentalUnsignedTypes::class) +@OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class) actual open class SystemDeviceInfo actual constructor() : DeviceInfo() { actual constructor(context: Context) : this() { this.context = context diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDHJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDHJvm.kt index 67ac25325..3db9de92b 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDHJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDHJvm.kt @@ -9,7 +9,8 @@ package net.mamoe.mirai.utils.cryptor -import net.mamoe.mirai.utils.md5 +import net.mamoe.mirai.utils.MiraiInternalAPI +import net.mamoe.mirai.utils.MiraiPlatformUtils import org.bouncycastle.jce.provider.BouncyCastleProvider import java.security.* import java.security.spec.ECGenParameterSpec @@ -58,6 +59,7 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) { .genKeyPair()) } + @OptIn(MiraiInternalAPI::class) actual fun calculateShareKey( privateKey: ECDHPrivateKey, publicKey: ECDHPublicKey @@ -65,7 +67,7 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) { val instance = KeyAgreement.getInstance("ECDH", "BC") instance.init(privateKey) instance.doPhase(publicKey, true) - return md5(instance.generateSecret()) + return MiraiPlatformUtils.md5(instance.generateSecret()) } actual fun constructPublicKey(key: ByteArray): ECDHPublicKey {