Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
Mirai
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Mirai
Commits
932a3ef1
Commit
932a3ef1
authored
Feb 28, 2020
by
Him188
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Review: misc improvements
parent
1ff5df1d
Changes
34
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
34 changed files
with
676 additions
and
1411 deletions
+676
-1411
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt
...Main/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt
+1
-1
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt
...tlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt
+9
-6
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
.../mirai/qqandroid/network/protocol/packet/PacketFactory.kt
+10
-8
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
...ndroid/network/protocol/packet/chat/receive/OnlinePush.kt
+2
-5
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt
.../mirai/qqandroid/network/protocol/packet/login/WtLogin.kt
+17
-20
mirai-core-qqandroid/src/commonTest/kotlin/test/printing.kt
mirai-core-qqandroid/src/commonTest/kotlin/test/printing.kt
+55
-0
mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt
...d/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt
+0
-533
mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt
...d/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt
+0
-297
mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/utils.kt
...-qqandroid/src/jvmTest/kotlin/androidPacketTests/utils.kt
+0
-66
mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt
....mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt
+1
-1
mirai-core-qqandroid/src/jvmTest/kotlin/test/protoBuf.kt
mirai-core-qqandroid/src/jvmTest/kotlin/test/protoBuf.kt
+102
-0
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/BotConfigurationAndroid.kt
...n/kotlin/net/mamoe/mirai/utils/BotConfigurationAndroid.kt
+28
-18
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt
...androidMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt
+7
-1
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
...n/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
+0
-46
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformDatagramChannelAndroid.kt
...et/mamoe/mirai/utils/io/PlatformDatagramChannelAndroid.kt
+0
-2
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt
...droidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt
+1
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
+8
-10
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt
.../net.mamoe.mirai/event/internal/InternalEventListeners.kt
+1
-2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt
...monMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt
+0
-6
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt
...mmonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt
+5
-6
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt
...e/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt
+4
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/contentToString.kt
...ommonMain/kotlin/net.mamoe.mirai/utils/contentToString.kt
+167
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/TEA.kt
...rc/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/TEA.kt
+57
-94
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt
...mmonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt
+8
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformDatagramChannel.kt
...otlin/net.mamoe.mirai/utils/io/PlatformDatagramChannel.kt
+0
-6
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt
.../src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt
+0
-150
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt
...src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt
+142
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/input.kt
...e/src/commonMain/kotlin/net.mamoe.mirai/utils/io/input.kt
+14
-32
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/output.kt
.../src/commonMain/kotlin/net.mamoe.mirai/utils/io/output.kt
+4
-2
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt
...mMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt
+25
-40
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
.../jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
+1
-0
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
...n/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
+0
-54
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocketJvm.kt
...Main/kotlin/net/mamoe/mirai/utils/io/PlatformSocketJvm.kt
+0
-2
mirai-core/src/jvmTest/kotlin/mirai/test/testCaptchaPacket/TestCaptchaPacket.kt
.../kotlin/mirai/test/testCaptchaPacket/TestCaptchaPacket.kt
+7
-3
No files found.
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt
View file @
932a3ef1
...
...
@@ -10,6 +10,7 @@
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
net.mamoe.mirai.contact.Member
...
...
@@ -19,7 +20,6 @@ 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.io.discardExact
import
net.mamoe.mirai.utils.io.hexToBytes
import
net.mamoe.mirai.utils.io.read
import
net.mamoe.mirai.utils.io.toByteArray
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt
View file @
932a3ef1
...
...
@@ -7,12 +7,13 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@
file
:
Suppress
(
"NOTHING_TO_INLINE"
,
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.qqandroid.network
import
kotlinx.atomicfu.AtomicInt
import
kotlinx.atomicfu.atomic
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.toByteArray
import
kotlinx.io.core.*
import
net.mamoe.mirai.BotAccount
import
net.mamoe.mirai.RawAccountIdUse
import
net.mamoe.mirai.data.OnlineStatus
...
...
@@ -20,12 +21,10 @@ import net.mamoe.mirai.qqandroid.QQAndroidBot
import
net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import
net.mamoe.mirai.qqandroid.network.protocol.packet.PacketLogger
import
net.mamoe.mirai.qqandroid.network.protocol.packet.Tlv
import
net.mamoe.mirai.utils.DeviceInfo
import
net.mamoe.mirai.qqandroid.utils.NetworkType
import
net.mamoe.mirai.utils.SystemDeviceInfo
import
net.mamoe.mirai.utils.*
import
net.mamoe.mirai.utils.cryptor.ECDH
import
net.mamoe.mirai.utils.cryptor.
decryptBy
import
net.mamoe.mirai.utils.cryptor.
TEA
import
net.mamoe.mirai.utils.io.*
/*
...
...
@@ -72,7 +71,7 @@ internal open class QQAndroidClient(
internal
inline
fun
<
R
>
tryDecryptOrNull
(
data
:
ByteArray
,
size
:
Int
=
data
.
size
,
mapper
:
(
ByteArray
)
->
R
):
R
?
{
keys
.
forEach
{
(
key
,
value
)
->
kotlin
.
runCatching
{
return
mapper
(
data
.
decryptBy
(
value
,
size
).
also
{
PacketLogger
.
verbose
{
"成功使用 $key 解密"
}
})
return
mapper
(
TEA
.
decrypt
(
data
,
value
,
size
).
also
{
PacketLogger
.
verbose
{
"成功使用 $key 解密"
}
})
}
}
return
null
...
...
@@ -314,6 +313,10 @@ internal class Pt4Token(data: ByteArray, creationTime: Long, expireTime: Long) :
internal
typealias
PSKeyMap
=
MutableMap
<
String
,
PSKey
>
internal
typealias
Pt4TokenMap
=
MutableMap
<
String
,
Pt4Token
>
internal
inline
fun
Input
.
readUShortLVString
():
String
=
kotlinx
.
io
.
core
.
String
(
this
.
readUShortLVByteArray
())
internal
inline
fun
Input
.
readUShortLVByteArray
():
ByteArray
=
this
.
readBytes
(
this
.
readUShort
().
toInt
())
internal
fun
parsePSKeyMapAndPt4TokenMap
(
data
:
ByteArray
,
creationTime
:
Long
,
expireTime
:
Long
,
outPSKeyMap
:
PSKeyMap
,
outPt4TokenMap
:
Pt4TokenMap
)
=
data
.
read
{
repeat
(
readShort
().
toInt
())
{
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
View file @
932a3ef1
...
...
@@ -25,9 +25,10 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.ConfigPushSvc
import
net.mamoe.mirai.qqandroid.network.protocol.packet.login.Heartbeat
import
net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc
import
net.mamoe.mirai.qqandroid.network.protocol.packet.login.WtLogin
import
net.mamoe.mirai.qqandroid.network.readUShortLVByteArray
import
net.mamoe.mirai.utils.*
import
net.mamoe.mirai.utils.cryptor.TEA
import
net.mamoe.mirai.utils.cryptor.adjustToPublicKey
import
net.mamoe.mirai.utils.cryptor.decryptBy
import
net.mamoe.mirai.utils.io.*
import
kotlin.contracts.ExperimentalContracts
import
kotlin.contracts.contract
...
...
@@ -194,8 +195,8 @@ internal object KnownPacketFactories {
kotlin
.
runCatching
{
when
(
flag2
)
{
2
->
data
.
decryptBy
(
DECRYPTER_16_ZERO
,
size
).
also
{
PacketLogger
.
verbose
{
"成功使用 16 zero 解密"
}
}
1
->
data
.
decryptBy
(
bot
.
client
.
wLoginSigInfo
.
d2Key
,
size
).
also
{
PacketLogger
.
verbose
{
"成功使用 d2Key 解密"
}
}
2
->
TEA
.
decrypt
(
data
,
DECRYPTER_16_ZERO
,
size
).
also
{
PacketLogger
.
verbose
{
"成功使用 16 zero 解密"
}
}
1
->
TEA
.
decrypt
(
data
,
bot
.
client
.
wLoginSigInfo
.
d2Key
,
size
).
also
{
PacketLogger
.
verbose
{
"成功使用 d2Key 解密"
}
}
0
->
data
else
->
error
(
""
)
}
...
...
@@ -335,6 +336,7 @@ internal object KnownPacketFactories {
return
IncomingPacket
(
packetFactory
,
ssoSequenceId
,
packet
,
commandName
)
}
@UseExperimental
(
MiraiInternalAPI
::
class
)
private
suspend
fun
<
T
:
Packet
?
>
ByteReadPacket
.
parseOicqResponse
(
bot
:
QQAndroidBot
,
packetFactory
:
OutgoingPacketFactory
<
T
>,
...
...
@@ -352,10 +354,10 @@ internal object KnownPacketFactories {
this
.
discardExact
(
1
)
// const = 0
val
packet
=
when
(
encryptionMethod
)
{
4
->
{
var
data
=
this
.
decryptBy
(
bot
.
client
.
ecdh
.
keyPair
.
initialShareKey
,
(
this
.
remaining
-
1
).
toInt
())
var
data
=
TEA
.
decrypt
(
this
,
bot
.
client
.
ecdh
.
keyPair
.
initialShareKey
,
(
this
.
remaining
-
1
).
toInt
())
val
peerShareKey
=
bot
.
client
.
ecdh
.
calculateShareKeyByPeerPublicKey
(
readUShortLVByteArray
().
adjustToPublicKey
())
data
=
data
.
decryptBy
(
peerShareKey
)
data
=
TEA
.
decrypt
(
data
,
peerShareKey
)
packetFactory
.
decode
(
bot
,
data
)
}
...
...
@@ -366,13 +368,13 @@ internal object KnownPacketFactories {
this
.
readFully
(
byteArrayBuffer
,
0
,
size
)
runCatching
{
byteArrayBuffer
.
decryptBy
(
bot
.
client
.
ecdh
.
keyPair
.
initialShareKey
,
size
)
TEA
.
decrypt
(
byteArrayBuffer
,
bot
.
client
.
ecdh
.
keyPair
.
initialShareKey
,
size
)
}.
getOrElse
{
byteArrayBuffer
.
decryptBy
(
bot
.
client
.
randomKey
,
size
)
TEA
.
decrypt
(
byteArrayBuffer
,
bot
.
client
.
randomKey
,
size
)
}.
toReadPacket
()
}
}
else
{
this
.
decryptBy
(
bot
.
client
.
randomKey
,
0
,
(
this
.
remaining
-
1
).
toInt
())
TEA
.
decrypt
(
this
,
bot
.
client
.
randomKey
,
0
,
(
this
.
remaining
-
1
).
toInt
())
}
packetFactory
.
decode
(
bot
,
data
)
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
View file @
932a3ef1
...
...
@@ -11,10 +11,7 @@
package
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.readBytes
import
kotlinx.io.core.readUByte
import
kotlinx.io.core.readUInt
import
kotlinx.io.core.*
import
net.mamoe.mirai.contact.MemberPermission
import
net.mamoe.mirai.data.MultiPacket
import
net.mamoe.mirai.data.NoPacket
...
...
@@ -38,7 +35,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import
net.mamoe.mirai.qqandroid.network.protocol.packet.buildResponseUniPacket
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.debug
import
net.mamoe.mirai.utils.io.discardExact
import
net.mamoe.mirai.utils.io.read
import
net.mamoe.mirai.utils.io.readString
import
net.mamoe.mirai.utils.io.toUHexString
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt
View file @
932a3ef1
This diff is collapsed.
Click to expand it.
mirai-core
/src/commonMain/kotlin/net.mamoe.mirai/utils/io/debugg
ing.kt
→
mirai-core
-qqandroid/src/commonTest/kotlin/test/print
ing.kt
View file @
932a3ef1
...
...
@@ -8,81 +8,34 @@
*/
@
file
:
Suppress
(
"NOTHING_TO_INLINE"
)
@
file
:
JvmMultifileClass
@
file
:
JvmName
(
"Utils"
)
package
net.mamoe.mirai.utils.io
package
test
import
kotlinx.io.core.*
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.Input
import
kotlinx.io.core.readAvailable
import
kotlinx.io.core.use
import
kotlinx.io.pool.useInstance
import
net.mamoe.mirai.utils.DefaultLogger
import
net.mamoe.mirai.utils.Mirai
Debug
API
import
net.mamoe.mirai.utils.Mirai
Internal
API
import
net.mamoe.mirai.utils.MiraiLoggerWithSwitch
import
net.mamoe.mirai.utils.io.ByteArrayPool
import
net.mamoe.mirai.utils.io.toReadPacket
import
net.mamoe.mirai.utils.io.toUHexString
import
net.mamoe.mirai.utils.withSwitch
import
kotlin.contracts.ExperimentalContracts
import
kotlin.contracts.InvocationKind
import
kotlin.contracts.contract
import
kotlin.jvm.JvmMultifileClass
import
kotlin.jvm.JvmName
@MiraiDebugAPI
(
"Unsatble"
)
val
DebugLogger
:
MiraiLoggerWithSwitch
=
DefaultLogger
(
"Packet Debug"
).
withSwitch
(
false
)
val
DebugLogger
:
MiraiLoggerWithSwitch
=
DefaultLogger
(
"Packet Debug"
).
withSwitch
(
true
)
@MiraiDebugAPI
(
"Unstable"
)
inline
fun
Throwable
.
logStacktrace
(
message
:
String
?
=
null
)
=
DebugLogger
.
error
(
message
,
this
)
@MiraiDebugAPI
(
"Low efficiency."
)
inline
fun
String
.
debugPrintThis
(
name
:
String
):
String
{
DebugLogger
.
debug
(
"$name=$this"
)
return
this
}
@MiraiDebugAPI
(
"Low efficiency."
)
inline
fun
ByteArray
.
debugPrintThis
(
name
:
String
):
ByteArray
{
DebugLogger
.
debug
(
name
+
"="
+
this
.
toUHexString
())
return
this
}
@MiraiDebugAPI
(
"Low efficiency."
)
inline
fun
IoBuffer
.
debugPrintThis
(
name
:
String
):
IoBuffer
{
ByteArrayPool
.
useInstance
{
val
count
=
this
.
readAvailable
(
it
)
DebugLogger
.
debug
(
name
+
"="
+
it
.
toUHexString
(
offset
=
0
,
length
=
count
))
return
it
.
toIoBuffer
(
0
,
count
)
}
}
@MiraiDebugAPI
(
"Low efficiency."
)
inline
fun
IoBuffer
.
debugCopyUse
(
block
:
IoBuffer
.()
->
Unit
):
IoBuffer
{
ByteArrayPool
.
useInstance
{
val
count
=
this
.
readAvailable
(
it
)
block
(
it
.
toIoBuffer
(
0
,
count
))
return
it
.
toIoBuffer
(
0
,
count
)
}
}
@MiraiDebugAPI
(
"Low efficiency."
)
inline
fun
Input
.
debugDiscardExact
(
n
:
Number
,
name
:
String
=
""
)
{
DebugLogger
.
debug
(
"Discarded($n) $name="
+
this
.
readBytes
(
n
.
toInt
()).
toUHexString
())
}
@MiraiDebugAPI
(
"Low efficiency."
)
inline
fun
ByteReadPacket
.
debugPrintThis
(
name
:
String
=
""
):
ByteReadPacket
{
ByteArrayPool
.
useInstance
{
val
count
=
this
.
readAvailable
(
it
)
DebugLogger
.
debug
(
"ByteReadPacket $name="
+
it
.
toUHexString
(
offset
=
0
,
length
=
count
))
return
it
.
toReadPacket
(
0
,
count
)
}
}
/**
* 备份数据, 并在 [block] 失败后执行 [onFail].
*
* 此方法非常低效. 请仅在测试环境使用.
*/
@MiraiDebugAPI
(
"Low efficiency"
)
@UseExperimental
(
ExperimentalContracts
::
class
)
@UseExperimental
(
ExperimentalContracts
::
class
,
MiraiInternalAPI
::
class
)
inline
fun
<
R
>
Input
.
debugIfFail
(
name
:
String
=
""
,
onFail
:
(
ByteArray
)
->
ByteReadPacket
=
{
it
.
toReadPacket
()
},
block
:
ByteReadPacket
.()
->
R
):
R
{
contract
{
...
...
mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt
deleted
100644 → 0
View file @
1ff5df1d
This diff is collapsed.
Click to expand it.
mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt
deleted
100644 → 0
View file @
1ff5df1d
This diff is collapsed.
Click to expand it.
mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/utils.kt
deleted
100644 → 0
View file @
1ff5df1d
/*
* 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
*/
@
file
:
Suppress
(
"DEPRECATION"
)
package
androidPacketTests
import
net.mamoe.mirai.utils.cryptor.decryptBy
import
org.bouncycastle.jce.provider.JCEECPrivateKey
import
org.bouncycastle.jce.spec.ECParameterSpec
import
org.bouncycastle.jce.spec.ECPrivateKeySpec
import
org.bouncycastle.math.ec.ECConstants
import
org.bouncycastle.math.ec.ECCurve
import
org.bouncycastle.util.encoders.Hex
import
java.math.BigInteger
import
java.security.interfaces.ECPrivateKey
fun
ByteArray
.
decryptBy16Zero
()
=
this
.
decryptBy
(
ByteArray
(
16
))
fun
ByteArray
.
dropTCPHead
():
ByteArray
=
this
.
drop
(
16
*
3
+
6
).
toByteArray
()
@Suppress
(
"LocalVariableName"
)
fun
loadPrivateKey
(
s
:
String
):
ECPrivateKey
{
fun
fromHex
(
hex
:
String
):
BigInteger
{
return
BigInteger
(
1
,
Hex
.
decode
(
hex
))
}
// p = 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1
// p = 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1
val
p
=
fromHex
(
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37"
)
val
a
=
ECConstants
.
ZERO
val
b
=
BigInteger
.
valueOf
(
3
)
val
n
=
fromHex
(
"FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D"
)
val
h
=
BigInteger
.
valueOf
(
1
)
val
curve
:
ECCurve
=
ECCurve
.
Fp
(
p
,
a
,
b
)
//ECPoint G = curve.decodePoint(Hex.decode("03"
//+ "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D"));
//ECPoint G = curve.decodePoint(Hex.decode("03"
//+ "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D"));
val
G
=
curve
.
decodePoint
(
Hex
.
decode
(
"04"
+
"DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D"
+
"9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D"
)
)
return
JCEECPrivateKey
(
"EC"
,
ECPrivateKeySpec
(
fromHex
(
s
),
ECParameterSpec
(
curve
,
G
,
n
,
h
)
)
)
// return KeyFactory.getInstance("ECDH").generatePrivate(PKCS8EncodedKeySpec(s))
}
mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt
View file @
932a3ef1
...
...
@@ -17,7 +17,7 @@ import kotlinx.serialization.Serializable
import net.mamoe.mirai.qqandroid.io.JceOutput
import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.qqandroid.io.buildJcePacket
import net.mamoe.mirai.utils.c
ryptor.c
ontentToString
import net.mamoe.mirai.utils.contentToString
import net.mamoe.mirai.utils.io.toUHexString
import kotlin.test.Test
import kotlin.test.assertEquals
...
...
mirai-core-qqandroid/src/jvmTest/kotlin/test/protoBuf.kt
0 → 100644
View file @
932a3ef1
/*
* 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
*/
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"unused"
,
"NO_REFLECTION_IN_CLASS_PATH"
)
package
net.mamoe.mirai.utils.cryptor
import
net.mamoe.mirai.utils.MiraiDebugAPI
// ProtoBuf utilities
@Suppress
(
"FunctionName"
,
"SpellCheckingInspection"
)
/*
* Type Meaning Used For
* 0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
* 1 64-bit fixed64, sfixed64, double
* 2 Length-delimi string, bytes, embedded messages, packed repeated fields
* 3 Start group Groups (deprecated)
* 4 End group Groups (deprecated)
* 5 32-bit fixed32, sfixed32, float
*
* https://www.jianshu.com/p/f888907adaeb
*/
@MiraiDebugAPI
fun
ProtoFieldId
(
serializedId
:
UInt
):
ProtoFieldId
=
ProtoFieldId
(
protoFieldNumber
(
serializedId
),
protoType
(
serializedId
)
)
@MiraiDebugAPI
data class
ProtoFieldId
(
val
fieldNumber
:
Int
,
val
type
:
ProtoType
)
{
override
fun
toString
():
String
=
"$type $fieldNumber"
}
@Suppress
(
"SpellCheckingInspection"
)
@MiraiDebugAPI
enum
class
ProtoType
(
val
value
:
Byte
,
private
val
typeName
:
String
)
{
/**
* int32, int64, uint32, uint64, sint32, sint64, bool, enum
*/
VAR_INT
(
0
x00
,
"varint"
),
/**
* fixed64, sfixed64, double
*/
BIT_64
(
0
x01
,
" 64bit"
),
/**
* string, bytes, embedded messages, packed repeated fields
*/
LENGTH_DELIMI
(
0
x02
,
"delimi"
),
/**
* Groups (deprecated)
*/
START_GROUP
(
0
x03
,
"startg"
),
/**
* Groups (deprecated)
*/
END_GROUP
(
0
x04
,
" endg"
),
/**
* fixed32, sfixed32, float
*/
BIT_32
(
0
x05
,
" 32bit"
),
;
override
fun
toString
():
String
=
this
.
typeName
companion
object
{
fun
valueOf
(
value
:
Byte
):
ProtoType
=
values
().
firstOrNull
{
it
.
value
==
value
}
?:
error
(
"Unknown ProtoType $value"
)
}
}
/**
* 由 ProtoBuf 序列化后的 id 得到类型
*
* serializedId = (fieldNumber << 3) | wireType
*/
@MiraiDebugAPI
fun
protoType
(
number
:
UInt
):
ProtoType
=
ProtoType
.
valueOf
(
number
.
toInt
().
shl
(
29
).
ushr
(
29
).
toByte
())
/**
* ProtoBuf 序列化后的 id 转为序列前标记的 id
*
* serializedId = (fieldNumber << 3) | wireType
*/
@MiraiDebugAPI
fun
protoFieldNumber
(
number
:
UInt
):
Int
=
number
.
toInt
().
ushr
(
3
)
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/BotConfigurationAndroid.kt
View file @
932a3ef1
...
...
@@ -15,23 +15,6 @@ import net.mamoe.mirai.network.BotNetworkHandler
import
kotlin.coroutines.CoroutineContext
import
kotlin.coroutines.EmptyCoroutineContext
/**
* 在各平台实现的默认的验证码处理器.
*/
actual
var
defaultLoginSolver
:
LoginSolver
=
object
:
LoginSolver
()
{
override
suspend
fun
onSolvePicCaptcha
(
bot
:
Bot
,
data
:
IoBuffer
):
String
?
{
error
(
"should be implemented manually by you"
)
}
override
suspend
fun
onSolveSliderCaptcha
(
bot
:
Bot
,
url
:
String
):
String
?
{
error
(
"should be implemented manually by you"
)
}
override
suspend
fun
onSolveUnsafeDeviceLoginVerify
(
bot
:
Bot
,
url
:
String
):
String
?
{
error
(
"should be implemented manually by you"
)
}
}
@Suppress
(
"ClassName"
,
"PropertyName"
)
actual
open
class
BotConfiguration
actual
constructor
()
{
/**
...
...
@@ -76,7 +59,7 @@ actual open class BotConfiguration actual constructor() {
/**
* 验证码处理器
*/
actual
var
loginSolver
:
LoginSolver
=
defaultLoginSolver
actual
var
loginSolver
:
LoginSolver
=
LoginSolver
.
Default
actual
companion
object
{
/**
...
...
@@ -115,4 +98,31 @@ inline class FileBasedDeviceInfo @BotConfigurationDsl constructor(val filepath:
*/
@BotConfigurationDsl
companion
object
ByDeviceDotJson
}
/**
* 验证码, 设备锁解决器
*/
actual
abstract
class
LoginSolver
{
actual
abstract
suspend
fun
onSolvePicCaptcha
(
bot
:
Bot
,
data
:
IoBuffer
):
String
?
actual
abstract
suspend
fun
onSolveSliderCaptcha
(
bot
:
Bot
,
url
:
String
):
String
?
actual
abstract
suspend
fun
onSolveUnsafeDeviceLoginVerify
(
bot
:
Bot
,
url
:
String
):
String
?
actual
companion
object
{
actual
val
Default
:
LoginSolver
get
()
=
object
:
LoginSolver
()
{
override
suspend
fun
onSolvePicCaptcha
(
bot
:
Bot
,
data
:
IoBuffer
):
String
?
{
error
(
"should be implemented manually by you"
)
}
override
suspend
fun
onSolveSliderCaptcha
(
bot
:
Bot
,
url
:
String
):
String
?
{
error
(
"should be implemented manually by you"
)
}
override
suspend
fun
onSolveUnsafeDeviceLoginVerify
(
bot
:
Bot
,
url
:
String
):
String
?
{
error
(
"should be implemented manually by you"
)
}
}
}
}
\ No newline at end of file
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt
View file @
932a3ef1
package
net.mamoe.mirai.utils
import
android.os.Build
private
var
isAddSuppressedSupported
:
Boolean
=
true
@MiraiInternalAPI
...
...
@@ -9,7 +11,11 @@ actual fun Throwable.addSuppressed(e: Throwable) {
return
}
try
{
this
.
addSuppressed
(
e
)
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
KITKAT
)
{
this
.
addSuppressed
(
e
)
}
else
{
isAddSuppressedSupported
=
false
}
}
catch
(
e
:
Exception
)
{
isAddSuppressedSupported
=
false
}
...
...
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
deleted
100644 → 0
View file @
1ff5df1d
/*
* 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.utils.cryptor
import
net.mamoe.mirai.utils.MiraiDebugAPI
import
java.lang.reflect.Field
import
kotlin.reflect.full.allSuperclasses
@MiraiDebugAPI
actual
fun
Any
.
contentToStringReflectively
(
prefix
:
String
,
filter
:
((
name
:
String
,
value
:
Any
?)
->
Boolean
)?):
String
{
return
(
this
::
class
.
simpleName
?:
"<UnnamedClass>"
)
+
"#"
+
this
::
class
.
hashCode
()
+
" {\n"
+
this
.
allFieldsFromSuperClassesMatching
{
it
.
name
.
startsWith
(
"net.mamoe.mirai"
)
}
.
distinctBy
{
it
.
name
}
.
filterNot
{
it
.
name
.
contains
(
"$"
)
||
it
.
name
==
"Companion"
||
it
.
isSynthetic
||
it
.
name
==
"serialVersionUID"
}
.
joinToStringPrefixed
(
prefix
=
prefix
)
{
it
.
isAccessible
=
true
if
(
filter
!=
null
)
{
kotlin
.
runCatching
{
if
(!
filter
(
it
.
name
,
it
.
get
(
this
)))
return
@
joinToStringPrefixed
""
}
}
it
.
name
+
"="
+
kotlin
.
runCatching
{
val
value
=
it
.
get
(
this
)
if
(
value
==
this
)
"<this>"
else
value
.
contentToString
(
prefix
)
}.
getOrElse
{
"<!>"
}
}
+
"\n$prefix}"
}
internal
fun
Any
.
allFieldsFromSuperClassesMatching
(
classFilter
:
(
Class
<
out
Any
>)
->
Boolean
):
Sequence
<
Field
>
{
return
(
this
::
class
.
java
.
takeIf
(
classFilter
)
?.
declaredFields
?.
asSequence
()
?:
sequenceOf
<
Field
>())
+
this
::
class
.
allSuperclasses
.
asSequence
()
.
map
{
it
.
java
}
.
filter
(
classFilter
)
.
flatMap
{
it
.
declaredFields
.
asSequence
()
}
}
\ No newline at end of file
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformDatagramChannelAndroid.kt
View file @
932a3ef1
...
...
@@ -20,8 +20,6 @@ import java.nio.channels.DatagramChannel
import
java.nio.channels.ReadableByteChannel
import
java.nio.channels.WritableByteChannel
actual
typealias
ClosedChannelException
=
java
.
nio
.
channels
.
ClosedChannelException
/**
* 多平台适配的 DatagramChannel.
*/
...
...
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt
View file @
932a3ef1
...
...
@@ -75,6 +75,7 @@ private inline fun InputStream.readInSequence(block: (Int) -> Unit) {
}
}
@UseExperimental
(
MiraiInternalAPI
::
class
)
actual
fun
ByteArray
.
unzip
(
offset
:
Int
,
length
:
Int
):
ByteArray
{
this
.
checkOffsetAndLength
(
offset
,
length
)
if
(
length
==
0
)
return
ByteArray
(
0
)
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
View file @
932a3ef1
...
...
@@ -22,7 +22,6 @@ import net.mamoe.mirai.network.ForceOfflineException
import
net.mamoe.mirai.network.LoginFailedException
import
net.mamoe.mirai.network.closeAndJoin
import
net.mamoe.mirai.utils.*
import
net.mamoe.mirai.utils.io.logStacktrace
import
kotlin.coroutines.CoroutineContext
/*
...
...
@@ -144,17 +143,16 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
}
suspend
fun
doInit
()
{
repeat
(
2
)
{
try
{
_network
.
init
()
return
}
catch
(
e
:
Exception
)
{
e
.
logStacktrace
()
tryNTimesOrException
(
2
)
{
if
(
it
!=
0
)
{
delay
(
3000
)
logger
.
warning
(
"Init failed. Retrying in 3s..."
)
}
logger
.
warning
(
"Init failed. Retrying in 3s..."
)
delay
(
3000
)
_network
.
init
()
}
?.
let
{
network
.
logger
.
error
(
it
)
logger
.
error
(
"cannot init. some features may be affected"
)
}
logger
.
error
(
"cannot init. some features may be affected"
)
}
logger
.
info
(
"Initializing BotNetworkHandler"
)
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt
View file @
932a3ef1
...
...
@@ -17,7 +17,6 @@ import net.mamoe.mirai.event.EventDisabled
import
net.mamoe.mirai.event.Listener
import
net.mamoe.mirai.event.ListeningStatus
import
net.mamoe.mirai.utils.*
import
net.mamoe.mirai.utils.io.logStacktrace
import
kotlin.coroutines.CoroutineContext
import
kotlin.coroutines.coroutineContext
import
kotlin.jvm.JvmField
...
...
@@ -65,8 +64,8 @@ internal class Handler<in E : Event>
MiraiLogger
.
warning
(
"""Event processing: An exception occurred but no CoroutineExceptionHandler found,
either in coroutineContext from Handler job, or in subscriberContext"""
.
trimIndent
()
,
e
)
e
.
logStacktrace
(
"Event processing(No CoroutineExceptionHandler found)"
)
}
// this.complete() // do not `completeExceptionally`, otherwise parentJob will fai`l.
// ListeningStatus.STOPPED
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt
View file @
932a3ef1
...
...
@@ -34,7 +34,6 @@ import kotlin.coroutines.EmptyCoroutineContext
* @see CoroutineScope.incoming
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
inline
fun
<
R
>
CoroutineScope
.
subscribeMessages
(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
crossinline
listeners
:
MessageSubscribersBuilder
<
MessagePacket
<
*
,
*
>>.()
->
R
...
...
@@ -60,7 +59,6 @@ inline fun <R> CoroutineScope.subscribeMessages(
* @see CoroutineScope.incoming
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
inline
fun
<
R
>
CoroutineScope
.
subscribeGroupMessages
(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
crossinline
listeners
:
MessageSubscribersBuilder
<
GroupMessage
>.()
->
R
...
...
@@ -81,7 +79,6 @@ inline fun <R> CoroutineScope.subscribeGroupMessages(
* @see CoroutineScope.incoming
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
inline
fun
<
R
>
CoroutineScope
.
subscribeFriendMessages
(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
crossinline
listeners
:
MessageSubscribersBuilder
<
FriendMessage
>.()
->
R
...
...
@@ -102,7 +99,6 @@ inline fun <R> CoroutineScope.subscribeFriendMessages(
* @see CoroutineScope.incoming
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
inline
fun
<
R
>
Bot
.
subscribeMessages
(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
crossinline
listeners
:
MessageSubscribersBuilder
<
MessagePacket
<
*
,
*
>>.()
->
R
...
...
@@ -125,7 +121,6 @@ inline fun <R> Bot.subscribeMessages(
* @see CoroutineScope.incoming
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
inline
fun
<
R
>
Bot
.
subscribeGroupMessages
(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
crossinline
listeners
:
MessageSubscribersBuilder
<
GroupMessage
>.()
->
R
...
...
@@ -146,7 +141,6 @@ inline fun <R> Bot.subscribeGroupMessages(
* @see CoroutineScope.incoming
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
inline
fun
<
R
>
Bot
.
subscribeFriendMessages
(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
crossinline
listeners
:
MessageSubscribersBuilder
<
FriendMessage
>.()
->
R
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt
View file @
932a3ef1
...
...
@@ -18,18 +18,17 @@ import kotlin.jvm.JvmStatic
/**
* 验证码, 设备锁解决器
*/
abstract
class
LoginSolver
{
expect
abstract
class
LoginSolver
{
abstract
suspend
fun
onSolvePicCaptcha
(
bot
:
Bot
,
data
:
IoBuffer
):
String
?
abstract
suspend
fun
onSolveSliderCaptcha
(
bot
:
Bot
,
url
:
String
):
String
?
abstract
suspend
fun
onSolveUnsafeDeviceLoginVerify
(
bot
:
Bot
,
url
:
String
):
String
?
}
/**
* 在各平台实现的默认的验证码处理器.
*/
expect
var
defaultLoginSolver
:
LoginSolver
companion
object
{
val
Default
:
LoginSolver
}
}
/**
* [Bot] 配置
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt
View file @
932a3ef1
...
...
@@ -29,6 +29,7 @@ import kotlin.jvm.JvmName
* 从接收者管道读取所有数据并写入 [dst]. 不会关闭 [dst]
*/
suspend
fun
ByteReadChannel
.
copyTo
(
dst
:
OutputStream
)
{
@UseExperimental
(
MiraiInternalAPI
::
class
)
ByteArrayPool
.
useInstance
{
do
{
val
size
=
this
.
readAvailable
(
it
)
...
...
@@ -41,6 +42,7 @@ suspend fun ByteReadChannel.copyTo(dst: OutputStream) {
* 从接收者管道读取所有数据并写入 [dst]. 不会关闭 [dst]
*/
suspend
fun
ByteReadChannel
.
copyTo
(
dst
:
Output
)
{
@UseExperimental
(
MiraiInternalAPI
::
class
)
ByteArrayPool
.
useInstance
{
do
{
val
size
=
this
.
readAvailable
(
it
)
...
...
@@ -72,6 +74,7 @@ suspend fun ByteReadChannel.copyTo(dst: kotlinx.coroutines.io.ByteWriteChannel)
*/
suspend
fun
ByteReadChannel
.
copyAndClose
(
dst
:
OutputStream
)
{
try
{
@UseExperimental
(
MiraiInternalAPI
::
class
)
ByteArrayPool
.
useInstance
{
do
{
val
size
=
this
.
readAvailable
(
it
)
...
...
@@ -88,6 +91,7 @@ suspend fun ByteReadChannel.copyAndClose(dst: OutputStream) {
*/
suspend
fun
ByteReadChannel
.
copyAndClose
(
dst
:
Output
)
{
try
{
@UseExperimental
(
MiraiInternalAPI
::
class
)
ByteArrayPool
.
useInstance
{
do
{
val
size
=
this
.
readAvailable
(
it
)
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/c
ryptor/protoBuf
.kt
→
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/c
ontentToString
.kt
View file @
932a3ef1
This diff is collapsed.
Click to expand it.
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/TEA.kt
View file @
932a3ef1
...
...
@@ -10,8 +10,8 @@
package
net.mamoe.mirai.utils.cryptor
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.IoBuffer
import
kotlinx.io.pool.useInstance
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.io.ByteArrayPool
import
net.mamoe.mirai.utils.io.toByteArray
import
net.mamoe.mirai.utils.io.toUHexString
...
...
@@ -20,7 +20,6 @@ import kotlin.experimental.xor
import
kotlin.jvm.JvmStatic
import
kotlin.random.Random
/**
* 解密错误
*/
...
...
@@ -29,98 +28,52 @@ class DecryptionFailedException : Exception {
constructor
(
message
:
String
?)
:
super
(
message
)
}
// region encrypt
/**
* 使用 [key] 解密 [this]
*
* @param key 长度至少为 16
* @throws DecryptionFailedException 解密错误时
*/
fun
ByteArray
.
encryptBy
(
key
:
ByteArray
,
length
:
Int
=
this
.
size
):
ByteArray
=
TEA
.
encrypt
(
this
,
key
,
sourceLength
=
length
)
/**
* 在 [ByteArrayPool] 缓存 [this], 然后使用 [key] 加密.
*
* @param key 长度至少为 16
* @consumer 由于缓存需要被回收, 需在方法内执行解密后明文的消耗过程
* @throws DecryptionFailedException 解密错误时
*/
inline
fun
ByteReadPacket
.
encryptBy
(
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
remaining
.
toInt
()
-
offset
,
consumer
:
(
ByteArray
)
->
Unit
)
{
ByteArrayPool
.
useInstance
{
this
.
readFully
(
it
,
offset
,
length
)
consumer
(
it
.
encryptBy
(
key
,
length
=
length
))
}
}
// endregion
// region decrypt
/**
* 使用 [key] 解密 [this].
*
* @param key 固定长度 16
* @throws DecryptionFailedException 解密错误时
*/
fun
ByteArray
.
decryptBy
(
key
:
ByteArray
,
length
:
Int
=
this
.
size
):
ByteArray
=
TEA
.
decrypt
(
checkDataLengthAndReturnSelf
(
length
),
key
,
sourceLength
=
length
)
/**
* 使用 [key] 解密 [this].
* [key] 将会被读取掉前 16 个字节
* 将会使用 [ByteArrayPool] 来缓存 [key].
* TEA 算法加密解密工具类.
*
* @param key 长度至少为 16
* @throws DecryptionFailedException 解密错误时
* **注意**: 此为 Mirai 内部 API. 它可能会在任何时刻被改变.
*/
fun
ByteArray
.
decryptBy
(
key
:
IoBuffer
,
length
:
Int
=
this
.
size
):
ByteArray
{
checkDataLengthAndReturnSelf
(
length
)
return
ByteArrayPool
.
useInstance
{
keyBuffer
->
key
.
readFully
(
keyBuffer
,
0
,
key
.
readRemaining
)
TEA
.
decrypt
(
this
,
keyBuffer
,
sourceLength
=
length
)
@MiraiInternalAPI
object
TEA
{
// TODO: 2020/2/28 使用 stream 式输入以避免缓存
/**
* 在 [ByteArrayPool] 缓存 [this], 然后使用 [key] 加密.
*
* @param key 长度至少为 16
* @consumer 由于缓存需要被回收, 需在方法内执行解密后明文的消耗过程
* @throws DecryptionFailedException 解密错误时
*/
inline
fun
encrypt
(
receiver
:
ByteReadPacket
,
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
receiver
.
remaining
.
toInt
()
-
offset
,
consumer
:
(
ByteArray
)
->
Unit
)
{
ByteArrayPool
.
useInstance
{
receiver
.
readFully
(
it
,
offset
,
length
)
consumer
(
encrypt
(
it
,
key
,
length
=
length
))
}
}
}
/**
* 在 [ByteArrayPool] 缓存 [this], 然后使用 [key] 解密.
*
* @param key 长度至少为 16
* @throws DecryptionFailedException 解密错误时
*/
fun
IoBuffer
.
decryptBy
(
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
readRemaining
-
offset
):
ByteArray
{
return
ByteArrayPool
.
useInstance
{
this
.
readFully
(
it
,
offset
,
length
)
it
.
checkDataLengthAndReturnSelf
(
length
)
TEA
.
decrypt
(
it
,
key
,
length
)
@JvmStatic
fun
decrypt
(
receiver
:
ByteReadPacket
,
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
(
receiver
.
remaining
-
offset
).
toInt
()):
ByteReadPacket
=
decryptAsByteArray
(
receiver
,
key
,
offset
,
length
)
{
data
->
ByteReadPacket
(
data
)
}
inline
fun
<
R
>
decryptAsByteArray
(
receiver
:
ByteReadPacket
,
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
(
receiver
.
remaining
-
offset
).
toInt
(),
consumer
:
(
ByteArray
)
->
R
):
R
{
return
ByteArrayPool
.
useInstance
{
receiver
.
readFully
(
it
,
offset
,
length
)
consumer
(
decrypt
(
it
,
key
,
length
))
}.
also
{
receiver
.
close
()
}
}
}
// endregion
// region ByteReadPacket extension
fun
ByteReadPacket
.
decryptBy
(
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
(
this
.
remaining
-
offset
).
toInt
()):
ByteReadPacket
=
decryptAsByteArray
(
key
,
offset
,
length
)
{
data
->
ByteReadPacket
(
data
)
}
fun
ByteReadPacket
.
decryptBy
(
key
:
IoBuffer
,
offset
:
Int
=
0
,
length
:
Int
=
(
this
.
remaining
-
offset
).
toInt
()):
ByteReadPacket
=
decryptAsByteArray
(
key
,
offset
,
length
)
{
data
->
ByteReadPacket
(
data
)
}
inline
fun
<
R
>
ByteReadPacket
.
decryptAsByteArray
(
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
(
this
.
remaining
-
offset
).
toInt
(),
consumer
:
(
ByteArray
)
->
R
):
R
=
ByteArrayPool
.
useInstance
{
readFully
(
it
,
offset
,
length
)
consumer
(
it
.
decryptBy
(
key
,
length
))
}.
also
{
close
()
}
inline
fun
<
R
>
ByteReadPacket
.
decryptAsByteArray
(
key
:
IoBuffer
,
offset
:
Int
=
0
,
length
:
Int
=
(
this
.
remaining
-
offset
).
toInt
(),
consumer
:
(
ByteArray
)
->
R
):
R
=
ByteArrayPool
.
useInstance
{
readFully
(
it
,
offset
,
length
)
consumer
(
it
.
decryptBy
(
key
,
length
))
}.
also
{
close
()
}
// endregion
private
object
TEA
{
private
const
val
UINT32_MASK
=
0
xffffffffL
private
fun
doOption
(
data
:
ByteArray
,
key
:
ByteArray
,
length
:
Int
,
encrypt
:
Boolean
):
ByteArray
{
...
...
@@ -345,15 +298,25 @@ private object TEA {
private
fun
fail
():
Nothing
=
throw
DecryptionFailedException
()
@PublishedApi
/**
* 使用 [key] 加密 [source]
*
* @param key 长度至少为 16
* @throws DecryptionFailedException 解密错误时
*/
@JvmStatic
internal
fun
encrypt
(
source
:
ByteArray
,
key
:
ByteArray
,
sourceLength
:
Int
=
source
.
size
):
ByteArray
=
doOption
(
source
,
key
,
sourceLength
,
true
)
@PublishedApi
fun
encrypt
(
source
:
ByteArray
,
key
:
ByteArray
,
length
:
Int
=
source
.
size
):
ByteArray
=
doOption
(
source
,
key
,
length
,
true
)
/**
* 使用 [key] 解密 [source]
*
* @param key 长度至少为 16
* @throws DecryptionFailedException 解密错误时
*/
@JvmStatic
internal
fun
decrypt
(
source
:
ByteArray
,
key
:
ByteArray
,
sourceL
ength
:
Int
=
source
.
size
):
ByteArray
=
doOption
(
source
,
key
,
sourceL
ength
,
false
)
fun
decrypt
(
source
:
ByteArray
,
key
:
ByteArray
,
l
ength
:
Int
=
source
.
size
):
ByteArray
=
doOption
(
source
,
key
,
l
ength
,
false
)
private
fun
ByteArray
.
pack
(
offset
:
Int
,
len
:
Int
):
Long
{
var
result
:
Long
=
0
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt
View file @
932a3ef1
...
...
@@ -30,5 +30,13 @@ object ByteArrayPool : DefaultPool<ByteArray>(256) {
override
fun
produceInstance
():
ByteArray
=
ByteArray
(
BUFFER_SIZE
)
override
fun
clearInstance
(
instance
:
ByteArray
):
ByteArray
=
instance
fun
checkBufferSize
(
size
:
Int
)
{
require
(
size
<=
BUFFER_SIZE
)
{
"sizePerPacket is too large. Maximum buffer size=$BUFFER_SIZE"
}
}
fun
checkBufferSize
(
size
:
Long
)
{
require
(
size
<=
BUFFER_SIZE
)
{
"sizePerPacket is too large. Maximum buffer size=$BUFFER_SIZE"
}
}
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformDatagramChannel.kt
View file @
932a3ef1
...
...
@@ -11,7 +11,6 @@ package net.mamoe.mirai.utils.io
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.Closeable
import
kotlinx.io.errors.IOException
import
net.mamoe.mirai.utils.MiraiInternalAPI
/**
...
...
@@ -32,11 +31,6 @@ expect class PlatformDatagramChannel(serverHost: String, serverPort: Short) : Cl
val
isOpen
:
Boolean
}
/**
* Channel 被关闭
*/
expect
class
ClosedChannelException
:
IOException
/**
* 在 [PlatformDatagramChannel.send] 或 [PlatformDatagramChannel.read] 时出现的错误.
*/
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt
deleted
100644 → 0
View file @
1ff5df1d
/*
* 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
*/
@
file
:
JvmName
(
"Varint"
)
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.utils.io
import
kotlinx.io.core.Input
import
kotlinx.io.core.Output
import
kotlin.experimental.or
import
kotlin.jvm.JvmName
import
kotlin.jvm.JvmSynthetic
/**
* Tool class for VarInt or VarLong operations.
*
* Some code from http://wiki.vg/Protocol.
*
* Source project: [Nukkit](http://github.com/nukkit/nukkit)
*
* @author MagicDroidX from Nukkit Project
* @author lmlstarqaq from Nukkit Project
*/
internal
fun
encodeZigZag32
(
signedInt
:
Int
):
Long
{
return
(
signedInt
shl
1
xor
(
signedInt
shr
31
)).
toLong
()
}
@JvmSynthetic
internal
fun
decodeZigZag32
(
uint
:
UInt
):
Int
{
return
decodeZigZag32
(
uint
.
toLong
())
}
internal
fun
decodeZigZag32
(
uint
:
Long
):
Int
{
return
(
uint
shr
1
).
toInt
()
xor
-(
uint
and
1
).
toInt
()
}
internal
fun
encodeZigZag64
(
signedLong
:
Long
):
Long
{
return
signedLong
shl
1
xor
(
signedLong
shr
63
)
}
internal
fun
decodeZigZag64
(
signedLong
:
Long
):
Long
{
return
signedLong
.
ushr
(
1
)
xor
-(
signedLong
and
1
)
}
inline
class
UVarInt
(
val
data
:
UInt
)
@JvmSynthetic
fun
Input
.
readUVarInt
():
UInt
{
return
read
(
this
,
5
).
toUInt
()
}
fun
Input
.
readVarLong
():
Long
{
return
decodeZigZag64
(
readUVarLong
().
toLong
())
}
@JvmSynthetic
fun
Input
.
readUVarLong
():
ULong
{
return
read
(
this
,
10
).
toULong
()
}
fun
Output
.
writeVarInt
(
signedInt
:
Int
)
{
this
.
writeUVarInt
(
encodeZigZag32
(
signedInt
))
}
@JvmSynthetic
fun
Output
.
writeUVarInt
(
uint
:
UInt
)
{
return
writeUVarInt
(
uint
.
toLong
())
}
fun
Output
.
writeUVarInt
(
uint
:
Long
)
{
this
.
write0
(
uint
)
}
fun
Output
.
writeVarLong
(
signedLong
:
Long
)
{
this
.
writeUVarLong
(
encodeZigZag64
(
signedLong
))
}
fun
Output
.
writeUVarLong
(
ulong
:
Long
)
{
this
.
write0
(
ulong
)
}
fun
UVarInt
.
toByteArray
():
ByteArray
{
val
list
=
mutableListOf
<
Byte
>()
var
value
=
this
.
data
.
toLong
()
do
{
var
temp
=
(
value
and
127
).
toByte
()
value
=
value
ushr
7
if
(
value
!=
0L
)
{
temp
=
temp
or
128
.
toByte
()
}
list
+=
temp
}
while
(
value
!=
0L
)
return
list
.
toByteArray
()
}
fun
UVarInt
.
toUHexString
(
separator
:
String
=
" "
):
String
=
buildString
{
var
value
=
data
.
toLong
()
var
isFirst
=
true
do
{
if
(!
isFirst
)
{
append
(
separator
)
}
var
temp
=
(
value
and
127
).
toByte
()
value
=
value
ushr
7
if
(
value
!=
0L
)
{
temp
=
temp
or
128
.
toByte
()
}
append
(
temp
.
toUByte
().
fixToUHex
())
isFirst
=
false
}
while
(
value
!=
0L
)
}
private
fun
Output
.
write0
(
long
:
Long
)
{
var
value
=
long
do
{
var
temp
=
(
value
and
127
).
toByte
()
value
=
value
ushr
7
if
(
value
!=
0L
)
{
temp
=
temp
or
128
.
toByte
()
}
this
.
writeByte
(
temp
)
}
while
(
value
!=
0L
)
}
private
fun
read
(
stream
:
Input
,
maxSize
:
Int
):
Long
{
var
value
:
Long
=
0
var
size
=
0
var
b
=
stream
.
readByte
().
toInt
()
while
(
b
and
0
x80
==
0
x80
)
{
value
=
value
or
((
b
and
0
x7F
).
toLong
()
shl
size
++
*
7
)
require
(
size
<
maxSize
)
{
"VarLong too big(expecting maxSize=$maxSize)"
}
b
=
stream
.
readByte
().
toInt
()
}
return
value
or
((
b
and
0
x7F
).
toLong
()
shl
size
*
7
)
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt
0 → 100644
View file @
932a3ef1
/*
* 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.utils.io
import
io.ktor.utils.io.ByteReadChannel
import
kotlinx.coroutines.ExperimentalCoroutinesApi
import
kotlinx.coroutines.flow.Flow
import
kotlinx.coroutines.flow.flow
import
kotlinx.coroutines.flow.flowOf
import
kotlinx.io.InputStream
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.Input
import
kotlinx.io.core.readAvailable
import
kotlinx.io.pool.useInstance
import
net.mamoe.mirai.utils.MiraiInternalAPI
/**
* 由 [chunkedFlow] 分割得到的区块
*/
class
ChunkedInput
(
/**
* 区块的数据.
* 由 [ByteArrayPool] 缓存并管理, 只可在 [Flow.collect] 中访问.
* 它的大小由 [ByteArrayPool.BUFFER_SIZE] 决定, 而有效(有数据)的大小由 [bufferSize] 决定.
*
* **注意**: 不要将他带出 [Flow.collect] 作用域, 否则将造成内存泄露
*/
val
buffer
:
ByteArray
,
internal
var
size
:
Int
)
{
/**
* [buffer] 的有效大小
*/
val
bufferSize
:
Int
get
()
=
size
}
/**
* 创建将 [ByteReadPacket] 以固定大小分割的 [Sequence].
*
* 对于一个 1000 长度的 [ByteReadPacket] 和参数 [sizePerPacket] = 300, 将会产生含四个元素的 [Sequence],
* 其长度分别为: 300, 300, 300, 100.
*
* 若 [ByteReadPacket.remaining] 小于 [sizePerPacket], 将会返回唯一元素 [this] 的 [Sequence]
*/
@UseExperimental
(
MiraiInternalAPI
::
class
)
fun
ByteReadPacket
.
chunkedFlow
(
sizePerPacket
:
Int
):
Flow
<
ChunkedInput
>
{
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
if
(
this
.
remaining
<=
sizePerPacket
.
toLong
())
{
ByteArrayPool
.
useInstance
{
buffer
->
return
flowOf
(
ChunkedInput
(
buffer
,
this
.
readAvailable
(
buffer
)))
}
}
return
flow
{
ByteArrayPool
.
useInstance
{
buffer
->
val
chunkedInput
=
ChunkedInput
(
buffer
,
0
)
do
{
chunkedInput
.
size
=
this
@
chunkedFlow
.
readAvailable
(
buffer
)
emit
(
chunkedInput
)
}
while
(
this
@
chunkedFlow
.
isNotEmpty
)
}
}
}
/**
* 创建将 [ByteReadChannel] 以固定大小分割的 [Sequence].
*
* 对于一个 1000 长度的 [ByteReadChannel] 和参数 [sizePerPacket] = 300, 将会产生含四个元素的 [Sequence],
* 其长度分别为: 300, 300, 300, 100.
*/
@UseExperimental
(
MiraiInternalAPI
::
class
)
fun
ByteReadChannel
.
chunkedFlow
(
sizePerPacket
:
Int
):
Flow
<
ChunkedInput
>
{
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
if
(
this
.
isClosedForRead
)
{
return
flowOf
()
}
return
flow
{
ByteArrayPool
.
useInstance
{
buffer
->
val
chunkedInput
=
ChunkedInput
(
buffer
,
0
)
do
{
chunkedInput
.
size
=
this
@
chunkedFlow
.
readAvailable
(
buffer
,
0
,
buffer
.
size
)
emit
(
chunkedInput
)
}
while
(!
this
@
chunkedFlow
.
isClosedForRead
)
}
}
}
/**
* 创建将 [Input] 以固定大小分割的 [Sequence].
*
* 对于一个 1000 长度的 [Input] 和参数 [sizePerPacket] = 300, 将会产生含四个元素的 [Sequence],
* 其长度分别为: 300, 300, 300, 100.
*/
@UseExperimental
(
MiraiInternalAPI
::
class
,
ExperimentalCoroutinesApi
::
class
)
internal
fun
Input
.
chunkedFlow
(
sizePerPacket
:
Int
):
Flow
<
ChunkedInput
>
{
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
if
(
this
.
endOfInput
)
{
return
flowOf
()
}
return
flow
{
ByteArrayPool
.
useInstance
{
buffer
->
val
chunkedInput
=
ChunkedInput
(
buffer
,
0
)
while
(!
this
@
chunkedFlow
.
endOfInput
)
{
chunkedInput
.
size
=
this
@
chunkedFlow
.
readAvailable
(
buffer
)
emit
(
chunkedInput
)
}
}
}
}
/**
* 创建将 [ByteReadPacket] 以固定大小分割的 [Sequence].
*
* 对于一个 1000 长度的 [ByteReadPacket] 和参数 [sizePerPacket] = 300, 将会产生含四个元素的 [Sequence],
* 其长度分别为: 300, 300, 300, 100.
*
* 若 [ByteReadPacket.remaining] 小于 [sizePerPacket], 将会返回唯一元素 [this] 的 [Sequence]
*/
@UseExperimental
(
MiraiInternalAPI
::
class
,
ExperimentalCoroutinesApi
::
class
)
internal
fun
InputStream
.
chunkedFlow
(
sizePerPacket
:
Int
):
Flow
<
ChunkedInput
>
{
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
return
flow
{
ByteArrayPool
.
useInstance
{
buffer
->
val
chunkedInput
=
ChunkedInput
(
buffer
,
0
)
while
(
this
@
chunkedFlow
.
available
()
!=
0
)
{
chunkedInput
.
size
=
this
@
chunkedFlow
.
read
(
buffer
)
emit
(
chunkedInput
)
}
}
}
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/input.kt
View file @
932a3ef1
...
...
@@ -20,26 +20,12 @@ import kotlinx.io.core.*
import
kotlinx.io.pool.useInstance
import
net.mamoe.mirai.utils.MiraiDebugAPI
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.cryptor.contentToString
import
kotlin.jvm.JvmMultifileClass
import
kotlin.jvm.JvmName
import
kotlin.jvm.JvmSynthetic
@Suppress
(
"NOTHING_TO_INLINE"
)
inline
fun
Input
.
discardExact
(
n
:
Short
)
=
this
.
discardExact
(
n
.
toInt
())
@Suppress
(
"NOTHING_TO_INLINE"
)
@JvmSynthetic
inline
fun
Input
.
discardExact
(
n
:
UShort
)
=
this
.
discardExact
(
n
.
toInt
())
@Suppress
(
"NOTHING_TO_INLINE"
)
@JvmSynthetic
inline
fun
Input
.
discardExact
(
n
:
UByte
)
=
this
.
discardExact
(
n
.
toInt
())
@Suppress
(
"NOTHING_TO_INLINE"
)
inline
fun
Input
.
discardExact
(
n
:
Byte
)
=
this
.
discardExact
(
n
.
toInt
())
fun
ByteReadPacket
.
transferTo
(
outputStream
:
OutputStream
)
{
@UseExperimental
(
MiraiInternalAPI
::
class
)
fun
ByteReadPacket
.
copyTo
(
outputStream
:
OutputStream
)
{
ByteArrayPool
.
useInstance
{
while
(
this
.
isNotEmpty
)
{
outputStream
.
write
(
it
,
0
,
this
.
readAvailable
(
it
))
...
...
@@ -56,21 +42,13 @@ inline fun <R> ByteReadPacket.useBytes(
block
(
it
,
n
)
}
@MiraiInternalAPI
inline
fun
ByteReadPacket
.
readPacketExact
(
n
:
Int
=
remaining
.
toInt
()
//not that safe but adequate
):
ByteReadPacket
=
this
.
readBytes
(
n
).
toReadPacket
()
inline
fun
Input
.
readUByteLVString
():
String
=
String
(
this
.
readUByteLVByteArray
())
inline
fun
Input
.
readUShortLVString
():
String
=
String
(
this
.
readUShortLVByteArray
())
inline
fun
Input
.
readUByteLVByteArray
():
ByteArray
=
this
.
readBytes
(
this
.
readUByte
().
toInt
())
inline
fun
Input
.
readUShortLVByteArray
():
ByteArray
=
this
.
readBytes
(
this
.
readUShort
().
toInt
())
private
inline
fun
<
R
>
inline
(
block
:
()
->
R
):
R
=
block
()
typealias
TlvMap
=
MutableMap
<
Int
,
ByteArray
>
inline
fun
TlvMap
.
getOrFail
(
tag
:
Int
):
ByteArray
{
...
...
@@ -81,12 +59,13 @@ inline fun TlvMap.getOrFail(tag: Int, lazyMessage: (tag: Int) -> String): ByteAr
return
this
[
tag
]
?:
error
(
lazyMessage
(
tag
))
}
@Suppress
(
"FunctionName"
)
@MiraiInternalAPI
inline
fun
Input
.
readTLVMap
(
tagSize
:
Int
=
2
,
suppressDuplication
:
Boolean
=
true
):
TlvMap
=
readTLVMap
(
true
,
tagSize
,
suppressDuplication
)
inline
fun
Input
.
_readTLVMap
(
tagSize
:
Int
=
2
,
suppressDuplication
:
Boolean
=
true
):
TlvMap
=
_
readTLVMap
(
true
,
tagSize
,
suppressDuplication
)
@MiraiDebugAPI
@Suppress
(
"DuplicatedCode"
)
fun
Input
.
readTLVMap
(
expectingEOF
:
Boolean
=
true
,
tagSize
:
Int
,
suppressDuplication
:
Boolean
=
true
):
TlvMap
{
@Suppress
(
"DuplicatedCode"
,
"FunctionName"
)
fun
Input
.
_
readTLVMap
(
expectingEOF
:
Boolean
=
true
,
tagSize
:
Int
,
suppressDuplication
:
Boolean
=
true
):
TlvMap
{
val
map
=
mutableMapOf
<
Int
,
ByteArray
>()
var
key
=
0
...
...
@@ -108,11 +87,14 @@ fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int, suppressDuplica
}.
toUByte
()
!=
UByte
.
MAX_VALUE
)
{
if
(
map
.
containsKey
(
key
))
{
@Suppress
(
"ControlFlowWithEmptyBody"
)
if
(!
suppressDuplication
)
{
DebugLogger
.
error
(
/*
@Suppress("DEPRECATION")
MiraiLogger.error(
@Suppress("IMPLICIT_CAST_TO_ANY")
"""
Error readTLVMap:
Error readTLVMap:
duplicated key ${when (tagSize) {
1 -> key.toByte()
2 -> key.toShort()
...
...
@@ -122,13 +104,13 @@ fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int, suppressDuplica
map=${map.contentToString()}
duplicating value=${this.readUShortLVByteArray().toUHexString()}
""".trimIndent()
)
)
*/
}
else
{
this
.
discardExact
(
this
.
readShort
().
toInt
()
and
0
xffff
)
}
}
else
{
try
{
map
[
key
]
=
this
.
read
UShortLVByteArray
(
)
map
[
key
]
=
this
.
read
Bytes
(
readUShort
().
toInt
()
)
}
catch
(
e
:
Exception
)
{
// BufferUnderflowException, java.io.EOFException
// if (expectingEOF) {
// return map
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/output.kt
View file @
932a3ef1
...
...
@@ -14,8 +14,9 @@
package
net.mamoe.mirai.utils.io
import
kotlinx.io.core.*
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.coerceAtMostOrFail
import
net.mamoe.mirai.utils.cryptor.
encryptBy
import
net.mamoe.mirai.utils.cryptor.
TEA
import
kotlin.jvm.JvmMultifileClass
import
kotlin.jvm.JvmName
...
...
@@ -67,5 +68,6 @@ fun BytePacketBuilder.writeHex(uHex: String) {
/**
* 会使用 [ByteArrayPool] 缓存
*/
@UseExperimental
(
MiraiInternalAPI
::
class
)
inline
fun
BytePacketBuilder
.
encryptAndWrite
(
key
:
ByteArray
,
encoder
:
BytePacketBuilder
.()
->
Unit
)
=
BytePacketBuilder
().
apply
(
encoder
).
build
().
encryptBy
(
key
)
{
decrypted
->
writeFully
(
decrypted
)
}
\ No newline at end of file
TEA
.
encrypt
(
BytePacketBuilder
().
apply
(
encoder
).
build
(),
key
)
{
decrypted
->
writeFully
(
decrypted
)
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt
View file @
932a3ef1
...
...
@@ -31,40 +31,12 @@ import javax.imageio.ImageIO
import
kotlin.coroutines.CoroutineContext
import
kotlin.coroutines.EmptyCoroutineContext
/**
* 平台默认的验证码识别器.
*
* 可被修改, 除覆盖配置外全局生效.
*/
actual
var
defaultLoginSolver
:
LoginSolver
=
DefaultLoginSolver
()
interface
LoginSolverInputReader
{
suspend
fun
read
(
question
:
String
):
String
?
suspend
operator
fun
invoke
(
question
:
String
):
String
?{
return
read
(
question
)
}
}
class
DefaultLoginSolverInputReader
:
LoginSolverInputReader
{
override
suspend
fun
read
(
question
:
String
):
String
?
{
return
readLine
()
}
}
class
DefaultLoginSolver
(
val
reader
:
LoginSolverInputReader
=
DefaultLoginSolverInputReader
()
,
val
overrideLogger
:
MiraiLogger
?
=
null
private
val
input
:
suspend
()
->
String
,
private
val
overrideLogger
:
MiraiLogger
?
=
null
)
:
LoginSolver
()
{
fun
getLogger
(
bot
:
Bot
):
MiraiLogger
{
if
(
overrideLogger
!=
null
){
return
overrideLogger
}
return
bot
.
logger
}
override
suspend
fun
onSolvePicCaptcha
(
bot
:
Bot
,
data
:
IoBuffer
):
String
?
=
loginSolverLock
.
withLock
{
val
logger
=
getLogger
(
bot
)
val
logger
=
overrideLogger
?:
bot
.
logger
val
tempFile
:
File
=
createTempFile
(
suffix
=
".png"
).
apply
{
deleteOnExit
()
}
withContext
(
Dispatchers
.
IO
)
{
tempFile
.
createNewFile
()
...
...
@@ -86,39 +58,38 @@ class DefaultLoginSolver(
}
}
logger
.
info
(
"请输入 4 位字母验证码. 若要更换验证码, 请直接回车"
)
return
reader
(
"请输入 4 位字母验证码. 若要更换验证码, 请直接回车"
)
!!
.
takeUnless
{
it
.
isEmpty
()
||
it
.
length
!=
4
}.
also
{
return
input
()
.
takeUnless
{
it
.
isEmpty
()
||
it
.
length
!=
4
}.
also
{
logger
.
info
(
"正在提交[$it]中..."
)
}
}
override
suspend
fun
onSolveSliderCaptcha
(
bot
:
Bot
,
url
:
String
):
String
?
=
loginSolverLock
.
withLock
{
val
logger
=
getLogger
(
bot
)
val
logger
=
overrideLogger
?:
bot
.
logger
logger
.
info
(
"需要滑动验证码"
)
logger
.
info
(
"请在任意浏览器中打开以下链接并完成验证码. "
)
logger
.
info
(
"完成后请输入任意字符 "
)
logger
.
info
(
url
)
return
reader
(
"完成后请输入任意字符"
).
also
{
return
input
(
).
also
{
logger
.
info
(
"正在提交中..."
)
}
}
override
suspend
fun
onSolveUnsafeDeviceLoginVerify
(
bot
:
Bot
,
url
:
String
):
String
?
=
loginSolverLock
.
withLock
{
val
logger
=
getLogger
(
bot
)
val
logger
=
overrideLogger
?:
bot
.
logger
logger
.
info
(
"需要进行账户安全认证"
)
logger
.
info
(
"该账户有[设备锁]/[不常用登录地点]/[不常用设备登录]的问题"
)
logger
.
info
(
"完成以下账号认证即可成功登录|理论本认证在mirai每个账户中最多出现1次"
)
logger
.
info
(
"请将该链接在QQ浏览器中打开并完成认证, 成功后输入任意字符"
)
logger
.
info
(
"这步操作将在后续的版本中优化"
)
logger
.
info
(
url
)
return
reader
(
"完成后请输入任意字符"
).
also
{
return
input
(
).
also
{
logger
.
info
(
"正在提交中..."
)
}
}
}
// Copied from Ktor CIO
p
ublic
fun
File
.
writeChannel
(
p
rivate
fun
File
.
writeChannel
(
coroutineContext
:
CoroutineContext
=
Dispatchers
.
IO
):
ByteWriteChannel
=
GlobalScope
.
reader
(
CoroutineName
(
"file-writer"
)
+
coroutineContext
,
autoFlush
=
true
)
{
@Suppress
(
"BlockingMethodInNonBlockingContext"
)
...
...
@@ -134,7 +105,7 @@ private val loginSolverLock = Mutex()
/**
* @author NaturalHG
*/
p
ublic
fun
BufferedImage
.
createCharImg
(
outputWidth
:
Int
=
100
,
ignoreRate
:
Double
=
0.95
):
String
{
p
rivate
fun
BufferedImage
.
createCharImg
(
outputWidth
:
Int
=
100
,
ignoreRate
:
Double
=
0.95
):
String
{
val
newHeight
=
(
this
.
height
*
(
outputWidth
.
toDouble
()
/
this
.
width
)).
toInt
()
val
tmp
=
this
.
getScaledInstance
(
outputWidth
,
newHeight
,
Image
.
SCALE_SMOOTH
)
val
image
=
BufferedImage
(
outputWidth
,
newHeight
,
BufferedImage
.
TYPE_INT_ARGB
)
...
...
@@ -229,7 +200,7 @@ actual open class BotConfiguration actual constructor() {
/**
* 验证码处理器
*/
actual
var
loginSolver
:
LoginSolver
=
defaultLoginSolver
actual
var
loginSolver
:
LoginSolver
=
LoginSolver
.
Default
actual
companion
object
{
/**
...
...
@@ -279,4 +250,18 @@ inline class FileBasedDeviceInfo @BotConfigurationDsl constructor(val filepath:
*/
@BotConfigurationDsl
companion
object
ByDeviceDotJson
}
/**
* 验证码, 设备锁解决器
*/
actual
abstract
class
LoginSolver
{
actual
abstract
suspend
fun
onSolvePicCaptcha
(
bot
:
Bot
,
data
:
IoBuffer
):
String
?
actual
abstract
suspend
fun
onSolveSliderCaptcha
(
bot
:
Bot
,
url
:
String
):
String
?
actual
abstract
suspend
fun
onSolveUnsafeDeviceLoginVerify
(
bot
:
Bot
,
url
:
String
):
String
?
actual
companion
object
{
actual
val
Default
:
LoginSolver
get
()
=
DefaultLoginSolver
(
input
=
{
withContext
(
Dispatchers
.
IO
)
{
readLine
()
}
?:
error
(
"No standard input"
)
})
}
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
View file @
932a3ef1
...
...
@@ -56,6 +56,7 @@ actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress
actual
val
Http
:
HttpClient
get
()
=
HttpClient
(
CIO
)
@UseExperimental
(
MiraiInternalAPI
::
class
)
actual
fun
ByteArray
.
unzip
(
offset
:
Int
,
length
:
Int
):
ByteArray
{
this
.
checkOffsetAndLength
(
offset
,
length
)
if
(
length
==
0
)
return
ByteArray
(
0
)
...
...
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
deleted
100644 → 0
View file @
1ff5df1d
/*
* 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.utils.cryptor
import
net.mamoe.mirai.utils.MiraiDebugAPI
import
java.lang.reflect.Field
import
kotlin.reflect.full.allSuperclasses
val
FIELD_TRY_SET_ACCESSIBLE
=
Field
::
class
.
java
.
declaredMethods
.
firstOrNull
{
it
.
name
==
"trySetAccessible"
}
@MiraiDebugAPI
actual
fun
Any
.
contentToStringReflectively
(
prefix
:
String
,
filter
:
((
name
:
String
,
value
:
Any
?)
->
Boolean
)?):
String
{
val
newPrefix
=
prefix
+
ProtoMap
.
indent
return
(
this
::
class
.
simpleName
?:
"<UnnamedClass>"
)
+
"#"
+
this
::
class
.
hashCode
()
+
" {\n"
+
this
.
allFieldsFromSuperClassesMatching
{
it
.
name
.
startsWith
(
"net.mamoe.mirai"
)
}
.
distinctBy
{
it
.
name
}
.
filterNot
{
it
.
name
.
contains
(
"$"
)
||
it
.
name
==
"Companion"
||
it
.
isSynthetic
||
it
.
name
==
"serialVersionUID"
}
.
filterNot
{
it
.
isEnumConstant
}
.
map
{
FIELD_TRY_SET_ACCESSIBLE
?.
invoke
(
it
,
true
)
?:
kotlin
.
run
{
it
.
isAccessible
=
true
}
val
value
=
it
.
get
(
this
)
if
(
filter
!=
null
)
{
kotlin
.
runCatching
{
if
(!
filter
(
it
.
name
,
value
))
return
@
map
it
.
name
to
FIELD_TRY_SET_ACCESSIBLE
}
}
it
.
name
to
value
}
.
filterNot
{
it
.
second
===
FIELD_TRY_SET_ACCESSIBLE
}
.
joinToStringPrefixed
(
prefix
=
newPrefix
)
{
(
name
,
value
)
->
"$name="
+
kotlin
.
runCatching
{
if
(
value
==
this
)
"<this>"
else
value
.
contentToString
(
newPrefix
)
}.
getOrElse
{
"<!>"
}
}
+
"\n$prefix}"
}
internal
fun
Any
.
allFieldsFromSuperClassesMatching
(
classFilter
:
(
Class
<
out
Any
>)
->
Boolean
):
Sequence
<
Field
>
{
return
(
this
::
class
.
java
.
takeIf
(
classFilter
)
?.
declaredFields
?.
asSequence
()
?:
sequenceOf
<
Field
>())
+
this
::
class
.
allSuperclasses
.
asSequence
()
.
map
{
it
.
java
}
.
filter
(
classFilter
)
.
flatMap
{
it
.
declaredFields
.
asSequence
()
}
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocketJvm.kt
View file @
932a3ef1
...
...
@@ -21,8 +21,6 @@ import java.nio.channels.ReadableByteChannel
import
java.nio.channels.WritableByteChannel
actual
typealias
ClosedChannelException
=
java
.
nio
.
channels
.
ClosedChannelException
/**
* 多平台适配的 DatagramChannel.
*/
...
...
mirai-core/src/jvmTest/kotlin/mirai/test/testCaptchaPacket/TestCaptchaPacket.kt
View file @
932a3ef1
...
...
@@ -9,16 +9,20 @@
package
mirai.test.testCaptchaPacket
import
net.mamoe.mirai.utils.cryptor.decryptBy
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.cryptor.TEA.decrypt
import
net.mamoe.mirai.utils.io.hexToBytes
import
net.mamoe.mirai.utils.io.toUHexString
@MiraiInternalAPI
fun
main
()
{
val
key
=
"65 F7 F3 14 E3 94 10 1F DD 95 84 A3 F5 9F AD 94"
.
hexToBytes
()
val
data
=
"8D 4F 6A 70 F8 4A DE 43 AF 75 D1 3F 3A 3F F2 E0 A8 16 1A 46 13 CD B0 51 45 00 29 52 57 75 6D 4A 4C D9 B7 98 8C B0 96 EC 57 4E 67 FB 8D C5 F1 BF 72 38 40 42 19 54 C2 28 F4 72 C8 AE 24 EB 66 B5 D0 45 0B 72 44 81 E2 F6 2B EE C3 85 93 BA CB B7 72 F4 1A 30 F9 5B 3D B0 79 3E F4 0B F2 1A A7 49 60 3B 37 02 60 0C 5D D5 76 76 47 4F B5 B3 F5 CA 58 6C FC D2 41 3E 24 D1 FB 0A 18 53 D8 E5 A5 85 A8 BC 51 54 3B 66 5B 21 C6 7B AF C9 62 F0 AA 9C CF 2E 84 0F CC 15 5B 35 93 49 5C E4 28 49 A7 8A D3 30 A9 6E 36 4E 7A 49 28 69 4D C3 25 39 6E 45 6E 40 F2 86 1E F4 4F 00 A6 9D E6 9B 84 19 69 C1 31 6A 17 BA F0 0D 8A 22 09 86 24 92 F7 22 C3 47 7F F2 BF 94 8A 8A B5 29"
.
hexToBytes
()
.
decryptBy
(
key
)
decrypt
(
"8D 4F 6A 70 F8 4A DE 43 AF 75 D1 3F 3A 3F F2 E0 A8 16 1A 46 13 CD B0 51 45 00 29 52 57 75 6D 4A 4C D9 B7 98 8C B0 96 EC 57 4E 67 FB 8D C5 F1 BF 72 38 40 42 19 54 C2 28 F4 72 C8 AE 24 EB 66 B5 D0 45 0B 72 44 81 E2 F6 2B EE C3 85 93 BA CB B7 72 F4 1A 30 F9 5B 3D B0 79 3E F4 0B F2 1A A7 49 60 3B 37 02 60 0C 5D D5 76 76 47 4F B5 B3 F5 CA 58 6C FC D2 41 3E 24 D1 FB 0A 18 53 D8 E5 A5 85 A8 BC 51 54 3B 66 5B 21 C6 7B AF C9 62 F0 AA 9C CF 2E 84 0F CC 15 5B 35 93 49 5C E4 28 49 A7 8A D3 30 A9 6E 36 4E 7A 49 28 69 4D C3 25 39 6E 45 6E 40 F2 86 1E F4 4F 00 A6 9D E6 9B 84 19 69 C1 31 6A 17 BA F0 0D 8A 22 09 86 24 92 F7 22 C3 47 7F F2 BF 94 8A 8A B5 29"
.
hexToBytes
(),
key
)
println
(
data
.
toUHexString
())
//00 02 00 00 08 04 01 E0 00 00 04 56 00 00 00 01 00 00 15 E3 01 00 38 58 CE A0 12 81 31 5C 5E 36 23 5B E4 0E 05 A6 47 BF 7C 1A 7A 35 37 59 90 17 50 66 0C 07 03 77 E4 48 DB 28 0A CF C3 A9 B7 C0 95 D3 9D 00 AA A5 EB FB D6 85 8D 10 61 5A D0 01 03 00 19 02 CA 53 7E F0 7B 32 82 EC 9F DE CF 51 8B A4 93 26 76 EC 42 1C 02 00 74 58 14 00 05 00 00 00 00 00 04 6C 73 64 61 00 40 CE 99 84 E8 F1 59 31 B0 3F 6C 4D 44 09 E4 82 77 96 67 03 A7 3A EA 8F 36 B9 20 79 7E C9 0F 75 3C 2A C3 E1 E5 C6 00 B3 5E 91 5B 47 63 EF AF 30 C0 48 2F 58 23 96 CF 65 2F 4C 75 95 A6 CA 5A 2C 5C 00 10 E1 50 C9 F4 F6 F4 2F D1 7F E9 8C AB B6 1C 38 7B
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment