Commit 243b2ea7 authored by Him188's avatar Him188

Gather all platform-specified utilities into a `MiraiPlatformUtils`

parent 9f988e7d
...@@ -78,6 +78,7 @@ internal class QQImpl( ...@@ -78,6 +78,7 @@ internal class QQImpl(
return MessageReceipt(source, this, null) return MessageReceipt(source, this, null)
} }
@OptIn(MiraiInternalAPI::class)
override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = try { override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = try {
if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) { if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) {
throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup") throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
...@@ -111,7 +112,7 @@ internal class QQImpl( ...@@ -111,7 +112,7 @@ internal class QQImpl(
ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast() ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast()
} }
is LongConn.OffPicUp.Response.RequireUpload -> { is LongConn.OffPicUp.Response.RequireUpload -> {
Http.postImage( MiraiPlatformUtils.Http.postImage(
"0x6ff0070", "0x6ff0070",
bot.uin, bot.uin,
null, null,
......
...@@ -222,7 +222,7 @@ internal abstract class QQAndroidBotBase constructor( ...@@ -222,7 +222,7 @@ internal abstract class QQAndroidBotBase constructor(
} }
override suspend fun openChannel(image: Image): ByteReadChannel { override suspend fun openChannel(image: Image): ByteReadChannel {
return Http.get<HttpResponse>(queryImageUrl(image)).content.toKotlinByteReadChannel() return MiraiPlatformUtils.Http.get<HttpResponse>(queryImageUrl(image)).content.toKotlinByteReadChannel()
} }
} }
......
...@@ -9,18 +9,14 @@ ...@@ -9,18 +9,14 @@
package net.mamoe.mirai.qqandroid.message package net.mamoe.mirai.qqandroid.message
import kotlinx.io.core.buildPacket import kotlinx.io.core.*
import kotlinx.io.core.discardExact
import kotlinx.io.core.readBytes
import kotlinx.io.core.readUInt
import net.mamoe.mirai.LowLevelAPI import net.mamoe.mirai.LowLevelAPI
import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.message.data.* 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.ImMsgBody
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.utils.ExternalImage import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.MiraiDebugAPI import net.mamoe.mirai.utils.io.encodeToString
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.io.hexToBytes import net.mamoe.mirai.utils.io.hexToBytes
import net.mamoe.mirai.utils.io.read import net.mamoe.mirai.utils.io.read
import net.mamoe.mirai.utils.io.toByteArray import net.mamoe.mirai.utils.io.toByteArray
...@@ -222,7 +218,7 @@ private val atAllData = ImMsgBody.Elem( ...@@ -222,7 +218,7 @@ private val atAllData = ImMsgBody.Elem(
) )
) )
@OptIn(MiraiInternalAPI::class) @OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgBody.Elem> { internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgBody.Elem> {
val elements = mutableListOf<ImMsgBody.Elem>() val elements = mutableListOf<ImMsgBody.Elem>()
...@@ -243,6 +239,14 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgB ...@@ -243,6 +239,14 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgB
elements.add(ImMsgBody.Elem(text = it.toJceData())) elements.add(ImMsgBody.Elem(text = it.toJceData()))
elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = " "))) elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = " ")))
} }
is RichMessage -> 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 OfflineGroupImage -> elements.add(ImMsgBody.Elem(customFace = it.toJceData()))
is OnlineGroupImageImpl -> elements.add(ImMsgBody.Elem(customFace = it.delegate)) is OnlineGroupImageImpl -> elements.add(ImMsgBody.Elem(customFace = it.delegate))
is OnlineFriendImageImpl -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate)) is OnlineFriendImageImpl -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate))
...@@ -400,6 +404,28 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChainBuilde ...@@ -400,6 +404,28 @@ internal fun List<ImMsgBody.Elem>.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 { "<failed>" }
}"
}
}
}
}
else -> {
println(it._miraiContentToString())
}
} }
} }
......
...@@ -191,8 +191,9 @@ internal open class QQAndroidClient( ...@@ -191,8 +191,9 @@ internal open class QQAndroidClient(
lateinit var t104: ByteArray lateinit var t104: ByteArray
} }
@OptIn(MiraiInternalAPI::class)
internal fun generateTgtgtKey(guid: ByteArray): ByteArray = internal fun generateTgtgtKey(guid: ByteArray): ByteArray =
md5(getRandomByteArray(16) + guid) MiraiPlatformUtils.md5(getRandomByteArray(16) + guid)
internal class ReserveUinInfo( internal class ReserveUinInfo(
......
...@@ -26,6 +26,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY ...@@ -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.MiraiInternalAPI
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import kotlinx.serialization.InternalSerializationApi import kotlinx.serialization.InternalSerializationApi
import net.mamoe.mirai.utils.MiraiPlatformUtils
@OptIn(MiraiInternalAPI::class, InternalSerializationApi::class) @OptIn(MiraiInternalAPI::class, InternalSerializationApi::class)
internal fun createImageDataPacketSequence( // RequestDataTrans internal fun createImageDataPacketSequence( // RequestDataTrans
...@@ -77,7 +78,7 @@ internal fun createImageDataPacketSequence( // RequestDataTrans ...@@ -77,7 +78,7 @@ internal fun createImageDataPacketSequence( // RequestDataTrans
dataoffset = offset, dataoffset = offset,
filesize = dataSize.toLong(), filesize = dataSize.toLong(),
serviceticket = uKey, serviceticket = uKey,
md5 = net.mamoe.mirai.utils.md5(chunkedInput.buffer, 0, chunkedInput.bufferSize), md5 = MiraiPlatformUtils.md5(chunkedInput.buffer, 0, chunkedInput.bufferSize),
fileMd5 = fileMd5, fileMd5 = fileMd5,
flag = 0, flag = 0,
rtcode = 0 rtcode = 0
......
...@@ -342,7 +342,7 @@ internal object KnownPacketFactories { ...@@ -342,7 +342,7 @@ internal object KnownPacketFactories {
1 -> { 1 -> {
input.discardExact(4) input.discardExact(4)
input.useBytes { data, length -> input.useBytes { data, length ->
data.unzip(length = length).let { MiraiPlatformUtils.unzip(data, 0, length).let {
val size = it.toInt() val size = it.toInt()
if (size == it.size || size == it.size + 4) { if (size == it.size || size == it.size + 4) {
it.toReadPacket(offset = 4) it.toReadPacket(offset = 4)
......
...@@ -15,9 +15,10 @@ import kotlinx.io.core.toByteArray ...@@ -15,9 +15,10 @@ import kotlinx.io.core.toByteArray
import kotlinx.io.core.writeFully import kotlinx.io.core.writeFully
import net.mamoe.mirai.qqandroid.network.protocol.LoginType import net.mamoe.mirai.qqandroid.network.protocol.LoginType
import net.mamoe.mirai.qqandroid.utils.NetworkType 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.currentTimeMillis
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.md5
import kotlin.random.Random import kotlin.random.Random
/** /**
...@@ -76,6 +77,7 @@ fun BytePacketBuilder.t18( ...@@ -76,6 +77,7 @@ fun BytePacketBuilder.t18(
} shouldEqualsTo 22 } shouldEqualsTo 22
} }
@OptIn(MiraiInternalAPI::class)
fun BytePacketBuilder.t106( fun BytePacketBuilder.t106(
appId: Long = 16L, appId: Long = 16L,
subAppId: Long = 537062845L, subAppId: Long = 537062845L,
...@@ -96,7 +98,7 @@ fun BytePacketBuilder.t106( ...@@ -96,7 +98,7 @@ fun BytePacketBuilder.t106(
guid?.requireSize(16) guid?.requireSize(16)
writeShortLVPacket { 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 writeShort(4)//TGTGTVer
writeInt(Random.nextInt()) writeInt(Random.nextInt())
writeInt(5)//ssoVer writeInt(5)//ssoVer
...@@ -321,12 +323,13 @@ fun BytePacketBuilder.t144( ...@@ -321,12 +323,13 @@ fun BytePacketBuilder.t144(
} }
} }
@OptIn(MiraiInternalAPI::class)
fun BytePacketBuilder.t109( fun BytePacketBuilder.t109(
androidId: ByteArray androidId: ByteArray
) { ) {
writeShort(0x109) writeShort(0x109)
writeShortLVPacket { writeShortLVPacket {
writeFully(md5(androidId)) writeFully(MiraiPlatformUtils.md5(androidId))
} shouldEqualsTo 16 } shouldEqualsTo 16
} }
...@@ -556,21 +559,23 @@ fun BytePacketBuilder.t400( ...@@ -556,21 +559,23 @@ fun BytePacketBuilder.t400(
} }
} }
@OptIn(MiraiInternalAPI::class)
fun BytePacketBuilder.t187( fun BytePacketBuilder.t187(
macAddress: ByteArray macAddress: ByteArray
) { ) {
writeShort(0x187) writeShort(0x187)
writeShortLVPacket { writeShortLVPacket {
writeFully(md5(macAddress)) // may be md5 writeFully(MiraiPlatformUtils.md5(macAddress)) // may be md5
} }
} }
@OptIn(MiraiInternalAPI::class)
fun BytePacketBuilder.t188( fun BytePacketBuilder.t188(
androidId: ByteArray androidId: ByteArray
) { ) {
writeShort(0x188) writeShort(0x188)
writeShortLVPacket { writeShortLVPacket {
writeFully(md5(androidId)) writeFully(MiraiPlatformUtils.md5(androidId))
} shouldEqualsTo 16 } shouldEqualsTo 16
} }
......
...@@ -23,9 +23,10 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory ...@@ -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.buildLoginOutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.writeSsoPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.writeSsoPacket
import net.mamoe.mirai.qqandroid.utils.NetworkType 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.encodeToString
import net.mamoe.mirai.utils.io.toReadPacket import net.mamoe.mirai.utils.io.toReadPacket
import net.mamoe.mirai.utils.localIpAddress
@Suppress("EnumEntryName") @Suppress("EnumEntryName")
internal enum class RegPushReason { internal enum class RegPushReason {
...@@ -89,6 +90,7 @@ internal class StatSvc { ...@@ -89,6 +90,7 @@ internal class StatSvc {
private const val subAppId = 537062845L private const val subAppId = 537062845L
@OptIn(MiraiInternalAPI::class)
operator fun invoke( operator fun invoke(
client: QQAndroidClient, client: QQAndroidClient,
regPushReason: RegPushReason = RegPushReason.appRegister regPushReason: RegPushReason = RegPushReason.appRegister
...@@ -138,7 +140,7 @@ internal class StatSvc { ...@@ -138,7 +140,7 @@ internal class StatSvc {
strOSVer = client.device.version.release.encodeToString(), strOSVer = client.device.version.release.encodeToString(),
uOldSSOIp = 0, 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))) acc or ((s.toLong() shl (index * 16)))
}, },
strVendorName = "MIUI", strVendorName = "MIUI",
......
...@@ -84,7 +84,7 @@ internal class WtLogin { ...@@ -84,7 +84,7 @@ internal class WtLogin {
t8(2052) t8(2052)
t104(client.t104) t104(client.t104)
t116(150470524, 66560) t116(150470524, 66560)
t401(md5(client.device.guid + "stMNokHgxZUGhsYp".toByteArray() + t402)) t401(MiraiPlatformUtils.md5(client.device.guid + "stMNokHgxZUGhsYp".toByteArray() + t402))
} }
} }
} }
......
...@@ -44,6 +44,7 @@ fun Bitmap.toExternalImage(formatName: String = "gif"): ExternalImage { ...@@ -44,6 +44,7 @@ fun Bitmap.toExternalImage(formatName: String = "gif"): ExternalImage {
/** /**
* 读取文件头识别图片属性, 然后构造 [ExternalImage] * 读取文件头识别图片属性, 然后构造 [ExternalImage]
*/ */
@OptIn(MiraiInternalAPI::class)
@Throws(IOException::class) @Throws(IOException::class)
fun File.toExternalImage(): ExternalImage { fun File.toExternalImage(): ExternalImage {
val input = BitmapFactory.decodeFile(this.absolutePath) val input = BitmapFactory.decodeFile(this.absolutePath)
...@@ -52,7 +53,7 @@ fun File.toExternalImage(): ExternalImage { ...@@ -52,7 +53,7 @@ fun File.toExternalImage(): ExternalImage {
return ExternalImage( return ExternalImage(
width = input.width, width = input.width,
height = input.height, height = input.height,
md5 = this.inputStream().use { it.md5() }, md5 = this.inputStream().use { MiraiPlatformUtils.md5(it) },
imageFormat = this.nameWithoutExtension, imageFormat = this.nameWithoutExtension,
input = this.inputStream().asInput(IoBuffer.Pool), input = this.inputStream().asInput(IoBuffer.Pool),
inputSize = this.length(), inputSize = this.length(),
......
...@@ -21,6 +21,8 @@ import kotlinx.serialization.Transient ...@@ -21,6 +21,8 @@ import kotlinx.serialization.Transient
import kotlinx.serialization.UnstableDefault import kotlinx.serialization.UnstableDefault
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration import kotlinx.serialization.json.JsonConfiguration
import net.mamoe.mirai.utils.MiraiPlatformUtils.localIpAddress
import net.mamoe.mirai.utils.MiraiPlatformUtils.md5
import java.io.File import java.io.File
/** /**
...@@ -102,6 +104,7 @@ actual open class SystemDeviceInfo actual constructor() : DeviceInfo() { ...@@ -102,6 +104,7 @@ actual open class SystemDeviceInfo actual constructor() : DeviceInfo() {
(context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager).connectionInfo.ssid.toByteArray() (context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager).connectionInfo.ssid.toByteArray()
}.getOrElse { byteArrayOf() } }.getOrElse { byteArrayOf() }
@OptIn(MiraiInternalAPI::class)
override val imsiMd5: ByteArray override val imsiMd5: ByteArray
@SuppressLint("HardwareIds") @SuppressLint("HardwareIds")
get() = md5(kotlin.runCatching { get() = md5(kotlin.runCatching {
...@@ -117,6 +120,8 @@ actual open class SystemDeviceInfo actual constructor() : DeviceInfo() { ...@@ -117,6 +120,8 @@ actual open class SystemDeviceInfo actual constructor() : DeviceInfo() {
(context.applicationContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager).deviceId (context.applicationContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager).deviceId
} }
}.getOrElse { "" } }.getOrElse { "" }
@OptIn(MiraiInternalAPI::class)
override val ipAddress: ByteArray get() = localIpAddress().split(".").map { it.toByte() }.takeIf { it.size == 4 }?.toByteArray() ?: byteArrayOf() 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 androidId: ByteArray get() = Build.ID.toByteArray()
override val apn: ByteArray get() = "wifi".toByteArray() override val apn: ByteArray get() = "wifi".toByteArray()
......
...@@ -10,7 +10,8 @@ ...@@ -10,7 +10,8 @@
package net.mamoe.mirai.utils.cryptor package net.mamoe.mirai.utils.cryptor
import android.annotation.SuppressLint 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.*
import java.security.spec.ECGenParameterSpec import java.security.spec.ECGenParameterSpec
import java.security.spec.X509EncodedKeySpec import java.security.spec.X509EncodedKeySpec
...@@ -71,6 +72,7 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) { ...@@ -71,6 +72,7 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
.genKeyPair()) .genKeyPair())
} }
@OptIn(MiraiInternalAPI::class)
actual fun calculateShareKey( actual fun calculateShareKey(
privateKey: ECDHPrivateKey, privateKey: ECDHPrivateKey,
publicKey: ECDHPublicKey publicKey: ECDHPublicKey
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("NOTHING_TO_INLINE")
package net.mamoe.mirai.utils package net.mamoe.mirai.utils
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
...@@ -15,88 +17,93 @@ import io.ktor.util.KtorExperimentalAPI ...@@ -15,88 +17,93 @@ import io.ktor.util.KtorExperimentalAPI
import kotlinx.io.pool.useInstance import kotlinx.io.pool.useInstance
import net.mamoe.mirai.utils.io.ByteArrayPool import net.mamoe.mirai.utils.io.ByteArrayPool
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.DataInput
import java.io.EOFException
import java.io.InputStream import java.io.InputStream
import java.net.InetAddress import java.net.InetAddress
import java.security.MessageDigest import java.security.MessageDigest
import java.util.zip.Deflater
import java.util.zip.Inflater import java.util.zip.Inflater
/** /**
* Ktor HttpClient. 不同平台使用不同引擎. * 时间戳
*/ */
@OptIn(KtorExperimentalAPI::class) actual val currentTimeMillis: Long get() = System.currentTimeMillis()
actual val Http: HttpClient
get() = HttpClient(CIO)
/** @MiraiInternalAPI
* Localhost 解析 actual object MiraiPlatformUtils {
*/ actual fun unzip(data: ByteArray, offset: Int, length: Int): ByteArray {
actual fun localIpAddress(): String = runCatching { data.checkOffsetAndLength(offset, length)
InetAddress.getLocalHost().hostAddress if (length == 0) return ByteArray(0)
}.getOrElse { "192.168.1.123" }
/** val inflater = Inflater()
* MD5 算法 inflater.reset()
* ByteArrayOutputStream().use { output ->
* @return 16 bytes inflater.setInput(data, offset, length)
*/ ByteArrayPool.useInstance {
actual fun md5(byteArray: ByteArray, offset: Int, length: Int): ByteArray = while (!inflater.finished()) {
MessageDigest.getInstance("MD5").apply { update(byteArray, offset, length) }.digest() output.write(it, 0, inflater.inflate(it))
}
fun InputStream.md5(): ByteArray { }
val digest = MessageDigest.getInstance("md5")
digest.reset() inflater.end()
this.readInSequence { return output.toByteArray()
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) { actual fun zip(data: ByteArray, offset: Int, length: Int): ByteArray {
var read: Int data.checkOffsetAndLength(offset, length)
while (this.read().also { read = it } != -1) { if (length == 0) return ByteArray(0)
block(read)
} val inflater = Deflater()
} inflater.reset()
ByteArrayOutputStream().use { output ->
@OptIn(MiraiInternalAPI::class) inflater.setInput(data, offset, length)
actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray { ByteArrayPool.useInstance {
this.checkOffsetAndLength(offset, length) while (!inflater.finished()) {
if (length == 0) return ByteArray(0) output.write(it, 0, inflater.deflate(it))
}
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()
} }
}
inflater.end() actual fun md5(data: ByteArray, offset: Int, length: Int): ByteArray {
return output.toByteArray() data.checkOffsetAndLength(offset, length)
return MessageDigest.getInstance("MD5").apply { update(data, offset, length) }.digest()
} }
}
/** actual inline fun md5(str: String): ByteArray = md5(str.toByteArray())
* 时间戳
*/ /**
actual val currentTimeMillis: Long get() = System.currentTimeMillis() * Ktor HttpClient. 不同平台使用不同引擎.
\ No newline at end of file */
@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
...@@ -14,7 +14,7 @@ package net.mamoe.mirai ...@@ -14,7 +14,7 @@ package net.mamoe.mirai
import kotlinx.io.core.toByteArray import kotlinx.io.core.toByteArray
import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.md5 import net.mamoe.mirai.utils.MiraiPlatformUtils
import kotlin.annotation.AnnotationTarget.* import kotlin.annotation.AnnotationTarget.*
@MiraiInternalAPI @MiraiInternalAPI
...@@ -28,7 +28,7 @@ data class BotAccount( ...@@ -28,7 +28,7 @@ data class BotAccount(
@MiraiInternalAPI @MiraiInternalAPI
val passwordMd5: ByteArray // md5 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 { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
......
...@@ -94,6 +94,7 @@ abstract class DeviceInfo { ...@@ -94,6 +94,7 @@ abstract class DeviceInfo {
} }
} }
@OptIn(MiraiInternalAPI::class)
@Serializable @Serializable
class DeviceInfoData( class DeviceInfoData(
override val display: ByteArray, override val display: ByteArray,
...@@ -122,7 +123,8 @@ class DeviceInfoData( ...@@ -122,7 +123,8 @@ class DeviceInfoData(
@OptIn(ExperimentalUnsignedTypes::class) @OptIn(ExperimentalUnsignedTypes::class)
override val ipAddress: ByteArray 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() ?: byteArrayOf()
override val androidId: ByteArray get() = display override val androidId: ByteArray get() = display
...@@ -138,7 +140,9 @@ class DeviceInfoData( ...@@ -138,7 +140,9 @@ class DeviceInfoData(
/** /**
* Defaults "%4;7t>;28<fc.5*6".toByteArray() * Defaults "%4;7t>;28<fc.5*6".toByteArray()
*/ */
fun generateGuid(androidId: ByteArray, macAddress: ByteArray): ByteArray = md5(androidId + macAddress) @OptIn(MiraiInternalAPI::class)
fun generateGuid(androidId: ByteArray, macAddress: ByteArray): ByteArray =
MiraiPlatformUtils.md5(androidId + macAddress)
/* /*
fun DeviceInfo.toOidb0x769DeviceInfo() : Oidb0x769.DeviceInfo = Oidb0x769.DeviceInfo( fun DeviceInfo.toOidb0x769DeviceInfo() : Oidb0x769.DeviceInfo = Oidb0x769.DeviceInfo(
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
package net.mamoe.mirai.utils package net.mamoe.mirai.utils
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import kotlinx.io.core.toByteArray
/** /**
* 时间戳 * 时间戳
...@@ -21,30 +20,30 @@ expect val currentTimeMillis: Long ...@@ -21,30 +20,30 @@ expect val currentTimeMillis: Long
inline val currentTimeSeconds: Long get() = currentTimeMillis / 1000 inline val currentTimeSeconds: Long get() = currentTimeMillis / 1000
/** /**
* 解 zip 压缩 * 仅供内部使用的工具类.
* 不写为扩展是为了避免污染命名空间.
*/ */
expect fun ByteArray.unzip(offset: Int = 0, length: Int = this.size - offset): ByteArray @MiraiInternalAPI
expect object MiraiPlatformUtils {
fun unzip(data: ByteArray, offset: Int = 0, length: Int = data.size - offset): ByteArray
/** fun zip(data: ByteArray, offset: Int = 0, length: Int = data.size - offset): ByteArray
* MD5 算法
*
* @return 16 bytes
*/
expect fun md5(byteArray: ByteArray, offset: Int = 0, length: Int = byteArray.size - offset): ByteArray
inline fun md5(str: String): ByteArray = md5(str.toByteArray())
/** fun md5(data: ByteArray, offset: Int = 0, length: Int = data.size - offset): ByteArray
* Localhost 解析
*/ inline fun md5(str: String): ByteArray
expect fun localIpAddress(): String
fun localIpAddress(): String
/**
* Ktor HttpClient. 不同平台使用不同引擎.
*/
@MiraiInternalAPI
val Http: HttpClient
}
/**
* Ktor HttpClient. 不同平台使用不同引擎.
*/
expect val Http: HttpClient
@Suppress("DuplicatedCode") // false positive. `this` is not the same for `List<Byte>` and `ByteArray` @Suppress("DuplicatedCode") // false positive. `this` is not the same for `List<Byte>` and `ByteArray`
internal fun ByteArray.checkOffsetAndLength(offset: Int, length: Int) { internal fun ByteArray.checkOffsetAndLength(offset: Int, length: Int) {
......
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
package net.mamoe.mirai.utils package net.mamoe.mirai.utils
import kotlinx.coroutines.io.ByteReadChannel
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.io.ByteReadChannel
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.io.core.Input import kotlinx.io.core.Input
import kotlinx.io.core.buildPacket import kotlinx.io.core.buildPacket
...@@ -60,6 +60,7 @@ suspend inline fun BufferedImage.suspendToExternalImage(): ExternalImage = withC ...@@ -60,6 +60,7 @@ suspend inline fun BufferedImage.suspendToExternalImage(): ExternalImage = withC
/** /**
* 读取文件头识别图片属性, 然后构造 [ExternalImage] * 读取文件头识别图片属性, 然后构造 [ExternalImage]
*/ */
@OptIn(MiraiInternalAPI::class)
@Throws(IOException::class) @Throws(IOException::class)
fun File.toExternalImage(): ExternalImage { fun File.toExternalImage(): ExternalImage {
val input = ImageIO.createImageInputStream(this) val input = ImageIO.createImageInputStream(this)
...@@ -71,7 +72,7 @@ fun File.toExternalImage(): ExternalImage { ...@@ -71,7 +72,7 @@ fun File.toExternalImage(): ExternalImage {
return ExternalImage( return ExternalImage(
width = image.getWidth(0), width = image.getWidth(0),
height = image.getHeight(0), height = image.getHeight(0),
md5 = this.inputStream().md5(), // dont change md5 = MiraiPlatformUtils.md5(this.inputStream()), // dont change
imageFormat = image.formatName, imageFormat = image.formatName,
input = this.inputStream(), input = this.inputStream(),
filename = this.name filename = this.name
......
...@@ -7,78 +7,103 @@ ...@@ -7,78 +7,103 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * 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 package net.mamoe.mirai.utils
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO import io.ktor.client.engine.cio.CIO
import io.ktor.util.KtorExperimentalAPI
import kotlinx.io.pool.useInstance import kotlinx.io.pool.useInstance
import net.mamoe.mirai.utils.io.ByteArrayPool 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.net.InetAddress
import java.security.MessageDigest import java.security.MessageDigest
import java.util.zip.Deflater
import java.util.zip.Inflater import java.util.zip.Inflater
actual fun md5(byteArray: ByteArray, offset: Int, length: Int): ByteArray = /**
MessageDigest.getInstance("MD5").apply { update(byteArray, offset, length) }.digest() * 时间戳
*/
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)
fun InputStream.md5(): ByteArray = this.use { val inflater = Inflater()
val digest = MessageDigest.getInstance("md5") inflater.reset()
digest.reset() ByteArrayOutputStream().use { output ->
this.use { input -> inflater.setInput(data, offset, length)
object : OutputStream() { ByteArrayPool.useInstance {
override fun write(b: Int) { while (!inflater.finished()) {
digest.update(b.toByte()) output.write(it, 0, inflater.inflate(it))
}
} }
}.use { output ->
input.copyTo(output) inflater.end()
return output.toByteArray()
} }
} }
return digest.digest()
} actual fun zip(data: ByteArray, offset: Int, length: Int): ByteArray {
data.checkOffsetAndLength(offset, length)
fun DataInput.md5(): ByteArray { if (length == 0) return ByteArray(0)
val digest = MessageDigest.getInstance("md5")
digest.reset() val inflater = Deflater()
val buffer = byteArrayOf(1) inflater.reset()
while (true) { ByteArrayOutputStream().use { output ->
try { inflater.setInput(data, offset, length)
this.readFully(buffer) ByteArrayPool.useInstance {
} catch (e: EOFException) { while (!inflater.finished()) {
break output.write(it, 0, inflater.deflate(it))
}
}
inflater.end()
return output.toByteArray()
} }
digest.update(buffer[0])
} }
return digest.digest()
}
actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress 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())
actual val Http: HttpClient get() = HttpClient(CIO) /**
* Ktor HttpClient. 不同平台使用不同引擎.
*/
@OptIn(KtorExperimentalAPI::class)
actual val Http: HttpClient
get() = HttpClient(CIO)
@OptIn(MiraiInternalAPI::class) /**
actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray { * Localhost 解析
this.checkOffsetAndLength(offset, length) */
if (length == 0) return ByteArray(0) actual fun localIpAddress(): String = runCatching {
InetAddress.getLocalHost().hostAddress
}.getOrElse { "192.168.1.123" }
val inflater = Inflater() fun md5(stream: InputStream): ByteArray {
inflater.reset() val digest = MessageDigest.getInstance("md5")
ByteArrayOutputStream().use { output -> digest.reset()
inflater.setInput(this, offset, length) stream.use { input ->
ByteArrayPool.useInstance { object : OutputStream() {
while (!inflater.finished()) { override fun write(b: Int) {
output.write(it, 0, inflater.inflate(it)) digest.update(b.toByte())
}
}.use { output ->
input.copyTo(output)
} }
} }
return digest.digest()
inflater.end()
return output.toByteArray()
} }
}
/** }
* 时间戳 \ No newline at end of file
*/
actual val currentTimeMillis: Long
get() = System.currentTimeMillis()
\ No newline at end of file
...@@ -15,6 +15,8 @@ import kotlinx.serialization.Transient ...@@ -15,6 +15,8 @@ import kotlinx.serialization.Transient
import kotlinx.serialization.UnstableDefault import kotlinx.serialization.UnstableDefault
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration 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.getRandomByteArray
import net.mamoe.mirai.utils.io.getRandomString import net.mamoe.mirai.utils.io.getRandomString
import java.io.File import java.io.File
...@@ -37,7 +39,7 @@ fun File.loadAsDeviceInfo(context: Context = ContextImpl()): DeviceInfo { ...@@ -37,7 +39,7 @@ fun File.loadAsDeviceInfo(context: Context = ContextImpl()): DeviceInfo {
private val JSON = Json(JsonConfiguration.Stable) private val JSON = Json(JsonConfiguration.Stable)
@Serializable @Serializable
@OptIn(ExperimentalUnsignedTypes::class) @OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
actual open class SystemDeviceInfo actual constructor() : DeviceInfo() { actual open class SystemDeviceInfo actual constructor() : DeviceInfo() {
actual constructor(context: Context) : this() { actual constructor(context: Context) : this() {
this.context = context this.context = context
......
...@@ -9,7 +9,8 @@ ...@@ -9,7 +9,8 @@
package net.mamoe.mirai.utils.cryptor 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 org.bouncycastle.jce.provider.BouncyCastleProvider
import java.security.* import java.security.*
import java.security.spec.ECGenParameterSpec import java.security.spec.ECGenParameterSpec
...@@ -58,6 +59,7 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) { ...@@ -58,6 +59,7 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
.genKeyPair()) .genKeyPair())
} }
@OptIn(MiraiInternalAPI::class)
actual fun calculateShareKey( actual fun calculateShareKey(
privateKey: ECDHPrivateKey, privateKey: ECDHPrivateKey,
publicKey: ECDHPublicKey publicKey: ECDHPublicKey
...@@ -65,7 +67,7 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) { ...@@ -65,7 +67,7 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
val instance = KeyAgreement.getInstance("ECDH", "BC") val instance = KeyAgreement.getInstance("ECDH", "BC")
instance.init(privateKey) instance.init(privateKey)
instance.doPhase(publicKey, true) instance.doPhase(publicKey, true)
return md5(instance.generateSecret()) return MiraiPlatformUtils.md5(instance.generateSecret())
} }
actual fun constructPublicKey(key: ByteArray): ECDHPublicKey { actual fun constructPublicKey(key: ByteArray): ECDHPublicKey {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment