Commit d19216a6 authored by Him188moe's avatar Him188moe

Updated

parent e3c23b35
......@@ -2,7 +2,7 @@ package net.mamoe.mirai.message.defaults
import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.MessageId
import net.mamoe.mirai.utils.lazyOutput
import net.mamoe.mirai.utils.lazyEncode
import org.intellij.lang.annotations.MagicConstant
import java.util.*
import java.util.stream.Collectors
......@@ -71,7 +71,7 @@ class MessageChain : Message {
return this
}
override fun toByteArray(): ByteArray = lazyOutput {
override fun toByteArray(): ByteArray = lazyEncode {
stream().forEach { message ->
it.write(message.toByteArray())
}
......
......@@ -2,7 +2,9 @@ package net.mamoe.mirai.message.defaults
import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.MessageId
import net.mamoe.mirai.utils.lazyOutput
import net.mamoe.mirai.network.packet.writeLVByteArray
import net.mamoe.mirai.network.packet.writeLVString
import net.mamoe.mirai.utils.lazyEncode
/**
* @author Him188moe
......@@ -14,12 +16,12 @@ class PlainText(private val text: String) : Message() {
return text
}
override fun toByteArray(): ByteArray = lazyOutput { section ->
override fun toByteArray(): ByteArray = lazyEncode { section ->
section.writeByte(this.type)
section.writeVarByteArray(lazyOutput { child ->
section.writeLVByteArray(lazyEncode { child ->
child.writeByte(0x01)
child.writeVarString(this.text)
child.writeLVString(this.text)
})
}
......
......@@ -18,9 +18,13 @@ import net.mamoe.mirai.message.defaults.MessageChain
import net.mamoe.mirai.network.BotNetworkHandler.*
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.network.packet.action.*
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageFailedPacket
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageResponsePacket
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageSuccessPacket
import net.mamoe.mirai.network.packet.login.*
import net.mamoe.mirai.task.MiraiThreadPool
import net.mamoe.mirai.utils.*
import java.awt.image.BufferedImage
import java.io.Closeable
import java.net.DatagramPacket
import java.net.DatagramSocket
......@@ -301,7 +305,7 @@ class BotNetworkHandler(private val bot: Bot) : Closeable {
private lateinit var loginIP: String
private var tgtgtKey: ByteArray = getRandomByteArray(16)
private var tlv0105: ByteArray = lazyOutput {
private var tlv0105: ByteArray = lazyEncode {
it.writeHex("01 05 00 30")
it.writeHex("00 01 01 02 00 14 01 01 00 10")
it.writeRandom(16)
......@@ -560,14 +564,24 @@ class BotNetworkHandler(private val bot: Bot) : Closeable {
internal var gtk: Int = 0
private val addFriendSessions = Collections.synchronizedCollection(mutableListOf<AddFriendSession>())
private val uploadImageSessions = Collections.synchronizedCollection(mutableListOf<UploadImageSession>())
override fun onPacketReceived(packet: ServerPacket) {
when (packet) {
is ServerCanAddFriendResponsePacket -> {
this.addFriendSessions.forEach {
this.uploadImageSessions.forEach {
it.onPacketReceived(packet)
}
}
is ServerTryUploadGroupImageSuccessPacket -> {
ImageNetworkUtils.postImage(packet.uKey.toUHexString(), )
}
is ServerTryUploadGroupImageFailedPacket -> {
}
is ServerTryUploadGroupImageResponsePacket.Encrypted -> distributePacket(packet.decrypt(sessionKey))
else -> {
}
}
......@@ -581,7 +595,7 @@ class BotNetworkHandler(private val bot: Bot) : Closeable {
fun addFriend(qqNumber: Long, message: Lazy<String> = lazyOf("")): CompletableFuture<AddFriendResult> {
val future = CompletableFuture<AddFriendResult>()
val session = AddFriendSession(qqNumber, future, message)
addFriendSessions.add(session)
uploadImageSessions.add(session)
session.sendAddRequest();
return future
}
......@@ -590,6 +604,58 @@ class BotNetworkHandler(private val bot: Bot) : Closeable {
}
private inner class UploadImageSession(
private val group: Long,
private val future: CompletableFuture<AddFriendResult>,
private val image: BufferedImage
) : Closeable {
lateinit var id: ByteArray
fun onPacketReceived(packet: ServerPacket) {
if (!::id.isInitialized) {
return
}
when (packet) {
is ServerCanAddFriendResponsePacket -> {
if (!(packet.idByteArray[2] == id[0] && packet.idByteArray[3] == id[1])) {
return
}
when (packet.state) {
ServerCanAddFriendResponsePacket.State.FAILED -> {
future.complete(AddFriendResult.FAILED)
close()
}
ServerCanAddFriendResponsePacket.State.ALREADY_ADDED -> {
future.complete(AddFriendResult.ALREADY_ADDED)
close()
}
ServerCanAddFriendResponsePacket.State.REQUIRE_VERIFICATION -> {
sendPacket(ClientAddFriendPacket(bot.account.qqNumber, qq, sessionKey))
}
ServerCanAddFriendResponsePacket.State.NOT_REQUIRE_VERIFICATION -> {
}
}
}
}
}
override fun sendRequest() {
}
override fun close() {
uploadImageSessions.remove(this)
}
}
private inner class AddFriendSession(
private val qq: Long,
private val future: CompletableFuture<AddFriendResult>,
......@@ -638,7 +704,7 @@ class BotNetworkHandler(private val bot: Bot) : Closeable {
}
override fun close() {
addFriendSessions.remove(this)
uploadImageSessions.remove(this)
}
}
}
......
package net.mamoe.mirai.network.handler
import net.mamoe.mirai.Bot
import net.mamoe.mirai.network.BotNetworkHandler
/**
* @author Him188moe
*/
data class BotSession(
val bot: Bot,
val sessionKey: ByteArray,
val networkHandler: BotNetworkHandler
) {
}
\ No newline at end of file
......@@ -113,7 +113,7 @@ fun DataOutputStream.encryptAndWrite(byteArray: ByteArray, key: ByteArray) {
}
fun DataOutputStream.encryptAndWrite(key: ByteArray, encoder: (ByteArrayDataOutputStream) -> Unit) {
this.write(TEA.encrypt(ByteArrayDataOutputStream().let { encoder(it); it.toByteArray() }, key))
this.write(TEA.encrypt(ByteArrayDataOutputStream().also(encoder).toByteArray(), key))
}
@ExperimentalUnsignedTypes
......@@ -204,7 +204,7 @@ fun Int.toLByteArray(): ByteArray = byteArrayOf(
)
@ExperimentalUnsignedTypes
fun Int.toUHexString(separator: String = " "): String = this.toByteArray().toUByteArray().toUHexString(separator)
fun Int.toUHexString(separator: String = " "): String = this.toByteArray().toUHexString(separator)
internal fun md5(str: String): ByteArray = MessageDigest.getInstance("MD5").digest(str.toByteArray())
......@@ -237,21 +237,11 @@ fun DataOutputStream.writeGroup(groupIdOrGroupNumber: Long) {
this.write(groupIdOrGroupNumber.toUInt().toByteArray())
}
fun DataOutputStream.writeVarByteArray(byteArray: ByteArray) {
fun DataOutputStream.writeLVByteArray(byteArray: ByteArray) {
this.writeShort(byteArray.size)
this.write(byteArray)
}
fun DataOutputStream.writeVarString(str: String) {
this.writeVarByteArray(str.toByteArray())
}
fun DataOutputStream.writeVarShort(short: Int) {
this.writeByte(0x02)
this.writeShort(short)
}
fun DataOutputStream.writeVarInt(int: Int) {
this.writeByte(0x04)
this.writeInt(int)
fun DataOutputStream.writeLVString(str: String) {
this.writeLVByteArray(str.toByteArray())
}
\ No newline at end of file
......@@ -19,7 +19,7 @@ import java.util.zip.GZIPInputStream
*
* @author Him188moe
*/
open class ServerEventPacket(input: DataInputStream, val packetId: ByteArray, val eventIdentity: ByteArray) : ServerPacket(input) {
abstract class ServerEventPacket(input: DataInputStream, val packetId: ByteArray, val eventIdentity: ByteArray) : ServerPacket(input) {
@PacketId("00 17")
class Raw(input: DataInputStream, private val packetId: ByteArray) : ServerPacket(input) {
@ExperimentalUnsignedTypes
......
......@@ -7,6 +7,7 @@ import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacke
import net.mamoe.mirai.network.packet.login.*
import net.mamoe.mirai.utils.*
import java.io.DataInputStream
import java.io.EOFException
/**
* @author Him188moe
......@@ -169,7 +170,7 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
return this.decryptBy(keyHex1.hexToBytes(), keyHex2.hexToBytes())
}
private fun decryptAsByteArray(key: ByteArray): ByteArray {
fun decryptAsByteArray(key: ByteArray): ByteArray {
input.goto(14)
return TEA.decrypt(input.readAllBytes().cutTail(1), key)
}
......@@ -257,4 +258,47 @@ fun <N : Number> DataInputStream.readShortAt(position: N): Short {
return this.readShort();
}
@ExperimentalUnsignedTypes
@JvmSynthetic
fun DataInputStream.gotoWhere(matcher: UByteArray): DataInputStream {
return this.gotoWhere(matcher.toByteArray())
}
/**
* 去往下一个含这些连续字节的位置
*/
@Throws(EOFException::class)
fun DataInputStream.gotoWhere(matcher: ByteArray): DataInputStream {
require(matcher.isNotEmpty())
loop@
do {
val byte = this.readByte()
if (byte == matcher[0]) {
//todo mark here
for (i in 1 until matcher.size) {
val b = this.readByte()
if (b != matcher[i]) {
continue@loop //todo goto mark
}
return this
}
}
} while (true)
}
/*
@Throws(EOFException::class)
fun DataInputStream.gotoWhere(matcher: ByteArray) {
require(matcher.isNotEmpty())
do {
val byte = this.readByte()
if (byte == matcher[0]) {
for (i in 1 until matcher.size){
}
}
} while (true)
}*/
fun ByteArray.cutTail(length: Int): ByteArray = this.copyOfRange(0, this.size - length)
\ No newline at end of file
......@@ -4,7 +4,7 @@ import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.utils.ByteArrayDataOutputStream
import net.mamoe.mirai.utils.TEA
import net.mamoe.mirai.utils.getRandomByteArray
import net.mamoe.mirai.utils.lazyOutput
import net.mamoe.mirai.utils.lazyEncode
import java.io.DataInputStream
import java.net.InetAddress
......@@ -85,7 +85,7 @@ class ServerSessionKeyResponsePacket(inputStream: DataInputStream, private val d
527 -> {
input.goto(63)
sessionKey = input.readNBytes(16)
tlv0105 = lazyOutput {
tlv0105 = lazyEncode {
it.writeHex("01 05 00 88 00 01 01 02 00 40 02 01 03 3C 01 03 00 00")
input.goto(dataLength - 122)
it.write(input.readNBytes(56))
......
......@@ -163,7 +163,7 @@ class ServerVerificationCodeWrongPacket(input: DataInputStream, dataSize: Int, p
* @author Him188moe
*/
@PacketId("00 BA 31")
open class ServerVerificationCodeTransmissionPacket(input: DataInputStream, private val dataSize: Int, private val packetId: ByteArray) : ServerVerificationCodePacket(input) {
abstract class ServerVerificationCodeTransmissionPacket(input: DataInputStream, private val dataSize: Int, private val packetId: ByteArray) : ServerVerificationCodePacket(input) {
lateinit var captchaSectionN: ByteArray
lateinit var verificationToken: ByteArray//56bytes
......
......@@ -111,7 +111,7 @@ class ServerAddGroupResponsePacket(input: DataInputStream) : ServerAddContactRes
/**
* 添加好友/群的回复
*/
open class ServerAddContactResponsePacket(input: DataInputStream) : ServerPacket(input) {
abstract class ServerAddContactResponsePacket(input: DataInputStream) : ServerPacket(input) {
class Raw(input: DataInputStream) : ServerPacket(input) {
......
......@@ -3,7 +3,7 @@ package net.mamoe.mirai.network.packet.action
import net.mamoe.mirai.message.defaults.MessageChain
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.utils.lazyOutput
import net.mamoe.mirai.utils.lazyEncode
import java.io.DataInputStream
/**
......@@ -29,7 +29,7 @@ class ClientSendFriendMessagePacket(
it.writeHex("37 0F")
it.writeQQ(botQQ)
it.writeQQ(targetQQ)
it.write(md5(lazyOutput { md5Key -> md5Key.writeQQ(targetQQ); md5Key.write(sessionKey) }))
it.write(md5(lazyEncode { md5Key -> md5Key.writeQQ(targetQQ); md5Key.write(sessionKey) }))
it.writeHex("00 0B")
it.writeRandom(2)
it.writeTime()
......
......@@ -4,16 +4,19 @@ import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.utils.toByteArray
import net.mamoe.mirai.utils.writeUVarInt
import java.awt.image.BufferedImage
import java.io.DataInputStream
/**
* 查询群消息的 image id.
* That is, 查询服务器上是否有这个图片, 有就返回 id, 没有就需要上传
* 请求上传图片. 将发送图片的 md5, size.
* 服务器返回以下之一:
* - 服务器已经存有这个图片 [ServerTryUploadGroupImageFailedPacket]
* - 服务器未存有, 返回一个 key 用于客户端上传 [ServerTryUploadGroupImageSuccessPacket]
*
* @author Him188moe
*/
@PacketId("03 88")
@ExperimentalUnsignedTypes
class ClientGetGroupImageIDPacket(
class ClientTryGetGroupImageIDPacket(
private val bot: Long,
private val sessionKey: ByteArray,
private val group: Long,
......@@ -85,6 +88,41 @@ class ClientGetGroupImageIDPacket(
}
}
abstract class ServerTryUploadGroupImageResponsePacket(input: DataInputStream) : ServerPacket(input) {
class Encrypted(input: DataInputStream) : ServerPacket(input) {
fun decrypt(sessionKey: ByteArray): ServerTryUploadGroupImageResponsePacket {
val data = this.decryptAsByteArray(sessionKey)
if (data.size == 239) {
return ServerTryUploadGroupImageSuccessPacket(data.dataInputStream()).setId(this.idHex)
}
return ServerTryUploadGroupImageFailedPacket(data.dataInputStream())
}
}
}
/**
* 服务器未存有图片, 返回一个 key 用于客户端上传
*/
class ServerTryUploadGroupImageSuccessPacket(input: DataInputStream) : ServerTryUploadGroupImageResponsePacket(input) {
lateinit var uKey: ByteArray
@ExperimentalUnsignedTypes
override fun decode() {
uKey = this.input.gotoWhere(ubyteArrayOf(0x42u, 0x80u, 0x01u)).readNBytes(128)
}
}
/**
* 服务器已经存有这个图片
*/
class ServerTryUploadGroupImageFailedPacket(input: DataInputStream) : ServerTryUploadGroupImageResponsePacket(input) {
override fun decode() {
}
}
fun main() {
println(0xff)
......
package net.mamoe.mirai.utils;
import org.apache.commons.httpclient.util.HttpURLConnection;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
/**
* @author NaturalHG
*/
public class ImageNetworkUtils {
public static void postImage(String ukey, int fileSize, String g_uin,String groupCode, byte[] img){
public static void postImage(String uKeyHex, int fileSize, String qqNumber, String groupCode, byte[] img) throws IOException {
//http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc&ukey=” + 删全部空 (ukey) + “&filesize=” + 到文本 (fileSize) + “&range=0&uin=” + g_uin + “&groupcode=” + Group
StringBuilder builder = new StringBuilder("http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc");
builder.append("&ukey=")
.append(ukey.trim())
.append(uKeyHex.trim())
.append("&filezise=").append(fileSize)
.append("&range=").append("0")
.append("&uin=").append(g_uin)
.append("&uin=").append(qqNumber)
.append("&groupcode=").append(groupCode);
try {
HttpURLConnection conn = (HttpURLConnection) new URL(builder.toString()).openConnection();
conn.setRequestProperty("User-agent","QQClient");
conn.setRequestProperty("Content-length","" + fileSize);
conn.setRequestMethod("POST");
conn.getOutputStream().write(img);
HttpURLConnection conn = (HttpURLConnection) new URL(builder.toString()).openConnection();
conn.setRequestProperty("User-agent", "QQClient");
conn.setRequestProperty("Content-length", "" + fileSize);
conn.setRequestMethod("POST");
conn.getOutputStream().write(img);
conn.connect();
System.out.println(conn.getResponseCode());
} catch (IOException e) {
e.printStackTrace();
}
conn.connect();
System.out.println(conn.getResponseCode());
System.out.println(conn.getResponseMessage());
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment