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
74fff88a
Commit
74fff88a
authored
Sep 07, 2019
by
Him188moe
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Captcha finished
parent
36f1980a
Changes
26
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
264 additions
and
205 deletions
+264
-205
document/protocol/log.txgt
document/protocol/log.txgt
+0
-72
mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java
mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java
+0
-1
mirai-core/src/main/java/net/mamoe/mirai/Robot.java
mirai-core/src/main/java/net/mamoe/mirai/Robot.java
+9
-0
mirai-core/src/main/java/net/mamoe/mirai/event/events/network/BeforePacketSendEvent.java
...moe/mirai/event/events/network/BeforePacketSendEvent.java
+4
-3
mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ClientPacketEvent.java
...t/mamoe/mirai/event/events/network/ClientPacketEvent.java
+3
-2
mirai-core/src/main/java/net/mamoe/mirai/event/events/network/PacketEvent.java
...ava/net/mamoe/mirai/event/events/network/PacketEvent.java
+5
-3
mirai-core/src/main/java/net/mamoe/mirai/event/events/network/PacketSentEvent.java
...net/mamoe/mirai/event/events/network/PacketSentEvent.java
+3
-2
mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ServerPacketEvent.java
...t/mamoe/mirai/event/events/network/ServerPacketEvent.java
+3
-2
mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ServerPacketReceivedEvent.java
...mirai/event/events/network/ServerPacketReceivedEvent.java
+3
-2
mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt
mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt
+2
-2
mirai-core/src/main/java/net/mamoe/mirai/network/RobotNetworkHandler.kt
.../main/java/net/mamoe/mirai/network/RobotNetworkHandler.kt
+61
-45
mirai-core/src/main/java/net/mamoe/mirai/network/packet/ClientPacket.kt
.../main/java/net/mamoe/mirai/network/packet/ClientPacket.kt
+12
-9
mirai-core/src/main/java/net/mamoe/mirai/network/packet/GradeInfo.kt
...src/main/java/net/mamoe/mirai/network/packet/GradeInfo.kt
+1
-1
mirai-core/src/main/java/net/mamoe/mirai/network/packet/SKey.kt
...core/src/main/java/net/mamoe/mirai/network/packet/SKey.kt
+1
-1
mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt
...c/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt
+2
-2
mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt
.../main/java/net/mamoe/mirai/network/packet/ServerPacket.kt
+37
-10
mirai-core/src/main/java/net/mamoe/mirai/network/packet/Session.kt
...e/src/main/java/net/mamoe/mirai/network/packet/Session.kt
+1
-1
mirai-core/src/main/java/net/mamoe/mirai/network/packet/Touch.kt
...ore/src/main/java/net/mamoe/mirai/network/packet/Touch.kt
+3
-3
mirai-core/src/main/java/net/mamoe/mirai/network/packet/VerificationCode.kt
...n/java/net/mamoe/mirai/network/packet/VerificationCode.kt
+78
-33
mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLogin.kt
.../java/net/mamoe/mirai/network/packet/login/ClientLogin.kt
+4
-2
mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponsePasswordVerifiedPacket.kt
...packet/login/ServerLoginResponsePasswordVerifiedPacket.kt
+1
-1
mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseResendPacket.kt
...i/network/packet/login/ServerLoginResponseResendPacket.kt
+1
-1
mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseVerificationCodeInitPacket.kt
...et/login/ServerLoginResponseVerificationCodeInitPacket.kt
+1
-1
mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiLogger.kt
...i-core/src/main/java/net/mamoe/mirai/utils/MiraiLogger.kt
+26
-0
mirai-core/src/main/java/net/mamoe/mirai/utils/Utils.kt
mirai-core/src/main/java/net/mamoe/mirai/utils/Utils.kt
+0
-3
mirai-core/src/test/java/HexComparator.java
mirai-core/src/test/java/HexComparator.java
+3
-3
No files found.
document/protocol/log.txgt
deleted
100644 → 0
View file @
36f1980a
This diff is collapsed.
Click to expand it.
mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java
View file @
74fff88a
...
@@ -150,7 +150,6 @@ public class MiraiServer {
...
@@ -150,7 +150,6 @@ public class MiraiServer {
MiraiConfigSection
<
Object
>
section
=
new
MiraiConfigSection
<>();
MiraiConfigSection
<
Object
>
section
=
new
MiraiConfigSection
<>();
System
.
out
.
println
(
"/"
);
Scanner
scanner
=
new
Scanner
(
System
.
in
);
Scanner
scanner
=
new
Scanner
(
System
.
in
);
getLogger
().
info
(
"Input a "
+
LoggerTextFormat
.
RED
+
" QQ number "
+
LoggerTextFormat
.
GREEN
+
"for default robotNetworkHandler"
);
getLogger
().
info
(
"Input a "
+
LoggerTextFormat
.
RED
+
" QQ number "
+
LoggerTextFormat
.
GREEN
+
"for default robotNetworkHandler"
);
getLogger
().
info
(
"输入用于默认机器人的QQ号"
);
getLogger
().
info
(
"输入用于默认机器人的QQ号"
);
...
...
mirai-core/src/main/java/net/mamoe/mirai/Robot.java
View file @
74fff88a
...
@@ -11,6 +11,7 @@ import org.jetbrains.annotations.NotNull;
...
@@ -11,6 +11,7 @@ import org.jetbrains.annotations.NotNull;
import
java.io.Closeable
;
import
java.io.Closeable
;
import
java.util.*
;
import
java.util.*
;
import
java.util.concurrent.atomic.AtomicInteger
;
/**
/**
* Mirai 的机器人. 一个机器人实例登录一个 QQ 账号.
* Mirai 的机器人. 一个机器人实例登录一个 QQ 账号.
...
@@ -34,12 +35,20 @@ import java.util.*;
...
@@ -34,12 +35,20 @@ import java.util.*;
public
final
class
Robot
implements
Closeable
{
public
final
class
Robot
implements
Closeable
{
public
static
final
List
<
Robot
>
instances
=
Collections
.
synchronizedList
(
new
LinkedList
<>());
public
static
final
List
<
Robot
>
instances
=
Collections
.
synchronizedList
(
new
LinkedList
<>());
public
final
int
id
=
_id
.
getAndAdd
(
1
);
private
static
final
AtomicInteger
_id
=
new
AtomicInteger
(
0
);
public
final
RobotAccount
account
;
public
final
RobotAccount
account
;
public
final
ContactSystem
contacts
=
new
ContactSystem
();
public
final
ContactSystem
contacts
=
new
ContactSystem
();
public
final
RobotNetworkHandler
network
;
public
final
RobotNetworkHandler
network
;
@Override
public
String
toString
()
{
return
String
.
format
(
"Robot{id=%d,qq=%d}"
,
id
,
this
.
account
.
qqNumber
);
}
/**
/**
* Robot 联系人管理.
* Robot 联系人管理.
*
*
...
...
mirai-core/src/main/java/net/mamoe/mirai/event/events/network/BeforePacketSendEvent.java
View file @
74fff88a
package
net.mamoe.mirai.event.events.network
;
package
net.mamoe.mirai.event.events.network
;
import
net.mamoe.mirai.Robot
;
import
net.mamoe.mirai.event.Cancellable
;
import
net.mamoe.mirai.event.Cancellable
;
import
net.mamoe.mirai.network.packet.ClientPacket
;
import
net.mamoe.mirai.network.packet.ClientPacket
;
import
org.jetbrains.annotations.NotNull
;
import
org.jetbrains.annotations.NotNull
;
/**
/**
* Packet 已经
{@link ClientPacket#encode()}
, 即将被发送
* Packet 已经
encoded
, 即将被发送
*
*
* @author Him188moe
* @author Him188moe
*/
*/
public
final
class
BeforePacketSendEvent
extends
ClientPacketEvent
implements
Cancellable
{
public
final
class
BeforePacketSendEvent
extends
ClientPacketEvent
implements
Cancellable
{
public
BeforePacketSendEvent
(
@NotNull
ClientPacket
packet
)
{
public
BeforePacketSendEvent
(
@NotNull
Robot
robot
,
@NotNull
ClientPacket
packet
)
{
super
(
packet
);
super
(
robot
,
packet
);
}
}
}
}
mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ClientPacketEvent.java
View file @
74fff88a
package
net.mamoe.mirai.event.events.network
;
package
net.mamoe.mirai.event.events.network
;
import
net.mamoe.mirai.Robot
;
import
net.mamoe.mirai.network.packet.ClientPacket
;
import
net.mamoe.mirai.network.packet.ClientPacket
;
import
org.jetbrains.annotations.NotNull
;
import
org.jetbrains.annotations.NotNull
;
...
@@ -7,8 +8,8 @@ import org.jetbrains.annotations.NotNull;
...
@@ -7,8 +8,8 @@ import org.jetbrains.annotations.NotNull;
* @author Him188moe
* @author Him188moe
*/
*/
public
abstract
class
ClientPacketEvent
extends
PacketEvent
{
public
abstract
class
ClientPacketEvent
extends
PacketEvent
{
public
ClientPacketEvent
(
@NotNull
ClientPacket
packet
)
{
public
ClientPacketEvent
(
@NotNull
Robot
robot
,
@NotNull
ClientPacket
packet
)
{
super
(
packet
);
super
(
robot
,
packet
);
}
}
@Override
@Override
...
...
mirai-core/src/main/java/net/mamoe/mirai/event/events/network/PacketEvent.java
View file @
74fff88a
package
net.mamoe.mirai.event.events.network
;
package
net.mamoe.mirai.event.events.network
;
import
net.mamoe.mirai.event.MiraiEvent
;
import
net.mamoe.mirai.Robot
;
import
net.mamoe.mirai.event.events.robot.RobotEvent
;
import
net.mamoe.mirai.network.packet.Packet
;
import
net.mamoe.mirai.network.packet.Packet
;
import
org.jetbrains.annotations.NotNull
;
import
org.jetbrains.annotations.NotNull
;
...
@@ -9,10 +10,11 @@ import java.util.Objects;
...
@@ -9,10 +10,11 @@ import java.util.Objects;
/**
/**
* @author Him188moe
* @author Him188moe
*/
*/
public
abstract
class
PacketEvent
extends
Mirai
Event
{
public
abstract
class
PacketEvent
extends
Robot
Event
{
private
final
Packet
packet
;
private
final
Packet
packet
;
public
PacketEvent
(
@NotNull
Packet
packet
)
{
public
PacketEvent
(
@NotNull
Robot
robot
,
@NotNull
Packet
packet
)
{
super
(
robot
);
this
.
packet
=
Objects
.
requireNonNull
(
packet
);
this
.
packet
=
Objects
.
requireNonNull
(
packet
);
}
}
...
...
mirai-core/src/main/java/net/mamoe/mirai/event/events/network/PacketSentEvent.java
View file @
74fff88a
package
net.mamoe.mirai.event.events.network
;
package
net.mamoe.mirai.event.events.network
;
import
net.mamoe.mirai.Robot
;
import
net.mamoe.mirai.network.packet.ClientPacket
;
import
net.mamoe.mirai.network.packet.ClientPacket
;
import
org.jetbrains.annotations.NotNull
;
import
org.jetbrains.annotations.NotNull
;
...
@@ -9,7 +10,7 @@ import org.jetbrains.annotations.NotNull;
...
@@ -9,7 +10,7 @@ import org.jetbrains.annotations.NotNull;
* @author Him188moe
* @author Him188moe
*/
*/
public
final
class
PacketSentEvent
extends
ClientPacketEvent
{
public
final
class
PacketSentEvent
extends
ClientPacketEvent
{
public
PacketSentEvent
(
@NotNull
ClientPacket
packet
)
{
public
PacketSentEvent
(
@NotNull
Robot
robot
,
@NotNull
ClientPacket
packet
)
{
super
(
packet
);
super
(
robot
,
packet
);
}
}
}
}
mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ServerPacketEvent.java
View file @
74fff88a
package
net.mamoe.mirai.event.events.network
;
package
net.mamoe.mirai.event.events.network
;
import
net.mamoe.mirai.Robot
;
import
net.mamoe.mirai.network.packet.ServerPacket
;
import
net.mamoe.mirai.network.packet.ServerPacket
;
/**
/**
* @author Him188moe
* @author Him188moe
*/
*/
public
abstract
class
ServerPacketEvent
extends
PacketEvent
{
public
abstract
class
ServerPacketEvent
extends
PacketEvent
{
public
ServerPacketEvent
(
ServerPacket
packet
)
{
public
ServerPacketEvent
(
Robot
robot
,
ServerPacket
packet
)
{
super
(
packet
);
super
(
robot
,
packet
);
}
}
@Override
@Override
...
...
mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ServerPacketReceivedEvent.java
View file @
74fff88a
package
net.mamoe.mirai.event.events.network
;
package
net.mamoe.mirai.event.events.network
;
import
net.mamoe.mirai.Robot
;
import
net.mamoe.mirai.event.Cancellable
;
import
net.mamoe.mirai.event.Cancellable
;
import
net.mamoe.mirai.network.packet.ServerPacket
;
import
net.mamoe.mirai.network.packet.ServerPacket
;
import
net.mamoe.mirai.network.packet.ServerVerificationCodePacket
;
import
net.mamoe.mirai.network.packet.ServerVerificationCodePacket
;
...
@@ -11,7 +12,7 @@ import net.mamoe.mirai.network.packet.ServerVerificationCodePacket;
...
@@ -11,7 +12,7 @@ import net.mamoe.mirai.network.packet.ServerVerificationCodePacket;
* @author Him188moe
* @author Him188moe
*/
*/
public
final
class
ServerPacketReceivedEvent
extends
ServerPacketEvent
implements
Cancellable
{
public
final
class
ServerPacketReceivedEvent
extends
ServerPacketEvent
implements
Cancellable
{
public
ServerPacketReceivedEvent
(
ServerPacket
packet
)
{
public
ServerPacketReceivedEvent
(
Robot
robot
,
ServerPacket
packet
)
{
super
(
packet
);
super
(
robot
,
packet
);
}
}
}
}
mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt
View file @
74fff88a
...
@@ -10,10 +10,10 @@ import java.util.stream.Collectors
...
@@ -10,10 +10,10 @@ import java.util.stream.Collectors
object
Protocol
{
object
Protocol
{
val
SERVER_IP
:
List
<
String
>
=
object
:
ArrayList
<
String
>()
{
val
SERVER_IP
:
List
<
String
>
=
object
:
ArrayList
<
String
>()
{
init
{
init
{
add
(
"183.60.56.29"
)
//
add("183.60.56.29")
arrayOf
(
arrayOf
(
"sz3.tencent.com"
,
//
"sz3.tencent.com",
"sz4.tencent.com"
,
"sz4.tencent.com"
,
"sz5.tencent.com"
,
"sz5.tencent.com"
,
"sz6.tencent.com"
,
"sz6.tencent.com"
,
...
...
mirai-core/src/main/java/net/mamoe/mirai/network/RobotNetworkHandler.kt
View file @
74fff88a
This diff is collapsed.
Click to expand it.
mirai-core/src/main/java/net/mamoe/mirai/network/packet/ClientPacket.kt
View file @
74fff88a
...
@@ -20,7 +20,7 @@ abstract class ClientPacket : ByteArrayDataOutputStream(), Packet {
...
@@ -20,7 +20,7 @@ abstract class ClientPacket : ByteArrayDataOutputStream(), Packet {
init
{
init
{
val
annotation
=
this
.
javaClass
.
getAnnotation
(
PacketId
::
class
.
java
)
val
annotation
=
this
.
javaClass
.
getAnnotation
(
PacketId
::
class
.
java
)
idHex
=
annotation
.
value
idHex
=
annotation
.
value
.
trim
()
try
{
try
{
this
.
writeHex
(
Protocol
.
head
)
this
.
writeHex
(
Protocol
.
head
)
...
@@ -60,10 +60,19 @@ abstract class ClientPacket : ByteArrayDataOutputStream(), Packet {
...
@@ -60,10 +60,19 @@ abstract class ClientPacket : ByteArrayDataOutputStream(), Packet {
return
toByteArray
()
return
toByteArray
()
}
}
open
fun
getFixedId
():
String
=
when
(
this
.
idHex
.
length
)
{
0
->
"__ __ __ __"
2
->
this
.
idHex
+
" __ __"
5
->
this
.
idHex
+
" __"
else
->
this
.
idHex
}
override
fun
toString
():
String
{
override
fun
toString
():
String
{
return
this
.
javaClass
.
simpleName
+
this
.
getAllDeclaredFields
().
joinToString
(
", "
,
"{"
,
"}"
)
{
return
this
.
javaClass
.
simpleName
+
"(${this.getFixedId()})"
+
this
.
getAllDeclaredFields
().
joinToString
(
", "
,
"{"
,
"}"
)
{
it
.
trySetAccessible
();
it
.
name
+
"="
+
it
.
get
(
this
).
let
{
value
->
it
.
trySetAccessible
();
it
.
name
+
"="
+
it
.
get
(
this
).
let
{
value
->
when
(
value
)
{
when
(
value
)
{
null
->
null
is
ByteArray
->
value
.
toUHexString
()
is
ByteArray
->
value
.
toUHexString
()
is
UByteArray
->
value
.
toUHexString
()
is
UByteArray
->
value
.
toUHexString
()
else
->
value
.
toString
()
else
->
value
.
toString
()
...
@@ -144,12 +153,6 @@ fun DataOutputStream.writeTLV0006(qq: Long, password: String, loginTime: Int, lo
...
@@ -144,12 +153,6 @@ fun DataOutputStream.writeTLV0006(qq: Long, password: String, loginTime: Int, lo
}
}
}
}
/*
@ExperimentalUnsignedTypes
fun main() {
println(lazyEncode { it.writeTLV0006(1994701021, "D1 A5 C8 BB E1 Q3 CC DD", 131513, "123.123.123.123", "AA BB CC DD EE FF AA BB CC".hexToBytes()) }.toUByteArray().toUHexString())
}*/
@ExperimentalUnsignedTypes
@ExperimentalUnsignedTypes
@TestedSuccessfully
@TestedSuccessfully
fun
DataOutputStream
.
writeCRC32
()
=
writeCRC32
(
getRandomByteArray
(
16
))
fun
DataOutputStream
.
writeCRC32
()
=
writeCRC32
(
getRandomByteArray
(
16
))
...
@@ -209,7 +212,7 @@ fun Int.toLByteArray(): ByteArray = byteArrayOf(
...
@@ -209,7 +212,7 @@ fun Int.toLByteArray(): ByteArray = byteArrayOf(
)
)
@ExperimentalUnsignedTypes
@ExperimentalUnsignedTypes
fun
Int
.
toHexString
(
separator
:
String
=
" "
):
String
=
this
.
toByteArray
().
toUByteArray
().
toUHexString
(
separator
)
fun
Int
.
to
U
HexString
(
separator
:
String
=
" "
):
String
=
this
.
toByteArray
().
toUByteArray
().
toUHexString
(
separator
)
internal
fun
md5
(
str
:
String
):
ByteArray
=
MessageDigest
.
getInstance
(
"MD5"
).
digest
(
str
.
toByteArray
())
internal
fun
md5
(
str
:
String
):
ByteArray
=
MessageDigest
.
getInstance
(
"MD5"
).
digest
(
str
.
toByteArray
())
...
...
mirai-core/src/main/java/net/mamoe/mirai/network/packet/GradeInfo.kt
View file @
74fff88a
...
@@ -42,7 +42,7 @@ class ServerAccountInfoResponsePacket(input: DataInputStream) : ServerPacket(inp
...
@@ -42,7 +42,7 @@ class ServerAccountInfoResponsePacket(input: DataInputStream) : ServerPacket(inp
fun
decrypt
(
sessionKey
:
ByteArray
):
ServerAccountInfoResponsePacket
{
fun
decrypt
(
sessionKey
:
ByteArray
):
ServerAccountInfoResponsePacket
{
this
.
input
goto
14
this
.
input
goto
14
val
data
=
this
.
input
.
readAllBytes
().
let
{
it
.
copyOfRange
(
0
,
it
.
size
-
1
)
}
val
data
=
this
.
input
.
readAllBytes
().
let
{
it
.
copyOfRange
(
0
,
it
.
size
-
1
)
}
return
ServerAccountInfoResponsePacket
(
TEA
.
decrypt
(
data
,
sessionKey
).
dataInputStream
())
;
return
ServerAccountInfoResponsePacket
(
TEA
.
decrypt
(
data
,
sessionKey
).
dataInputStream
())
.
setId
(
this
.
idHex
)
}
}
}
}
}
}
\ No newline at end of file
mirai-core/src/main/java/net/mamoe/mirai/network/packet/SKey.kt
View file @
74fff88a
...
@@ -58,7 +58,7 @@ class ServerSKeyResponsePacket(input: DataInputStream) : ServerPacket(input) {
...
@@ -58,7 +58,7 @@ class ServerSKeyResponsePacket(input: DataInputStream) : ServerPacket(input) {
fun
decrypt
(
sessionKey
:
ByteArray
):
ServerSKeyResponsePacket
{
fun
decrypt
(
sessionKey
:
ByteArray
):
ServerSKeyResponsePacket
{
this
.
input
goto
14
this
.
input
goto
14
val
data
=
this
.
input
.
readAllBytes
().
let
{
it
.
copyOfRange
(
0
,
it
.
size
-
1
)
}
val
data
=
this
.
input
.
readAllBytes
().
let
{
it
.
copyOfRange
(
0
,
it
.
size
-
1
)
}
return
ServerSKeyResponsePacket
(
TEA
.
decrypt
(
data
,
sessionKey
).
dataInputStream
())
;
return
ServerSKeyResponsePacket
(
TEA
.
decrypt
(
data
,
sessionKey
).
dataInputStream
())
.
setId
(
this
.
idHex
)
}
}
}
}
}
}
\ No newline at end of file
mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt
View file @
74fff88a
...
@@ -39,12 +39,12 @@ open class ServerEventPacket(input: DataInputStream, val packetId: ByteArray, va
...
@@ -39,12 +39,12 @@ open class ServerEventPacket(input: DataInputStream, val packetId: ByteArray, va
//"02 10", "00 12" -> ServerUnknownEventPacket(this.input, packetId, eventIdentity)
//"02 10", "00 12" -> ServerUnknownEventPacket(this.input, packetId, eventIdentity)
else
->
UnknownServerEventPacket
(
this
.
input
,
packetId
,
eventIdentity
)
else
->
UnknownServerEventPacket
(
this
.
input
,
packetId
,
eventIdentity
)
}
}
.
setId
(
this
.
idHex
)
}
}
@PacketId
(
"00 17"
)
@PacketId
(
"00 17"
)
class
Encrypted
(
input
:
DataInputStream
,
private
val
packetId
:
ByteArray
)
:
ServerPacket
(
input
)
{
class
Encrypted
(
input
:
DataInputStream
,
private
val
packetId
:
ByteArray
)
:
ServerPacket
(
input
)
{
fun
decrypt
(
sessionKey
:
ByteArray
):
Raw
=
Raw
(
decryptBy
(
sessionKey
),
packetId
)
fun
decrypt
(
sessionKey
:
ByteArray
):
Raw
=
Raw
(
decryptBy
(
sessionKey
),
packetId
)
.
setId
(
this
.
idHex
)
}
}
}
}
}
}
...
...
mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt
View file @
74fff88a
package
net.mamoe.mirai.network.packet
package
net.mamoe.mirai.network.packet
import
lombok.Getter
import
net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
import
net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
import
net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket
import
net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket
import
net.mamoe.mirai.network.packet.login.*
import
net.mamoe.mirai.network.packet.login.*
...
@@ -10,6 +11,24 @@ import java.io.DataInputStream
...
@@ -10,6 +11,24 @@ import java.io.DataInputStream
* @author Him188moe
* @author Him188moe
*/
*/
abstract
class
ServerPacket
(
val
input
:
DataInputStream
)
:
Packet
{
abstract
class
ServerPacket
(
val
input
:
DataInputStream
)
:
Packet
{
@Getter
var
idHex
:
String
var
encoded
:
Boolean
=
false
init
{
idHex
=
try
{
val
annotation
=
this
.
javaClass
.
getAnnotation
(
PacketId
::
class
.
java
)
annotation
.
value
.
trim
()
}
catch
(
e
:
NullPointerException
)
{
""
}
}
fun
<
P
:
ServerPacket
>
P
.
setId
(
idHex
:
String
):
P
{
this
.
idHex
=
idHex
return
this
}
open
fun
decode
()
{
open
fun
decode
()
{
...
@@ -19,14 +38,12 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
...
@@ -19,14 +38,12 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
@ExperimentalUnsignedTypes
@ExperimentalUnsignedTypes
fun
ofByteArray
(
bytes
:
ByteArray
):
ServerPacket
{
fun
ofByteArray
(
bytes
:
ByteArray
):
ServerPacket
{
//println("Raw received: ${bytes.toUByteArray().toUHexString()}")
val
stream
=
bytes
.
dataInputStream
()
val
stream
=
bytes
.
dataInputStream
()
stream
.
skip
(
3
)
stream
.
skip
(
3
)
val
idHex
=
stream
.
readInt
().
toUHexString
(
" "
)
return
when
(
val
idHex
=
stream
.
readInt
().
toHexString
(
" "
)
)
{
return
when
(
idHex
)
{
"08 25 31 01"
->
ServerTouchResponsePacket
.
Encrypted
(
ServerTouchResponsePacket
.
Type
.
TYPE_08_25_31_01
,
stream
)
"08 25 31 01"
->
ServerTouchResponsePacket
.
Encrypted
(
ServerTouchResponsePacket
.
Type
.
TYPE_08_25_31_01
,
stream
)
"08 25 31 02"
->
ServerTouchResponsePacket
.
Encrypted
(
ServerTouchResponsePacket
.
Type
.
TYPE_08_25_31_02
,
stream
)
"08 25 31 02"
->
ServerTouchResponsePacket
.
Encrypted
(
ServerTouchResponsePacket
.
Type
.
TYPE_08_25_31_02
,
stream
)
...
@@ -37,12 +54,12 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
...
@@ -37,12 +54,12 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
else
->
{
else
->
{
MiraiLogger
debug
(
"ServerLoginResponseResendPacketEncrypted: flag=$idHex"
);
ServerLoginResponseResendPacket
.
Flag
.
OTHER
MiraiLogger
debug
(
"ServerLoginResponseResendPacketEncrypted: flag=$idHex"
);
ServerLoginResponseResendPacket
.
Flag
.
OTHER
}
}
})
})
.
apply
{
this
.
idHex
=
idHex
}
871
->
return
ServerLoginResponseVerificationCodeInitPacket
.
Encrypted
(
stream
)
871
->
return
ServerLoginResponseVerificationCodeInitPacket
.
Encrypted
(
stream
)
.
apply
{
this
.
idHex
=
idHex
}
}
}
if
(
bytes
.
size
>
700
)
{
if
(
bytes
.
size
>
700
)
{
return
ServerLoginResponseSuccessPacket
.
Encrypted
(
stream
)
return
ServerLoginResponseSuccessPacket
.
Encrypted
(
stream
)
.
apply
{
this
.
idHex
=
idHex
}
}
}
return
ServerLoginResponseFailedPacket
(
when
(
bytes
.
size
)
{
return
ServerLoginResponseFailedPacket
(
when
(
bytes
.
size
)
{
...
@@ -60,7 +77,7 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
...
@@ -60,7 +77,7 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
351 -> throw IllegalArgumentException(bytes.size.toString() + " (Illegal package data or Unknown error)")//包数据有误
351 -> throw IllegalArgumentException(bytes.size.toString() + " (Illegal package data or Unknown error)")//包数据有误
else -> throw IllegalArgumentException(bytes.size.toString())*/
else -> throw IllegalArgumentException(bytes.size.toString())*/
},
stream
)
},
stream
)
.
apply
{
this
.
idHex
=
idHex
}
}
}
"08 28 04 34"
->
ServerSessionKeyResponsePacket
.
Encrypted
(
stream
)
"08 28 04 34"
->
ServerSessionKeyResponsePacket
.
Encrypted
(
stream
)
...
@@ -85,13 +102,14 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
...
@@ -85,13 +102,14 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
else
->
throw
IllegalArgumentException
(
idHex
)
else
->
throw
IllegalArgumentException
(
idHex
)
}
}
}
}
.
apply
{
this
.
idHex
=
idHex
}
}
}
}
}
@ExperimentalUnsignedTypes
@ExperimentalUnsignedTypes
override
fun
toString
():
String
{
override
fun
toString
():
String
{
return
this
.
javaClass
.
simpleName
+
this
.
getAllDeclaredFields
().
joinToString
(
", \n
"
,
"{"
,
"}"
)
{
return
this
.
javaClass
.
simpleName
+
"(${this.getFixedId()})"
+
this
.
getAllDeclaredFields
().
joinToString
(
",
"
,
"{"
,
"}"
)
{
it
.
trySetAccessible
();
it
.
name
+
"="
+
it
.
get
(
this
).
let
{
value
->
it
.
trySetAccessible
();
it
.
name
+
"="
+
it
.
get
(
this
).
let
{
value
->
when
(
value
)
{
when
(
value
)
{
is
ByteArray
->
value
.
toUHexString
()
is
ByteArray
->
value
.
toUHexString
()
...
@@ -102,6 +120,15 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
...
@@ -102,6 +120,15 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
}
}
}
}
open
fun
getFixedId
():
String
=
getFixedId
(
this
.
idHex
)
fun
getFixedId
(
id
:
String
):
String
=
when
(
id
.
length
)
{
0
->
"__ __ __ __"
2
->
"$id __ __"
5
->
"$id __"
else
->
id
}
fun
decryptBy
(
key
:
ByteArray
):
DataInputStream
{
fun
decryptBy
(
key
:
ByteArray
):
DataInputStream
{
input
.
goto
(
14
)
input
.
goto
(
14
)
return
DataInputStream
(
TEA
.
decrypt
(
input
.
readAllBytes
().
let
{
it
.
copyOfRange
(
0
,
it
.
size
-
1
)
},
key
).
inputStream
())
return
DataInputStream
(
TEA
.
decrypt
(
input
.
readAllBytes
().
let
{
it
.
copyOfRange
(
0
,
it
.
size
-
1
)
},
key
).
inputStream
())
...
...
mirai-core/src/main/java/net/mamoe/mirai/network/packet/Session.kt
View file @
74fff88a
...
@@ -107,7 +107,7 @@ class ServerSessionKeyResponsePacket(inputStream: DataInputStream, private val d
...
@@ -107,7 +107,7 @@ class ServerSessionKeyResponsePacket(inputStream: DataInputStream, private val d
fun
decrypt
(
sessionResponseDecryptionKey
:
ByteArray
):
ServerSessionKeyResponsePacket
{
fun
decrypt
(
sessionResponseDecryptionKey
:
ByteArray
):
ServerSessionKeyResponsePacket
{
this
.
input
goto
14
this
.
input
goto
14
val
data
=
this
.
input
.
readAllBytes
().
let
{
it
.
copyOfRange
(
0
,
it
.
size
-
1
)
}
val
data
=
this
.
input
.
readAllBytes
().
let
{
it
.
copyOfRange
(
0
,
it
.
size
-
1
)
}
return
ServerSessionKeyResponsePacket
(
TEA
.
decrypt
(
data
,
sessionResponseDecryptionKey
).
dataInputStream
(),
data
.
size
)
return
ServerSessionKeyResponsePacket
(
TEA
.
decrypt
(
data
,
sessionResponseDecryptionKey
).
dataInputStream
(),
data
.
size
)
.
setId
(
this
.
idHex
)
}
}
}
}
}
}
\ No newline at end of file
mirai-core/src/main/java/net/mamoe/mirai/network/packet/Touch.kt
View file @
74fff88a
...
@@ -13,8 +13,9 @@ import java.io.IOException
...
@@ -13,8 +13,9 @@ import java.io.IOException
*
*
* @author Him188moe
* @author Him188moe
*/
*/
@PacketId
(
"08 25 31 0?"
)
class
ServerTouchResponsePacket
(
inputStream
:
DataInputStream
)
:
ServerPacket
(
inputStream
)
{
class
ServerTouchResponsePacket
(
inputStream
:
DataInputStream
)
:
ServerPacket
(
inputStream
)
{
var
serverIP
:
String
?
=
null
;
var
serverIP
:
String
?
=
null
var
loginTime
:
Int
=
0
var
loginTime
:
Int
=
0
lateinit
var
loginIP
:
String
lateinit
var
loginIP
:
String
...
@@ -54,7 +55,7 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp
...
@@ -54,7 +55,7 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp
fun
decrypt
():
ServerTouchResponsePacket
=
ServerTouchResponsePacket
(
decryptBy
(
when
(
type
)
{
fun
decrypt
():
ServerTouchResponsePacket
=
ServerTouchResponsePacket
(
decryptBy
(
when
(
type
)
{
Type
.
TYPE_08_25_31_02
->
Protocol
.
redirectionKey
.
hexToBytes
()
Type
.
TYPE_08_25_31_02
->
Protocol
.
redirectionKey
.
hexToBytes
()
Type
.
TYPE_08_25_31_01
->
Protocol
.
key0825
.
hexToBytes
()
Type
.
TYPE_08_25_31_01
->
Protocol
.
key0825
.
hexToBytes
()
}))
}))
.
setId
(
this
.
idHex
)
}
}
}
}
...
@@ -83,7 +84,6 @@ class ClientTouchPacket(val qq: Long, val serverIp: String) : ClientPacket() {
...
@@ -83,7 +84,6 @@ class ClientTouchPacket(val qq: Long, val serverIp: String) : ClientPacket() {
this
.
writeIP
(
serverIp
);
this
.
writeIP
(
serverIp
);
this
.
writeHex
(
"00 02 00 36 00 12 00 02 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 14 00 1D 01 02 00 19"
)
this
.
writeHex
(
"00 02 00 36 00 12 00 02 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 14 00 1D 01 02 00 19"
)
this
.
writeHex
(
Protocol
.
publicKey
)
this
.
writeHex
(
Protocol
.
publicKey
)
println
(
super
.
toUByteArray
().
toUHexString
())
return
super
.
toByteArray
()
return
super
.
toByteArray
()
}
}
}.
toByteArray
()))
}.
toByteArray
()))
...
...
mirai-core/src/main/java/net/mamoe/mirai/network/packet/VerificationCode.kt
View file @
74fff88a
This diff is collapsed.
Click to expand it.
mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLogin.kt
View file @
74fff88a
...
@@ -2,7 +2,10 @@ package net.mamoe.mirai.network.packet.login
...
@@ -2,7 +2,10 @@ package net.mamoe.mirai.network.packet.login
import
net.mamoe.mirai.network.Protocol
import
net.mamoe.mirai.network.Protocol
import
net.mamoe.mirai.network.packet.*
import
net.mamoe.mirai.network.packet.*
import
net.mamoe.mirai.utils.*
import
net.mamoe.mirai.utils.ByteArrayDataOutputStream
import
net.mamoe.mirai.utils.TEA
import
net.mamoe.mirai.utils.TestedSuccessfully
import
net.mamoe.mirai.utils.hexToBytes
import
java.io.DataOutputStream
import
java.io.DataOutputStream
/**
/**
...
@@ -32,7 +35,6 @@ class ClientPasswordSubmissionPacket(
...
@@ -32,7 +35,6 @@ class ClientPasswordSubmissionPacket(
this
.
encryptAndWrite
(
Protocol
.
shareKey
.
hexToBytes
())
{
this
.
encryptAndWrite
(
Protocol
.
shareKey
.
hexToBytes
())
{
it
.
writePart1
(
qq
,
password
,
loginTime
,
loginIP
,
tgtgtKey
,
token0825
)
it
.
writePart1
(
qq
,
password
,
loginTime
,
loginIP
,
tgtgtKey
,
token0825
)
it
.
writePart2
()
it
.
writePart2
()
println
(
it
.
toByteArray
().
toUHexString
())
}
}
}
}
}
}
...
...
mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponsePasswordVerifiedPacket.kt
View file @
74fff88a
...
@@ -53,7 +53,7 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in
...
@@ -53,7 +53,7 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in
@ExperimentalUnsignedTypes
@ExperimentalUnsignedTypes
fun
decrypt
(
tgtgtKey
:
ByteArray
):
ServerLoginResponseSuccessPacket
{
fun
decrypt
(
tgtgtKey
:
ByteArray
):
ServerLoginResponseSuccessPacket
{
input
goto
14
input
goto
14
return
ServerLoginResponseSuccessPacket
(
TEA
.
decrypt
(
TEA
.
decrypt
(
input
.
readAllBytes
().
cutTail
(
1
),
Protocol
.
shareKey
),
tgtgtKey
).
dataInputStream
())
;
return
ServerLoginResponseSuccessPacket
(
TEA
.
decrypt
(
TEA
.
decrypt
(
input
.
readAllBytes
().
cutTail
(
1
),
Protocol
.
shareKey
),
tgtgtKey
).
dataInputStream
())
.
setId
(
this
.
idHex
)
}
}
}
}
...
...
mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseResendPacket.kt
View file @
74fff88a
...
@@ -44,6 +44,6 @@ class ServerLoginResponseResendPacket(input: DataInputStream, val flag: Flag) :
...
@@ -44,6 +44,6 @@ class ServerLoginResponseResendPacket(input: DataInputStream, val flag: Flag) :
class
Encrypted
(
input
:
DataInputStream
,
private
val
flag
:
Flag
)
:
ServerPacket
(
input
)
{
class
Encrypted
(
input
:
DataInputStream
,
private
val
flag
:
Flag
)
:
ServerPacket
(
input
)
{
@TestedSuccessfully
@TestedSuccessfully
fun
decrypt
(
tgtgtKey
:
ByteArray
):
ServerLoginResponseResendPacket
=
ServerLoginResponseResendPacket
(
decryptBy
(
tgtgtKey
),
flag
)
fun
decrypt
(
tgtgtKey
:
ByteArray
):
ServerLoginResponseResendPacket
=
ServerLoginResponseResendPacket
(
decryptBy
(
tgtgtKey
),
flag
)
.
setId
(
this
.
idHex
)
}
}
}
}
mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseVerificationCodeInitPacket.kt
View file @
74fff88a
...
@@ -43,7 +43,7 @@ class ServerLoginResponseVerificationCodeInitPacket(input: DataInputStream, priv
...
@@ -43,7 +43,7 @@ class ServerLoginResponseVerificationCodeInitPacket(input: DataInputStream, priv
fun
decrypt
():
ServerLoginResponseVerificationCodeInitPacket
{
fun
decrypt
():
ServerLoginResponseVerificationCodeInitPacket
{
this
.
input
goto
14
this
.
input
goto
14
val
data
=
TEA
.
CRYPTOR_SHARE_KEY
.
decrypt
(
this
.
input
.
readAllBytes
().
cutTail
(
1
));
val
data
=
TEA
.
CRYPTOR_SHARE_KEY
.
decrypt
(
this
.
input
.
readAllBytes
().
cutTail
(
1
));
return
ServerLoginResponseVerificationCodeInitPacket
(
data
.
dataInputStream
(),
data
.
size
)
return
ServerLoginResponseVerificationCodeInitPacket
(
data
.
dataInputStream
(),
data
.
size
)
.
setId
(
this
.
idHex
)
}
}
}
}
}
}
...
...
mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiLogger.kt
View file @
74fff88a
package
net.mamoe.mirai.utils
package
net.mamoe.mirai.utils
import
net.mamoe.mirai.Robot
import
java.text.SimpleDateFormat
import
java.text.SimpleDateFormat
import
java.util.*
import
java.util.*
...
@@ -34,6 +35,31 @@ object MiraiLogger {
...
@@ -34,6 +35,31 @@ object MiraiLogger {
}
}
}
}
infix
fun
Robot
.
log
(
o
:
Any
?)
=
info
(
o
)
infix
fun
Robot
.
println
(
o
:
Any
?)
=
info
(
o
)
infix
fun
Robot
.
info
(
o
:
Any
?)
=
print
(
this
,
o
.
toString
(),
LoggerTextFormat
.
RESET
)
infix
fun
Robot
.
error
(
o
:
Any
?)
=
print
(
this
,
o
.
toString
(),
LoggerTextFormat
.
RED
)
infix
fun
Robot
.
notice
(
o
:
Any
?)
=
print
(
this
,
o
.
toString
(),
LoggerTextFormat
.
LIGHT_BLUE
)
infix
fun
Robot
.
purple
(
o
:
Any
?)
=
print
(
this
,
o
.
toString
(),
LoggerTextFormat
.
PURPLE
)
infix
fun
Robot
.
success
(
o
:
Any
?)
=
print
(
this
,
o
.
toString
(),
LoggerTextFormat
.
GREEN
)
infix
fun
Robot
.
debug
(
o
:
Any
?)
=
print
(
this
,
o
.
toString
(),
LoggerTextFormat
.
YELLOW
)
private
fun
print
(
robot
:
Robot
,
value
:
String
?,
color
:
LoggerTextFormat
=
LoggerTextFormat
.
WHITE
)
{
val
s
=
SimpleDateFormat
(
"MM-dd HH:mm:ss"
).
format
(
Date
())
kotlin
.
io
.
println
(
"$color[Mirai] $s #R${robot.id}: $value"
)
}
private
fun
print
(
value
:
String
?,
color
:
LoggerTextFormat
=
LoggerTextFormat
.
WHITE
)
{
val
s
=
SimpleDateFormat
(
"MM-dd HH:mm:ss"
).
format
(
Date
())
kotlin
.
io
.
println
(
"$color[Mirai] $s : $value"
)
}
fun
Any
.
logInfo
()
=
MiraiLogger
.
info
(
this
)
fun
Any
.
logInfo
()
=
MiraiLogger
.
info
(
this
)
...
...
mirai-core/src/main/java/net/mamoe/mirai/utils/Utils.kt
View file @
74fff88a
...
@@ -60,9 +60,6 @@ fun String.hexToShort(): Short = hexToBytes().let { ((it[1].toInt() shl 8) + it[
...
@@ -60,9 +60,6 @@ fun String.hexToShort(): Short = hexToBytes().let { ((it[1].toInt() shl 8) + it[
@ExperimentalUnsignedTypes
@ExperimentalUnsignedTypes
fun
String
.
hexToInt
():
Int
=
hexToBytes
().
let
{
((
it
[
0
].
toInt
()
shl
24
)
+
(
it
[
1
].
toInt
()
shl
16
)
+
(
it
[
2
].
toInt
()
shl
8
)
+
it
[
3
])
}
fun
String
.
hexToInt
():
Int
=
hexToBytes
().
let
{
((
it
[
0
].
toInt
()
shl
24
)
+
(
it
[
1
].
toInt
()
shl
16
)
+
(
it
[
2
].
toInt
()
shl
8
)
+
it
[
3
])
}
@ExperimentalUnsignedTypes
fun
String
.
hexToByte
():
Byte
=
hexToBytes
()[
0
]
open
class
ByteArrayDataOutputStream
:
DataOutputStream
(
ByteArrayOutputStream
())
{
open
class
ByteArrayDataOutputStream
:
DataOutputStream
(
ByteArrayOutputStream
())
{
open
fun
toByteArray
():
ByteArray
=
(
out
as
ByteArrayOutputStream
).
toByteArray
()
open
fun
toByteArray
():
ByteArray
=
(
out
as
ByteArrayOutputStream
).
toByteArray
()
@ExperimentalUnsignedTypes
@ExperimentalUnsignedTypes
...
...
mirai-core/src/test/java/HexComparator.java
View file @
74fff88a
...
@@ -51,9 +51,9 @@ public class HexComparator {
...
@@ -51,9 +51,9 @@ public class HexComparator {
@SuppressWarnings
({
"unused"
,
"NonAsciiCharacters"
})
@SuppressWarnings
({
"unused"
,
"NonAsciiCharacters"
})
private
static
class
TestConsts
{
private
static
class
TestConsts
{
private
static
final
String
牛逼
=
UtilsKt
.
toUHexString
(
"牛逼"
.
getBytes
(),
" "
);
private
static
final
String
牛逼
=
UtilsKt
.
toUHexString
(
"牛逼"
.
getBytes
(),
" "
);
private
static
final
String
_1994701021
=
ClientPacketKt
.
toHexString
(
1994701021
,
" "
);
private
static
final
String
_1994701021
=
ClientPacketKt
.
to
U
HexString
(
1994701021
,
" "
);
private
static
final
String
_1040400290
=
ClientPacketKt
.
toHexString
(
1040400290
,
" "
);
private
static
final
String
_1040400290
=
ClientPacketKt
.
to
U
HexString
(
1040400290
,
" "
);
private
static
final
String
_580266363
=
ClientPacketKt
.
toHexString
(
580266363
,
" "
);
private
static
final
String
_580266363
=
ClientPacketKt
.
to
U
HexString
(
580266363
,
" "
);
}
}
private
final
List
<
Match
>
matches
=
new
LinkedList
<>();
private
final
List
<
Match
>
matches
=
new
LinkedList
<>();
...
...
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