mirror of
https://github.com/Mrs4s/MiraiGo.git
synced 2025-05-04 19:17:38 +08:00
192 lines
6.1 KiB
Go
192 lines
6.1 KiB
Go
// 企点协议相关特殊逻辑
|
|
|
|
package client
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/Mrs4s/MiraiGo/binary"
|
|
"github.com/Mrs4s/MiraiGo/client/internal/network"
|
|
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x3f6"
|
|
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x6ff"
|
|
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
|
"github.com/Mrs4s/MiraiGo/internal/proto"
|
|
"github.com/Mrs4s/MiraiGo/utils"
|
|
)
|
|
|
|
func init() {
|
|
decoders["qidianservice.69"] = decodeLoginExtraResponse
|
|
decoders["HttpConn.0x6ff_501"] = decodeConnKeyResponse
|
|
}
|
|
|
|
// getQiDianAddressDetailList 外部联系人列表
|
|
func (c *QQClient) getQiDianAddressDetailList() ([]*FriendInfo, error) {
|
|
req := &cmd0x6ff.C519ReqBody{
|
|
SubCmd: proto.Uint32(33),
|
|
CrmCommonHead: &cmd0x6ff.C519CRMMsgHead{
|
|
KfUin: proto.Uint64(uint64(c.QiDian.MasterUin)),
|
|
VerNo: proto.Uint32(uint32(utils.ConvertSubVersionToInt(c.version.SortVersionName))),
|
|
CrmSubCmd: proto.Uint32(33),
|
|
LaborUin: proto.Uint64(uint64(c.Uin)),
|
|
},
|
|
GetAddressDetailListReqBody: &cmd0x6ff.GetAddressDetailListReqBody{
|
|
Timestamp2: proto.Uint64(0),
|
|
},
|
|
}
|
|
rspData, err := c.bigDataRequest(1305, req)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "request error")
|
|
}
|
|
rsp := &cmd0x6ff.C519RspBody{}
|
|
if err = proto.Unmarshal(rspData, rsp); err != nil {
|
|
return nil, errors.Wrap(err, "unmarshal error")
|
|
}
|
|
if rsp.GetAddressDetailListRspBody == nil {
|
|
return nil, errors.New("rsp body is nil")
|
|
}
|
|
ret := []*FriendInfo{}
|
|
for _, detail := range rsp.GetAddressDetailListRspBody.AddressDetail {
|
|
if len(detail.Qq) == 0 {
|
|
c.Warning("address detail %v QQ is 0", string(detail.Name))
|
|
continue
|
|
}
|
|
ret = append(ret, &FriendInfo{
|
|
Uin: int64(detail.Qq[0].GetAccount()),
|
|
Nickname: string(detail.Name),
|
|
})
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
func (c *QQClient) buildLoginExtraPacket() (uint16, []byte) {
|
|
req := &cmd0x3f6.C3F6ReqBody{
|
|
SubCmd: proto.Uint32(69),
|
|
CrmCommonHead: &cmd0x3f6.C3F6CRMMsgHead{
|
|
CrmSubCmd: proto.Uint32(69),
|
|
VerNo: proto.Uint32(uint32(utils.ConvertSubVersionToInt(c.version.SortVersionName))),
|
|
Clienttype: proto.Uint32(2),
|
|
},
|
|
SubcmdLoginProcessCompleteReqBody: &cmd0x3f6.QDUserLoginProcessCompleteReqBody{
|
|
Kfext: proto.Uint64(uint64(c.Uin)),
|
|
Pubno: &c.version.AppId,
|
|
Buildno: proto.Uint32(uint32(utils.ConvertSubVersionToInt(c.version.SortVersionName))),
|
|
TerminalType: proto.Uint32(2),
|
|
Status: proto.Uint32(10),
|
|
LoginTime: proto.Uint32(5),
|
|
HardwareInfo: proto.String(string(c.deviceInfo.Model)),
|
|
SoftwareInfo: proto.String(string(c.deviceInfo.Version.Release)),
|
|
Guid: c.deviceInfo.Guid,
|
|
AppName: &c.version.ApkId,
|
|
SubAppId: &c.version.AppId,
|
|
},
|
|
}
|
|
payload, _ := proto.Marshal(req)
|
|
return c.uniPacket("qidianservice.69", payload)
|
|
}
|
|
|
|
func (c *QQClient) buildConnKeyRequestPacket() (uint16, []byte) {
|
|
req := &cmd0x6ff.C501ReqBody{
|
|
ReqBody: &cmd0x6ff.SubCmd0X501ReqBody{
|
|
Uin: proto.Uint64(uint64(c.Uin)),
|
|
IdcId: proto.Uint32(0),
|
|
Appid: proto.Uint32(16),
|
|
LoginSigType: proto.Uint32(1),
|
|
RequestFlag: proto.Uint32(3),
|
|
ServiceTypes: []uint32{1},
|
|
},
|
|
}
|
|
payload, _ := proto.Marshal(req)
|
|
return c.uniPacket("HttpConn.0x6ff_501", payload)
|
|
}
|
|
|
|
func (c *QQClient) bigDataRequest(subCmd uint32, req proto.Message) ([]byte, error) {
|
|
if c.QiDian.bigDataReqSession == nil {
|
|
return nil, errors.New("please call conn key request method before")
|
|
}
|
|
data, _ := proto.Marshal(req)
|
|
head, _ := proto.Marshal(&msg.IMHead{
|
|
HeadType: proto.Uint32(4),
|
|
HttpconnHead: &msg.HttpConnHead{
|
|
Uin: proto.Uint64(uint64(c.Uin)),
|
|
Command: proto.Uint32(1791),
|
|
SubCommand: &subCmd,
|
|
Seq: proto.Uint32(uint32(c.nextHighwayApplySeq())),
|
|
Version: proto.Uint32(386), // todo: short version convert
|
|
Flag: proto.Uint32(1),
|
|
CompressType: proto.Uint32(0),
|
|
ErrorCode: proto.Uint32(0),
|
|
},
|
|
LoginSig: &msg.LoginSig{
|
|
Type: proto.Uint32(22),
|
|
Sig: c.QiDian.bigDataReqSession.SigSession,
|
|
},
|
|
})
|
|
tea := binary.NewTeaCipher(c.QiDian.bigDataReqSession.SessionKey)
|
|
body := tea.Encrypt(data)
|
|
url := fmt.Sprintf("http://%v/cgi-bin/httpconn", c.QiDian.bigDataReqAddrs[0])
|
|
httpReq, _ := http.NewRequest("POST", url, bytes.NewReader(binary.NewWriterF(func(w *binary.Writer) {
|
|
w.WriteByte(40)
|
|
w.WriteUInt32(uint32(len(head)))
|
|
w.WriteUInt32(uint32(len(body)))
|
|
w.Write(head)
|
|
w.Write(body)
|
|
w.WriteByte(41)
|
|
})))
|
|
rsp, err := http.DefaultClient.Do(httpReq)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "request error")
|
|
}
|
|
defer func() { _ = rsp.Body.Close() }()
|
|
rspBody, _ := io.ReadAll(rsp.Body)
|
|
r := binary.NewReader(rspBody)
|
|
r.ReadByte()
|
|
l1 := int(r.ReadInt32())
|
|
l2 := int(r.ReadInt32())
|
|
r.ReadBytes(l1)
|
|
payload := r.ReadBytes(l2)
|
|
return tea.Decrypt(payload), nil
|
|
}
|
|
|
|
func decodeLoginExtraResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
|
|
rsp := cmd0x3f6.C3F6RspBody{}
|
|
if err := proto.Unmarshal(payload, &rsp); err != nil {
|
|
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
|
|
}
|
|
if rsp.SubcmdLoginProcessCompleteRspBody == nil {
|
|
return nil, errors.New("login process resp is nil")
|
|
}
|
|
c.QiDian = &QiDianAccountInfo{
|
|
MasterUin: int64(rsp.SubcmdLoginProcessCompleteRspBody.GetCorpuin()),
|
|
ExtName: rsp.SubcmdLoginProcessCompleteRspBody.GetExtuinName(),
|
|
CreateTime: int64(rsp.SubcmdLoginProcessCompleteRspBody.GetOpenAccountTime()),
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func decodeConnKeyResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
|
|
rsp := cmd0x6ff.C501RspBody{}
|
|
if err := proto.Unmarshal(payload, &rsp); err != nil {
|
|
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
|
|
}
|
|
if c.QiDian == nil {
|
|
return nil, errors.New("please call login extra before")
|
|
}
|
|
c.QiDian.bigDataReqSession = &bigDataSessionInfo{
|
|
SigSession: rsp.RspBody.SigSession,
|
|
SessionKey: rsp.RspBody.SessionKey,
|
|
}
|
|
for _, srv := range rsp.RspBody.Addrs {
|
|
if srv.GetServiceType() == 1 {
|
|
for _, addr := range srv.Addrs {
|
|
c.QiDian.bigDataReqAddrs = append(c.QiDian.bigDataReqAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(addr.GetIp()), addr.GetPort()))
|
|
}
|
|
}
|
|
}
|
|
return nil, nil
|
|
}
|