1
0
mirror of https://github.com/Mrs4s/MiraiGo.git synced 2025-05-04 11:07:40 +08:00
MiraiGo/client/upload_file.go
2022-06-19 22:59:20 +08:00

177 lines
4.7 KiB
Go

package client
import (
"crypto/md5"
"crypto/sha1"
"fmt"
"io"
"github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/client/internal/highway"
"github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x346"
"github.com/Mrs4s/MiraiGo/client/pb/exciting"
"github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/message"
)
func init() {
decoders["OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_UPLOAD_V3-1700"] = decodePrivateFileUploadReq
}
type FileResource struct {
FileName string
Body io.ReadSeeker // FileResource content body
size int64
md5 []byte
sha1 []byte
}
type fileUploadRsp struct {
existed bool
busid int32
uuid []byte
uploadKey []byte
// upload group file need
UploadIpLanV4 []string
UploadPort int32
}
func (f *FileResource) init() {
md5H := md5.New()
sha1H := sha1.New()
whence, _ := f.Body.Seek(0, io.SeekCurrent)
f.size, _ = io.Copy(io.MultiWriter(md5H, sha1H), f.Body)
_, _ = f.Body.Seek(whence, io.SeekStart) // restore
// calculate md5&sha1 hash
f.md5 = md5H.Sum(nil)
f.sha1 = sha1H.Sum(nil)
}
func (c *QQClient) _UploadFile(target message.Source, file *FileResource) error {
file.init()
// 同文件等待其他线程上传
fkey := string(file.sha1)
fsWaiter.Wait(fkey)
defer fsWaiter.Done(fkey)
var rsp *fileUploadRsp
if target.SourceType == message.SourcePrivate {
i, err := c.sendAndWait(c.buildPrivateFileUploadReqPacket(target, file))
if err != nil {
return err
}
rsp = i.(*fileUploadRsp)
}
if !rsp.existed {
ext := &exciting.FileUploadExt{
Unknown1: proto.Int32(100),
Unknown2: proto.Int32(2),
Entry: &exciting.FileUploadEntry{
BusiBuff: &exciting.ExcitingBusiInfo{
BusId: proto.Int32(rsp.busid),
SenderUin: proto.Some(c.Uin),
ReceiverUin: proto.Some(target.PrimaryID),
GroupCode: proto.Int64(0),
},
FileEntry: &exciting.ExcitingFileEntry{
FileSize: proto.Some(file.size),
Md5: file.md5,
Sha1: file.sha1,
FileId: rsp.uuid,
UploadKey: rsp.uploadKey,
},
ClientInfo: &exciting.ExcitingClientInfo{
ClientType: proto.Int32(2),
AppId: proto.String(fmt.Sprint(c.version.AppId)),
TerminalType: proto.Int32(2),
ClientVer: proto.String("d92615c5"),
Unknown: proto.Int32(4),
},
FileNameInfo: &exciting.ExcitingFileNameInfo{
FileName: proto.Some(file.FileName),
},
},
Unknown200: proto.Int32(1),
}
if target.SourceType == message.SourceGroup {
if len(rsp.UploadIpLanV4) == 0 {
return errors.New("server requires unsupported ftn upload")
}
ext.Unknown3 = proto.Int32(0)
ext.Unknown200 = proto.None[int32]()
ext.Entry.BusiBuff.GroupCode = proto.Int64(target.PrimaryID)
ext.Entry.Host = &exciting.ExcitingHostConfig{
Hosts: []*exciting.ExcitingHostInfo{
{
Url: &exciting.ExcitingUrlInfo{
Unknown: proto.Int32(1),
Host: proto.Some(rsp.UploadIpLanV4[0]),
},
Port: proto.Some(rsp.UploadPort),
},
},
}
}
extPkt, _ := proto.Marshal(ext)
input := highway.Transaction{
CommandID: 71,
Body: file.Body,
Size: file.size,
Sum: file.md5,
Ticket: c.highwaySession.SigSession,
Ext: extPkt,
}
if target.SourceType == message.SourcePrivate {
input.CommandID = 69
}
if _, err := c.highwaySession.UploadExciting(input); err != nil {
return errors.Wrap(err, "upload failed")
}
}
return nil
}
func (c *QQClient) buildPrivateFileUploadReqPacket(target message.Source, file *FileResource) (uint16, []byte) {
req := cmd0x346.C346ReqBody{
Cmd: 1700,
Seq: c.nextFriendSeq(),
ApplyUploadReqV3: &cmd0x346.ApplyUploadReqV3{
SenderUin: c.Uin,
RecverUin: target.PrimaryID,
FileSize: file.size,
FileName: file.FileName,
Bytes_10MMd5: file.md5, // TODO: investigate this
Sha: file.sha1,
LocalFilepath: "/storage/emulated/0/Android/data/com.tencent.mobileqq/Tencent/QQfile_recv/" + file.FileName,
Md5: file.md5,
},
BusinessId: 3,
ClientType: 104,
FlagSupportMediaplatform: 1,
}
pkg, _ := proto.Marshal(&req)
return c.uniPacket("OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_UPLOAD_V3-1700", pkg)
}
// OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_UPLOAD_V3-1700
func decodePrivateFileUploadReq(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) {
var rsp cmd0x346.C346RspBody
err := proto.Unmarshal(payload, &rsp)
if err != nil {
return nil, err
}
v3 := rsp.ApplyUploadRspV3
r := &fileUploadRsp{
existed: v3.BoolFileExist,
busid: 3,
uuid: v3.Uuid,
uploadKey: v3.MediaPlateformUploadKey,
}
return r, nil
}