2023-06-30 11:05:33 +00:00
|
|
|
package libgm
|
2023-06-30 09:54:08 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2023-07-15 12:57:07 +00:00
|
|
|
"sync"
|
2023-06-30 09:54:08 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"golang.org/x/exp/slices"
|
|
|
|
"google.golang.org/protobuf/proto"
|
2023-07-09 11:16:52 +00:00
|
|
|
|
|
|
|
"go.mau.fi/mautrix-gmessages/libgm/pblite"
|
2023-06-30 09:54:08 +00:00
|
|
|
|
|
|
|
"go.mau.fi/mautrix-gmessages/libgm/binary"
|
|
|
|
"go.mau.fi/mautrix-gmessages/libgm/payload"
|
2023-07-09 11:16:52 +00:00
|
|
|
"go.mau.fi/mautrix-gmessages/libgm/routes"
|
2023-06-30 09:54:08 +00:00
|
|
|
"go.mau.fi/mautrix-gmessages/libgm/util"
|
|
|
|
)
|
|
|
|
|
2023-07-15 22:45:57 +00:00
|
|
|
type SessionHandler struct {
|
2023-07-09 11:16:52 +00:00
|
|
|
client *Client
|
2023-06-30 09:54:08 +00:00
|
|
|
|
2023-07-15 22:45:57 +00:00
|
|
|
responseWaiters map[string]chan<- *pblite.Response
|
|
|
|
responseWaitersLock sync.Mutex
|
2023-06-30 09:54:08 +00:00
|
|
|
|
2023-07-15 12:57:07 +00:00
|
|
|
ackMapLock sync.Mutex
|
|
|
|
ackMap []string
|
|
|
|
ackTicker *time.Ticker
|
2023-06-30 09:54:08 +00:00
|
|
|
|
2023-07-15 22:45:57 +00:00
|
|
|
sessionID string
|
2023-06-30 09:54:08 +00:00
|
|
|
|
|
|
|
responseTimeout time.Duration
|
|
|
|
}
|
|
|
|
|
2023-07-15 22:45:57 +00:00
|
|
|
func (s *SessionHandler) ResetSessionID() {
|
|
|
|
s.sessionID = util.RandomUUIDv4()
|
2023-06-30 09:54:08 +00:00
|
|
|
}
|
|
|
|
|
2023-07-15 22:45:57 +00:00
|
|
|
func (s *SessionHandler) sendMessageNoResponse(actionType binary.ActionType, encryptedData proto.Message) error {
|
|
|
|
_, payload, _, err := s.buildMessage(actionType, encryptedData)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = s.client.rpc.sendMessageRequest(util.SEND_MESSAGE, payload)
|
|
|
|
return err
|
2023-06-30 09:54:08 +00:00
|
|
|
}
|
|
|
|
|
2023-07-15 22:45:57 +00:00
|
|
|
func (s *SessionHandler) sendAsyncMessage(actionType binary.ActionType, encryptedData proto.Message) (<-chan *pblite.Response, error) {
|
|
|
|
requestID, payload, _, buildErr := s.buildMessage(actionType, encryptedData)
|
2023-07-09 11:16:52 +00:00
|
|
|
if buildErr != nil {
|
2023-07-15 22:45:57 +00:00
|
|
|
return nil, buildErr
|
2023-06-30 09:54:08 +00:00
|
|
|
}
|
|
|
|
|
2023-07-15 22:45:57 +00:00
|
|
|
ch := s.waitResponse(requestID)
|
2023-07-09 11:16:52 +00:00
|
|
|
_, reqErr := s.client.rpc.sendMessageRequest(util.SEND_MESSAGE, payload)
|
|
|
|
if reqErr != nil {
|
2023-07-15 22:45:57 +00:00
|
|
|
s.cancelResponse(requestID, ch)
|
|
|
|
return nil, reqErr
|
|
|
|
}
|
|
|
|
return ch, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SessionHandler) sendMessage(actionType binary.ActionType, encryptedData proto.Message) (*pblite.Response, error) {
|
|
|
|
ch, err := s.sendAsyncMessage(actionType, encryptedData)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2023-06-30 09:54:08 +00:00
|
|
|
}
|
2023-07-15 22:45:57 +00:00
|
|
|
|
|
|
|
// TODO add timeout
|
|
|
|
return <-ch, nil
|
2023-07-09 11:16:52 +00:00
|
|
|
}
|
2023-06-30 09:54:08 +00:00
|
|
|
|
2023-07-09 11:16:52 +00:00
|
|
|
func (s *SessionHandler) buildMessage(actionType binary.ActionType, encryptedData proto.Message) (string, []byte, binary.ActionType, error) {
|
2023-07-15 22:45:57 +00:00
|
|
|
var requestID string
|
2023-07-09 11:16:52 +00:00
|
|
|
pairedDevice := s.client.authData.DevicePair.Mobile
|
2023-07-15 22:45:57 +00:00
|
|
|
sessionId := s.client.sessionHandler.sessionID
|
2023-07-09 11:16:52 +00:00
|
|
|
token := s.client.authData.TachyonAuthToken
|
2023-06-30 09:54:08 +00:00
|
|
|
|
2023-07-09 11:16:52 +00:00
|
|
|
routeInfo, ok := routes.Routes[actionType]
|
|
|
|
if !ok {
|
|
|
|
return "", nil, 0, fmt.Errorf("failed to build message: could not find route %d", actionType)
|
2023-06-30 09:54:08 +00:00
|
|
|
}
|
|
|
|
|
2023-07-09 11:16:52 +00:00
|
|
|
if routeInfo.UseSessionID {
|
2023-07-15 22:45:57 +00:00
|
|
|
requestID = s.sessionID
|
2023-07-09 11:16:52 +00:00
|
|
|
} else {
|
2023-07-15 22:45:57 +00:00
|
|
|
requestID = util.RandomUUIDv4()
|
2023-06-30 09:54:08 +00:00
|
|
|
}
|
2023-07-09 11:16:52 +00:00
|
|
|
|
2023-07-15 22:45:57 +00:00
|
|
|
tmpMessage := payload.NewSendMessageBuilder(token, pairedDevice, requestID, sessionId).SetRoute(routeInfo.Action).SetSessionId(s.sessionID)
|
2023-07-09 11:16:52 +00:00
|
|
|
|
|
|
|
if encryptedData != nil {
|
|
|
|
tmpMessage.SetEncryptedProtoMessage(encryptedData, s.client.authData.Cryptor)
|
2023-06-30 09:54:08 +00:00
|
|
|
}
|
|
|
|
|
2023-07-09 11:16:52 +00:00
|
|
|
if routeInfo.UseTTL {
|
|
|
|
tmpMessage.SetTTL(s.client.authData.TTL)
|
2023-06-30 09:54:08 +00:00
|
|
|
}
|
2023-07-09 11:16:52 +00:00
|
|
|
|
|
|
|
message, buildErr := tmpMessage.Build()
|
|
|
|
if buildErr != nil {
|
|
|
|
return "", nil, 0, buildErr
|
2023-06-30 09:54:08 +00:00
|
|
|
}
|
2023-07-09 11:16:52 +00:00
|
|
|
|
2023-07-15 22:45:57 +00:00
|
|
|
return requestID, message, routeInfo.Action, nil
|
2023-06-30 09:54:08 +00:00
|
|
|
}
|
|
|
|
|
2023-07-15 22:45:57 +00:00
|
|
|
func (s *SessionHandler) queueMessageAck(messageID string) {
|
2023-07-15 12:57:07 +00:00
|
|
|
s.ackMapLock.Lock()
|
|
|
|
defer s.ackMapLock.Unlock()
|
2023-07-15 22:45:57 +00:00
|
|
|
if !slices.Contains(s.ackMap, messageID) {
|
|
|
|
s.ackMap = append(s.ackMap, messageID)
|
|
|
|
s.client.Logger.Trace().Any("message_id", messageID).Msg("Queued ack for message")
|
|
|
|
} else {
|
|
|
|
s.client.Logger.Trace().Any("message_id", messageID).Msg("Ack for message was already queued")
|
2023-06-30 09:54:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SessionHandler) startAckInterval() {
|
|
|
|
if s.ackTicker != nil {
|
|
|
|
s.ackTicker.Stop()
|
|
|
|
}
|
|
|
|
ticker := time.NewTicker(5 * time.Second)
|
|
|
|
s.ackTicker = ticker
|
|
|
|
go func() {
|
|
|
|
for range ticker.C {
|
|
|
|
s.sendAckRequest()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SessionHandler) sendAckRequest() {
|
2023-07-15 12:57:07 +00:00
|
|
|
s.ackMapLock.Lock()
|
2023-07-15 12:49:51 +00:00
|
|
|
dataToAck := s.ackMap
|
|
|
|
s.ackMap = nil
|
2023-07-15 12:57:07 +00:00
|
|
|
s.ackMapLock.Unlock()
|
|
|
|
if len(dataToAck) == 0 {
|
|
|
|
return
|
|
|
|
}
|
2023-07-15 12:49:51 +00:00
|
|
|
ackMessages := make([]*binary.AckMessageData, len(dataToAck))
|
|
|
|
for i, reqID := range dataToAck {
|
|
|
|
ackMessages[i] = &binary.AckMessageData{
|
|
|
|
RequestID: reqID,
|
|
|
|
Device: s.client.authData.DevicePair.Browser,
|
|
|
|
}
|
|
|
|
}
|
2023-06-30 09:54:08 +00:00
|
|
|
ackMessagePayload := &binary.AckMessagePayload{
|
|
|
|
AuthData: &binary.AuthMessage{
|
2023-07-15 12:49:51 +00:00
|
|
|
RequestID: util.RandomUUIDv4(),
|
2023-07-09 11:16:52 +00:00
|
|
|
TachyonAuthToken: s.client.authData.TachyonAuthToken,
|
|
|
|
ConfigVersion: payload.ConfigMessage,
|
2023-06-30 09:54:08 +00:00
|
|
|
},
|
|
|
|
EmptyArr: &binary.EmptyArr{},
|
2023-07-15 12:49:51 +00:00
|
|
|
Acks: ackMessages,
|
2023-06-30 09:54:08 +00:00
|
|
|
}
|
2023-07-15 12:56:55 +00:00
|
|
|
jsonData, err := pblite.Marshal(ackMessagePayload)
|
2023-06-30 09:54:08 +00:00
|
|
|
if err != nil {
|
2023-07-09 20:32:19 +00:00
|
|
|
panic(err)
|
2023-06-30 09:54:08 +00:00
|
|
|
}
|
|
|
|
_, err = s.client.rpc.sendMessageRequest(util.ACK_MESSAGES, jsonData)
|
|
|
|
if err != nil {
|
2023-07-09 20:32:19 +00:00
|
|
|
panic(err)
|
2023-06-30 09:54:08 +00:00
|
|
|
}
|
2023-07-15 15:49:28 +00:00
|
|
|
s.client.Logger.Debug().Strs("message_ids", dataToAck).Msg("Sent acks")
|
2023-06-30 09:54:08 +00:00
|
|
|
}
|