Refactor login flow

This commit is contained in:
Tulir Asokan 2023-07-16 15:55:30 +03:00
parent 7fe4f0b047
commit 6225b83d2f
15 changed files with 276 additions and 305 deletions

View file

@ -18,6 +18,7 @@ package main
import ( import (
"context" "context"
"fmt"
"github.com/skip2/go-qrcode" "github.com/skip2/go-qrcode"
"maunium.net/go/mautrix" "maunium.net/go/mautrix"
@ -82,57 +83,82 @@ func fnLogin(ce *WrappedCommandEvent) {
ce.Reply("You're already logged in. Perhaps you wanted to `reconnect`?") ce.Reply("You're already logged in. Perhaps you wanted to `reconnect`?")
} }
return return
} } else if ce.User.pairSuccessChan != nil {
ce.Reply("You already have a login in progress")
ce.User.hackyLoginCommand = ce
ce.User.hackyLoginCommandPrevEvent = ""
_, err := ce.User.Login(context.Background())
if err != nil {
ce.User.log.Errorf("Failed to log in:", err)
ce.Reply("Failed to log in: %v", err)
return return
} }
ch, err := ce.User.Login(context.Background(), 6)
if err != nil {
ce.ZLog.Err(err).Msg("Failed to start login")
ce.Reply("Failed to start login: %v", err)
return
}
var prevEvent id.EventID
for item := range ch {
switch {
case item.qr != "":
ce.ZLog.Debug().Msg("Got code in QR channel")
prevEvent = ce.User.sendQR(ce, item.qr, prevEvent)
case item.err != nil:
ce.ZLog.Err(err).Msg("Error in QR channel")
prevEvent = ce.User.sendQREdit(ce, &event.MessageEventContent{
MsgType: event.MsgNotice,
Body: fmt.Sprintf("Failed to log in: %v", err),
}, prevEvent)
case item.success:
ce.ZLog.Debug().Msg("Got pair success in QR channel")
prevEvent = ce.User.sendQREdit(ce, &event.MessageEventContent{
MsgType: event.MsgNotice,
Body: "Successfully logged in",
}, prevEvent)
}
}
ce.ZLog.Trace().Msg("Login command finished")
} }
func (user *User) sendQR(ce *WrappedCommandEvent, code string, prevEvent id.EventID) id.EventID { func (user *User) sendQREdit(ce *WrappedCommandEvent, content *event.MessageEventContent, prevEvent id.EventID) id.EventID {
url, ok := user.uploadQR(ce, code)
if !ok {
return prevEvent
}
content := event.MessageEventContent{
MsgType: event.MsgImage,
Body: code,
URL: url.CUString(),
}
if len(prevEvent) != 0 { if len(prevEvent) != 0 {
content.SetEdit(prevEvent) content.SetEdit(prevEvent)
} }
resp, err := ce.Bot.SendMessageEvent(ce.RoomID, event.EventMessage, &content) resp, err := ce.Bot.SendMessageEvent(ce.RoomID, event.EventMessage, &content)
if err != nil { if err != nil {
user.log.Errorln("Failed to send edited QR code to user:", err) ce.ZLog.Err(err).Msg("Failed to send edited QR code")
} else if len(prevEvent) == 0 { } else if len(prevEvent) == 0 {
prevEvent = resp.EventID prevEvent = resp.EventID
} }
return prevEvent return prevEvent
} }
func (user *User) uploadQR(ce *WrappedCommandEvent, code string) (id.ContentURI, bool) { func (user *User) sendQR(ce *WrappedCommandEvent, code string, prevEvent id.EventID) id.EventID {
var content event.MessageEventContent
url, err := user.uploadQR(code)
if err != nil {
ce.ZLog.Err(err).Msg("Failed to upload QR code")
content = event.MessageEventContent{
MsgType: event.MsgNotice,
Body: fmt.Sprintf("Failed to upload QR code: %v", err),
}
} else {
content = event.MessageEventContent{
MsgType: event.MsgImage,
Body: code,
URL: url.CUString(),
}
}
return user.sendQREdit(ce, &content, prevEvent)
}
func (user *User) uploadQR(code string) (id.ContentURI, error) {
qrCode, err := qrcode.Encode(code, qrcode.Low, 256) qrCode, err := qrcode.Encode(code, qrcode.Low, 256)
if err != nil { if err != nil {
user.log.Errorln("Failed to encode QR code:", err) return id.ContentURI{}, err
ce.Reply("Failed to encode QR code: %v", err)
return id.ContentURI{}, false
} }
resp, err := user.bridge.Bot.UploadBytes(qrCode, "image/png")
bot := user.bridge.AS.BotClient()
resp, err := bot.UploadBytes(qrCode, "image/png")
if err != nil { if err != nil {
user.log.Errorln("Failed to upload QR code:", err) return id.ContentURI{}, err
ce.Reply("Failed to upload QR code: %v", err)
return id.ContentURI{}, false
} }
return resp.ContentURI, true return resp.ContentURI, nil
} }
var cmdLogout = &commands.FullHandler{ var cmdLogout = &commands.FullHandler{
@ -236,7 +262,7 @@ func fnPing(ce *WrappedCommandEvent) {
ce.Reply("You're not logged into Google Messages.") ce.Reply("You're not logged into Google Messages.")
} }
} else if ce.User.Client == nil || !ce.User.Client.IsConnected() { } else if ce.User.Client == nil || !ce.User.Client.IsConnected() {
ce.Reply("You're logged in as %s (device #%d), but you don't have a Google Messages connection.", ce.User.Phone) ce.Reply("You're logged in as %s, but you don't have a Google Messages connection.", ce.User.Phone)
} else { } else {
ce.Reply("Logged in as %s, connection to Google Messages may be OK", ce.User.Phone) ce.Reply("Logged in as %s, connection to Google Messages may be OK", ce.User.Phone)
} }

View file

@ -39,11 +39,13 @@ func main() {
w.TimeFormat = time.Stamp w.TimeFormat = time.Stamp
})).With().Timestamp().Logger() })).With().Timestamp().Logger()
file, err := os.Open("session.json") file, err := os.Open("session.json")
var doLogin bool
if err != nil { if err != nil {
if !errors.Is(err, os.ErrNotExist) { if !errors.Is(err, os.ErrNotExist) {
panic(err) panic(err)
} }
sess = *libgm.NewAuthData() sess = *libgm.NewAuthData()
doLogin = true
} else { } else {
must(json.NewDecoder(file).Decode(&sess)) must(json.NewDecoder(file).Decode(&sess))
log.Info().Msg("Loaded session?") log.Info().Msg("Loaded session?")
@ -51,7 +53,23 @@ func main() {
_ = file.Close() _ = file.Close()
cli = libgm.NewClient(&sess, log) cli = libgm.NewClient(&sess, log)
cli.SetEventHandler(evtHandler) cli.SetEventHandler(evtHandler)
must(cli.Connect()) if doLogin {
qr := mustReturn(cli.StartLogin())
qrterminal.GenerateHalfBlock(qr, qrterminal.L, os.Stdout)
go func() {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for range ticker.C {
if sess.Browser != nil {
return
}
qr := mustReturn(cli.RefreshPhoneRelay())
qrterminal.GenerateHalfBlock(qr, qrterminal.L, os.Stdout)
}
}()
} else {
must(cli.Connect())
}
c := make(chan os.Signal) c := make(chan os.Signal)
input := make(chan string) input := make(chan string)
@ -97,20 +115,12 @@ func evtHandler(rawEvt any) {
log.Debug().Any("data", evt).Msg("Client is ready!") log.Debug().Any("data", evt).Msg("Client is ready!")
case *events.PairSuccessful: case *events.PairSuccessful:
log.Debug().Any("data", evt).Msg("Pair successful") log.Debug().Any("data", evt).Msg("Pair successful")
//kd := evt.Data.(*binary.AuthenticationContainer_KeyData)
//sess.DevicePair = &pblite.DevicePair{
// Mobile: kd.KeyData.Mobile,
// Browser: kd.KeyData.Browser,
//}
//sess.TachyonAuthToken = evt.AuthMessage.TachyonAuthToken
saveSession() saveSession()
log.Debug().Msg("Wrote session") log.Debug().Msg("Wrote session")
case *binary.Message: case *binary.Message:
log.Debug().Any("data", evt).Msg("Message event") log.Debug().Any("data", evt).Msg("Message event")
case *binary.Conversation: case *binary.Conversation:
log.Debug().Any("data", evt).Msg("Conversation event") log.Debug().Any("data", evt).Msg("Conversation event")
case *events.QR:
qrterminal.GenerateHalfBlock(evt.URL, qrterminal.L, os.Stdout)
case *events.BrowserActive: case *events.BrowserActive:
log.Debug().Any("data", evt).Msg("Browser active") log.Debug().Any("data", evt).Msg("Browser active")
default: default:

View file

@ -5,12 +5,10 @@ import (
"crypto/rand" "crypto/rand"
"crypto/sha256" "crypto/sha256"
"encoding/base64" "encoding/base64"
"encoding/json"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"os"
"strconv" "strconv"
"time" "time"
@ -21,7 +19,6 @@ import (
"go.mau.fi/mautrix-gmessages/libgm/binary" "go.mau.fi/mautrix-gmessages/libgm/binary"
"go.mau.fi/mautrix-gmessages/libgm/crypto" "go.mau.fi/mautrix-gmessages/libgm/crypto"
"go.mau.fi/mautrix-gmessages/libgm/events" "go.mau.fi/mautrix-gmessages/libgm/events"
"go.mau.fi/mautrix-gmessages/libgm/payload"
"go.mau.fi/mautrix-gmessages/libgm/pblite" "go.mau.fi/mautrix-gmessages/libgm/pblite"
"go.mau.fi/mautrix-gmessages/libgm/util" "go.mau.fi/mautrix-gmessages/libgm/util"
) )
@ -50,11 +47,10 @@ type EventHandler func(evt any)
type Client struct { type Client struct {
Logger zerolog.Logger Logger zerolog.Logger
rpc *RPC rpc *RPC
pairer *Pairer
evHandler EventHandler evHandler EventHandler
sessionHandler *SessionHandler sessionHandler *SessionHandler
authData *AuthData AuthData *AuthData
proxy Proxy proxy Proxy
http *http.Client http *http.Client
@ -73,7 +69,7 @@ func NewClient(authData *AuthData, logger zerolog.Logger) *Client {
responseTimeout: time.Duration(5000) * time.Millisecond, responseTimeout: time.Duration(5000) * time.Millisecond,
} }
cli := &Client{ cli := &Client{
authData: authData, AuthData: authData,
Logger: logger, Logger: logger,
sessionHandler: sessionHandler, sessionHandler: sessionHandler,
http: &http.Client{}, http: &http.Client{},
@ -102,46 +98,52 @@ func (c *Client) SetProxy(proxy string) error {
c.Logger.Debug().Any("proxy", proxyParsed.Host).Msg("SetProxy") c.Logger.Debug().Any("proxy", proxyParsed.Host).Msg("SetProxy")
return nil return nil
} }
func (c *Client) Connect() error { func (c *Client) Connect() error {
if c.authData.TachyonAuthToken != nil { if c.AuthData.TachyonAuthToken == nil {
refreshErr := c.refreshAuthToken() return fmt.Errorf("no auth token")
if refreshErr != nil { } else if c.AuthData.Browser == nil {
panic(refreshErr) return fmt.Errorf("not logged in")
}
webEncryptionKeyResponse, webEncryptionKeyErr := c.GetWebEncryptionKey()
if webEncryptionKeyErr != nil {
c.Logger.Err(webEncryptionKeyErr).Any("response", webEncryptionKeyResponse).Msg("GetWebEncryptionKey request failed")
return webEncryptionKeyErr
}
c.updateWebEncryptionKey(webEncryptionKeyResponse.GetKey())
go c.rpc.ListenReceiveMessages()
c.sessionHandler.startAckInterval()
bugleRes, bugleErr := c.IsBugleDefault()
if bugleErr != nil {
panic(bugleErr)
}
c.Logger.Info().Any("isBugle", bugleRes.Success).Msg("IsBugleDefault")
sessionErr := c.SetActiveSession()
if sessionErr != nil {
panic(sessionErr)
}
return nil
} else {
pairer, err := c.NewPairer(c.authData.RefreshKey, 20)
if err != nil {
panic(err)
}
c.pairer = pairer
registered, err2 := c.pairer.RegisterPhoneRelay()
if err2 != nil {
return err2
}
c.authData.TachyonAuthToken = registered.AuthKeyData.TachyonAuthToken
go c.rpc.ListenReceiveMessages()
return nil
} }
refreshErr := c.refreshAuthToken()
if refreshErr != nil {
panic(refreshErr)
}
webEncryptionKeyResponse, webEncryptionKeyErr := c.GetWebEncryptionKey()
if webEncryptionKeyErr != nil {
c.Logger.Err(webEncryptionKeyErr).Any("response", webEncryptionKeyResponse).Msg("GetWebEncryptionKey request failed")
return webEncryptionKeyErr
}
c.updateWebEncryptionKey(webEncryptionKeyResponse.GetKey())
go c.rpc.ListenReceiveMessages()
c.sessionHandler.startAckInterval()
bugleRes, bugleErr := c.IsBugleDefault()
if bugleErr != nil {
panic(bugleErr)
}
c.Logger.Info().Any("isBugle", bugleRes.Success).Msg("IsBugleDefault")
sessionErr := c.SetActiveSession()
if sessionErr != nil {
panic(sessionErr)
}
return nil
}
func (c *Client) StartLogin() (string, error) {
registered, err := c.RegisterPhoneRelay()
if err != nil {
return "", err
}
c.AuthData.TachyonAuthToken = registered.AuthKeyData.TachyonAuthToken
go c.rpc.ListenReceiveMessages()
qr, err := c.GenerateQRCodeData(registered.GetPairingKey())
if err != nil {
return "", fmt.Errorf("failed to generate QR code: %w", err)
}
return qr, nil
} }
func (c *Client) Disconnect() { func (c *Client) Disconnect() {
@ -154,7 +156,7 @@ func (c *Client) IsConnected() bool {
} }
func (c *Client) IsLoggedIn() bool { func (c *Client) IsLoggedIn() bool {
return c.authData != nil && c.authData.Browser != nil return c.AuthData != nil && c.AuthData.Browser != nil
} }
func (c *Client) Reconnect() error { func (c *Client) Reconnect() error {
@ -164,10 +166,10 @@ func (c *Client) Reconnect() error {
} }
err := c.Connect() err := c.Connect()
if err != nil { if err != nil {
c.Logger.Err(err).Any("tachyonAuthToken", c.authData.TachyonAuthToken).Msg("Failed to reconnect") c.Logger.Err(err).Any("tachyonAuthToken", c.AuthData.TachyonAuthToken).Msg("Failed to reconnect")
return err return err
} }
c.Logger.Debug().Any("tachyonAuthToken", c.authData.TachyonAuthToken).Msg("Successfully reconnected to server") c.Logger.Debug().Any("tachyonAuthToken", c.AuthData.TachyonAuthToken).Msg("Successfully reconnected to server")
return nil return nil
} }
@ -185,8 +187,8 @@ func (c *Client) DownloadMedia(mediaID string, key []byte) ([]byte, error) {
}, },
AuthData: &binary.AuthMessage{ AuthData: &binary.AuthMessage{
RequestID: uuid.NewString(), RequestID: uuid.NewString(),
TachyonAuthToken: c.authData.TachyonAuthToken, TachyonAuthToken: c.AuthData.TachyonAuthToken,
ConfigVersion: payload.ConfigMessage, ConfigVersion: util.ConfigMessage,
}, },
} }
downloadMetadataBytes, err2 := proto.Marshal(downloadMetadata) downloadMetadataBytes, err2 := proto.Marshal(downloadMetadata)
@ -242,7 +244,7 @@ func (c *Client) FetchConfigVersion() {
panic(parseErr) panic(parseErr)
} }
currVersion := payload.ConfigMessage currVersion := util.ConfigMessage
if version.Year != currVersion.Year || version.Month != currVersion.Month || version.Day != currVersion.Day { if version.Year != currVersion.Year || version.Month != currVersion.Month || version.Day != currVersion.Day {
toLog := c.diffVersionFormat(currVersion, version) toLog := c.diffVersionFormat(currVersion, version)
c.Logger.Info().Any("version", toLog).Msg("There's a new version available!") c.Logger.Info().Any("version", toLog).Msg("There's a new version available!")
@ -257,56 +259,26 @@ func (c *Client) diffVersionFormat(curr *binary.ConfigVersion, latest *binary.Co
func (c *Client) updateWebEncryptionKey(key []byte) { func (c *Client) updateWebEncryptionKey(key []byte) {
c.Logger.Debug().Any("key", key).Msg("Updated WebEncryptionKey") c.Logger.Debug().Any("key", key).Msg("Updated WebEncryptionKey")
c.authData.WebEncryptionKey = key c.AuthData.WebEncryptionKey = key
} }
func (c *Client) updateTachyonAuthToken(t []byte, validFor int64) { func (c *Client) updateTachyonAuthToken(t []byte, validFor int64) {
c.authData.TachyonAuthToken = t c.AuthData.TachyonAuthToken = t
validForDuration := time.Duration(validFor) * time.Microsecond validForDuration := time.Duration(validFor) * time.Microsecond
if validForDuration == 0 { if validForDuration == 0 {
validForDuration = 24 * time.Hour validForDuration = 24 * time.Hour
} }
c.authData.TachyonExpiry = time.Now().UTC().Add(time.Microsecond * time.Duration(validFor)) c.AuthData.TachyonExpiry = time.Now().UTC().Add(time.Microsecond * time.Duration(validFor))
c.authData.TachyonTTL = validForDuration.Microseconds() c.AuthData.TachyonTTL = validForDuration.Microseconds()
c.Logger.Debug().Time("tachyon_expiry", c.authData.TachyonExpiry).Int64("valid_for", validFor).Msg("Updated tachyon token") c.Logger.Debug().Time("tachyon_expiry", c.AuthData.TachyonExpiry).Int64("valid_for", validFor).Msg("Updated tachyon token")
}
func (c *Client) updateDevicePair(mobile, browser *binary.Device) {
c.authData.Mobile = mobile
c.authData.Browser = browser
c.Logger.Debug().Any("mobile", mobile).Any("browser", browser).Msg("Updated device pair")
}
func (c *Client) SaveAuthSession(path string) error {
toSaveJson, jsonErr := json.Marshal(c.authData)
if jsonErr != nil {
return jsonErr
}
writeErr := os.WriteFile(path, toSaveJson, os.ModePerm)
return writeErr
}
func LoadAuthSession(path string) (*AuthData, error) {
jsonData, readErr := os.ReadFile(path)
if readErr != nil {
return nil, readErr
}
sessionData := &AuthData{}
marshalErr := json.Unmarshal(jsonData, sessionData)
if marshalErr != nil {
return nil, marshalErr
}
return sessionData, nil
} }
func (c *Client) refreshAuthToken() error { func (c *Client) refreshAuthToken() error {
if c.authData.Browser == nil || time.Until(c.authData.TachyonExpiry) > RefreshTachyonBuffer { if c.AuthData.Browser == nil || time.Until(c.AuthData.TachyonExpiry) > RefreshTachyonBuffer {
return nil return nil
} }
c.Logger.Debug().Time("tachyon_expiry", c.authData.TachyonExpiry).Msg("Refreshing auth token") c.Logger.Debug().Time("tachyon_expiry", c.AuthData.TachyonExpiry).Msg("Refreshing auth token")
jwk := c.authData.RefreshKey jwk := c.AuthData.RefreshKey
requestID := uuid.NewString() requestID := uuid.NewString()
timestamp := time.Now().UnixMilli() * 1000 timestamp := time.Now().UnixMilli() * 1000
@ -319,10 +291,10 @@ func (c *Client) refreshAuthToken() error {
payload, err := pblite.Marshal(&binary.RegisterRefreshPayload{ payload, err := pblite.Marshal(&binary.RegisterRefreshPayload{
MessageAuth: &binary.AuthMessage{ MessageAuth: &binary.AuthMessage{
RequestID: requestID, RequestID: requestID,
TachyonAuthToken: c.authData.TachyonAuthToken, TachyonAuthToken: c.AuthData.TachyonAuthToken,
ConfigVersion: payload.ConfigMessage, ConfigVersion: util.ConfigMessage,
}, },
CurrBrowserDevice: c.authData.Browser, CurrBrowserDevice: c.AuthData.Browser,
UnixTimestamp: timestamp, UnixTimestamp: timestamp,
Signature: sig, Signature: sig,
EmptyRefreshArr: &binary.EmptyRefreshArr{EmptyArr: &binary.EmptyArr{}}, EmptyRefreshArr: &binary.EmptyRefreshArr{EmptyArr: &binary.EmptyArr{}},
@ -363,6 +335,6 @@ func (c *Client) refreshAuthToken() error {
validFor, _ := strconv.ParseInt(resp.GetTokenData().GetValidFor(), 10, 64) validFor, _ := strconv.ParseInt(resp.GetTokenData().GetValidFor(), 10, 64)
c.updateTachyonAuthToken(token, validFor) c.updateTachyonAuthToken(token, validFor)
c.triggerEvent(events.NewAuthTokenRefreshed(token)) c.triggerEvent(&events.AuthTokenRefreshed{})
return nil return nil
} }

View file

@ -43,7 +43,7 @@ func (r *RPC) deduplicateUpdate(response *pblite.Response) bool {
} }
func (r *RPC) HandleRPCMsg(msg *binary.InternalMessage) { func (r *RPC) HandleRPCMsg(msg *binary.InternalMessage) {
response, decodeErr := pblite.DecryptInternalMessage(msg, r.client.authData.RequestCrypto) response, decodeErr := pblite.DecryptInternalMessage(msg, r.client.AuthData.RequestCrypto)
if decodeErr != nil { if decodeErr != nil {
r.client.Logger.Error().Err(decodeErr).Msg("rpc decrypt msg err") r.client.Logger.Error().Err(decodeErr).Msg("rpc decrypt msg err")
return return
@ -55,7 +55,6 @@ func (r *RPC) HandleRPCMsg(msg *binary.InternalMessage) {
r.client.sessionHandler.queueMessageAck(response.ResponseID) r.client.sessionHandler.queueMessageAck(response.ResponseID)
if r.client.sessionHandler.receiveResponse(response) { if r.client.sessionHandler.receiveResponse(response) {
r.client.Logger.Debug().Str("request_id", response.Data.RequestID).Msg("Received response")
return return
} }
switch response.BugleRoute { switch response.BugleRoute {

View file

@ -19,15 +19,7 @@ func NewClientReady(sessionID string, conversationList *binary.Conversations) *C
} }
} }
type AuthTokenRefreshed struct { type AuthTokenRefreshed struct{}
Token []byte
}
func NewAuthTokenRefreshed(token []byte) *AuthTokenRefreshed {
return &AuthTokenRefreshed{
Token: token,
}
}
type HTTPError struct { type HTTPError struct {
Action string Action string

View file

@ -12,7 +12,6 @@ import (
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"go.mau.fi/mautrix-gmessages/libgm/binary" "go.mau.fi/mautrix-gmessages/libgm/binary"
"go.mau.fi/mautrix-gmessages/libgm/payload"
"go.mau.fi/mautrix-gmessages/libgm/util" "go.mau.fi/mautrix-gmessages/libgm/util"
) )
@ -131,10 +130,10 @@ func (c *Client) buildStartUploadPayload() (string, error) {
ImageType: 1, ImageType: 1,
AuthData: &binary.AuthMessage{ AuthData: &binary.AuthMessage{
RequestID: uuid.NewString(), RequestID: uuid.NewString(),
TachyonAuthToken: c.authData.TachyonAuthToken, TachyonAuthToken: c.AuthData.TachyonAuthToken,
ConfigVersion: payload.ConfigMessage, ConfigVersion: util.ConfigMessage,
}, },
Mobile: c.authData.Mobile, Mobile: c.AuthData.Mobile,
} }
protoDataBytes, err := proto.Marshal(protoData) protoDataBytes, err := proto.Marshal(protoData)

View file

@ -3,38 +3,16 @@ package libgm
import ( import (
"crypto/x509" "crypto/x509"
"io" "io"
"time"
"github.com/google/uuid" "github.com/google/uuid"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"go.mau.fi/mautrix-gmessages/libgm/binary" "go.mau.fi/mautrix-gmessages/libgm/binary"
"go.mau.fi/mautrix-gmessages/libgm/crypto"
"go.mau.fi/mautrix-gmessages/libgm/events"
"go.mau.fi/mautrix-gmessages/libgm/payload"
"go.mau.fi/mautrix-gmessages/libgm/util" "go.mau.fi/mautrix-gmessages/libgm/util"
) )
type Pairer struct { func (c *Client) RegisterPhoneRelay() (*binary.RegisterPhoneRelayResponse, error) {
client *Client key, err := x509.MarshalPKIXPublicKey(c.AuthData.RefreshKey.GetPublicKey())
KeyData *crypto.JWK
ticker *time.Ticker
tickerTime time.Duration
pairingKey []byte
}
func (c *Client) NewPairer(keyData *crypto.JWK, refreshQrCodeTime int) (*Pairer, error) {
p := &Pairer{
client: c,
KeyData: keyData,
tickerTime: time.Duration(refreshQrCodeTime) * time.Second,
}
c.pairer = p
return p, nil
}
func (p *Pairer) RegisterPhoneRelay() (*binary.RegisterPhoneRelayResponse, error) {
key, err := x509.MarshalPKIXPublicKey(p.KeyData.GetPublicKey())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -42,10 +20,10 @@ func (p *Pairer) RegisterPhoneRelay() (*binary.RegisterPhoneRelayResponse, error
body, err := proto.Marshal(&binary.AuthenticationContainer{ body, err := proto.Marshal(&binary.AuthenticationContainer{
AuthMessage: &binary.AuthMessage{ AuthMessage: &binary.AuthMessage{
RequestID: uuid.NewString(), RequestID: uuid.NewString(),
Network: &payload.Network, Network: &util.Network,
ConfigVersion: payload.ConfigMessage, ConfigVersion: util.ConfigMessage,
}, },
BrowserDetails: payload.BrowserDetailsMessage, BrowserDetails: util.BrowserDetailsMessage,
Data: &binary.AuthenticationContainer_KeyData{ Data: &binary.AuthenticationContainer_KeyData{
KeyData: &binary.KeyData{ KeyData: &binary.KeyData{
EcdsaKeys: &binary.ECDSAKeys{ EcdsaKeys: &binary.ECDSAKeys{
@ -56,91 +34,66 @@ func (p *Pairer) RegisterPhoneRelay() (*binary.RegisterPhoneRelayResponse, error
}, },
}) })
if err != nil { if err != nil {
p.client.Logger.Err(err) c.Logger.Err(err)
return &binary.RegisterPhoneRelayResponse{}, err
}
relayResponse, reqErr := p.client.MakeRelayRequest(util.RegisterPhoneRelayURL, body)
if reqErr != nil {
p.client.Logger.Err(reqErr)
return nil, err return nil, err
} }
responseBody, err2 := io.ReadAll(relayResponse.Body) relayResponse, reqErr := c.MakeRelayRequest(util.RegisterPhoneRelayURL, body)
if err2 != nil { if reqErr != nil {
return nil, err2 c.Logger.Err(reqErr)
return nil, err
}
responseBody, err := io.ReadAll(relayResponse.Body)
if err != nil {
return nil, err
} }
relayResponse.Body.Close() relayResponse.Body.Close()
res := &binary.RegisterPhoneRelayResponse{} res := &binary.RegisterPhoneRelayResponse{}
err3 := proto.Unmarshal(responseBody, res) err = proto.Unmarshal(responseBody, res)
if err3 != nil { if err != nil {
return nil, err3 return nil, err
} }
p.pairingKey = res.GetPairingKey()
p.client.Logger.Debug().Any("response", res).Msg("Registerphonerelay response")
url, qrErr := p.GenerateQRCodeData()
if qrErr != nil {
return nil, qrErr
}
p.client.triggerEvent(&events.QR{URL: url})
p.startRefreshRelayTask()
return res, err return res, err
} }
func (p *Pairer) startRefreshRelayTask() { func (c *Client) RefreshPhoneRelay() (string, error) {
if p.ticker != nil {
p.ticker.Stop()
}
ticker := time.NewTicker(30 * time.Second)
p.ticker = ticker
go func() {
for range ticker.C {
p.RefreshPhoneRelay()
}
}()
}
func (p *Pairer) RefreshPhoneRelay() {
body, err := proto.Marshal(&binary.AuthenticationContainer{ body, err := proto.Marshal(&binary.AuthenticationContainer{
AuthMessage: &binary.AuthMessage{ AuthMessage: &binary.AuthMessage{
RequestID: uuid.NewString(), RequestID: uuid.NewString(),
Network: &payload.Network, Network: &util.Network,
TachyonAuthToken: p.client.authData.TachyonAuthToken, TachyonAuthToken: c.AuthData.TachyonAuthToken,
ConfigVersion: payload.ConfigMessage, ConfigVersion: util.ConfigMessage,
}, },
}) })
if err != nil { if err != nil {
p.client.Logger.Err(err).Msg("refresh phone relay err") return "", err
return
} }
relayResponse, reqErr := p.client.MakeRelayRequest(util.RefreshPhoneRelayURL, body) relayResponse, err := c.MakeRelayRequest(util.RefreshPhoneRelayURL, body)
if reqErr != nil { if err != nil {
p.client.Logger.Err(reqErr).Msg("refresh phone relay err") return "", err
} }
responseBody, err2 := io.ReadAll(relayResponse.Body) responseBody, err := io.ReadAll(relayResponse.Body)
defer relayResponse.Body.Close() defer relayResponse.Body.Close()
if err2 != nil { if err != nil {
p.client.Logger.Err(err2).Msg("refresh phone relay err") return "", err
} }
p.client.Logger.Debug().Any("responseLength", len(responseBody)).Msg("Response Body Length")
res := &binary.RefreshPhoneRelayResponse{} res := &binary.RefreshPhoneRelayResponse{}
err3 := proto.Unmarshal(responseBody, res) err = proto.Unmarshal(responseBody, res)
if err3 != nil { if err != nil {
p.client.Logger.Err(err3) return "", err
} }
p.pairingKey = res.GetPairKey() qr, err := c.GenerateQRCodeData(res.GetPairKey())
p.client.Logger.Debug().Any("res", res).Msg("RefreshPhoneRelayResponse") if err != nil {
url, qrErr := p.GenerateQRCodeData() return "", err
if qrErr != nil {
panic(qrErr)
} }
p.client.triggerEvent(&events.QR{URL: url}) return qr, nil
} }
func (c *Client) GetWebEncryptionKey() (*binary.WebEncryptionKeyResponse, error) { func (c *Client) GetWebEncryptionKey() (*binary.WebEncryptionKeyResponse, error) {
body, err := proto.Marshal(&binary.AuthenticationContainer{ body, err := proto.Marshal(&binary.AuthenticationContainer{
AuthMessage: &binary.AuthMessage{ AuthMessage: &binary.AuthMessage{
RequestID: uuid.NewString(), RequestID: uuid.NewString(),
TachyonAuthToken: c.authData.TachyonAuthToken, TachyonAuthToken: c.AuthData.TachyonAuthToken,
ConfigVersion: payload.ConfigMessage, ConfigVersion: util.ConfigMessage,
}, },
}) })
if err != nil { if err != nil {
@ -160,25 +113,20 @@ func (c *Client) GetWebEncryptionKey() (*binary.WebEncryptionKeyResponse, error)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if c.pairer != nil {
if c.pairer.ticker != nil {
c.pairer.ticker.Stop()
}
}
return parsedResponse, nil return parsedResponse, nil
} }
func (c *Client) Unpair() (*binary.RevokeRelayPairingResponse, error) { func (c *Client) Unpair() (*binary.RevokeRelayPairingResponse, error) {
if c.authData.TachyonAuthToken == nil || c.authData.Browser == nil { if c.AuthData.TachyonAuthToken == nil || c.AuthData.Browser == nil {
return nil, nil return nil, nil
} }
payload, err := proto.Marshal(&binary.RevokeRelayPairing{ payload, err := proto.Marshal(&binary.RevokeRelayPairing{
AuthMessage: &binary.AuthMessage{ AuthMessage: &binary.AuthMessage{
RequestID: uuid.NewString(), RequestID: uuid.NewString(),
TachyonAuthToken: c.authData.TachyonAuthToken, TachyonAuthToken: c.AuthData.TachyonAuthToken,
ConfigVersion: payload.ConfigMessage, ConfigVersion: util.ConfigMessage,
}, },
Browser: c.authData.Browser, Browser: c.AuthData.Browser,
}) })
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -17,7 +17,7 @@ func (c *Client) handlePairingEvent(response *pblite.Response) {
switch evt := pairEventData.Event.(type) { switch evt := pairEventData.Event.(type) {
case *binary.PairEvents_Paired: case *binary.PairEvents_Paired:
callbackErr := c.pairCallback(evt.Paired) callbackErr := c.completePairing(evt.Paired)
if callbackErr != nil { if callbackErr != nil {
panic(callbackErr) panic(callbackErr)
} }
@ -29,27 +29,12 @@ func (c *Client) handlePairingEvent(response *pblite.Response) {
} }
} }
func (c *Client) NewDevicePair(mobile, browser *binary.Device) *pblite.DevicePair { func (c *Client) completePairing(data *binary.PairedData) error {
return &pblite.DevicePair{ c.updateTachyonAuthToken(data.GetTokenData().GetTachyonAuthToken(), data.GetTokenData().GetTTL())
Mobile: mobile, c.AuthData.Mobile = data.Mobile
Browser: browser, c.AuthData.Browser = data.Browser
}
}
func (c *Client) pairCallback(data *binary.PairedData) error { c.triggerEvent(&events.PairSuccessful{PairedData: data})
tokenData := data.GetTokenData()
c.updateTachyonAuthToken(tokenData.GetTachyonAuthToken(), tokenData.GetTTL())
c.updateDevicePair(data.Mobile, data.Browser)
webEncryptionKeyResponse, webErr := c.GetWebEncryptionKey()
if webErr != nil {
return webErr
}
c.updateWebEncryptionKey(webEncryptionKeyResponse.GetKey())
c.triggerEvent(&events.PairSuccessful{data})
reconnectErr := c.Reconnect() reconnectErr := c.Reconnect()
if reconnectErr != nil { if reconnectErr != nil {

View file

@ -9,6 +9,7 @@ import (
"go.mau.fi/mautrix-gmessages/libgm/crypto" "go.mau.fi/mautrix-gmessages/libgm/crypto"
"go.mau.fi/mautrix-gmessages/libgm/pblite" "go.mau.fi/mautrix-gmessages/libgm/pblite"
"go.mau.fi/mautrix-gmessages/libgm/routes" "go.mau.fi/mautrix-gmessages/libgm/routes"
"go.mau.fi/mautrix-gmessages/libgm/util"
) )
type SendMessageBuilder struct { type SendMessageBuilder struct {
@ -32,7 +33,7 @@ func NewSendMessageBuilder(tachyonAuthToken []byte, pairedDevice *binary.Device,
MessageAuth: &binary.SendMessageAuth{ MessageAuth: &binary.SendMessageAuth{
RequestID: requestId, RequestID: requestId,
TachyonAuthToken: tachyonAuthToken, TachyonAuthToken: tachyonAuthToken,
ConfigVersion: ConfigMessage, ConfigVersion: util.ConfigMessage,
}, },
EmptyArr: &binary.EmptyArr{}, EmptyArr: &binary.EmptyArr{},
}, },

View file

@ -9,11 +9,11 @@ import (
"go.mau.fi/mautrix-gmessages/libgm/util" "go.mau.fi/mautrix-gmessages/libgm/util"
) )
func (p *Pairer) GenerateQRCodeData() (string, error) { func (c *Client) GenerateQRCodeData(pairingKey []byte) (string, error) {
urlData := &binary.URLData{ urlData := &binary.URLData{
PairingKey: p.pairingKey, PairingKey: pairingKey,
AESKey: p.client.authData.RequestCrypto.AESKey, AESKey: c.AuthData.RequestCrypto.AESKey,
HMACKey: p.client.authData.RequestCrypto.HMACKey, HMACKey: c.AuthData.RequestCrypto.HMACKey,
} }
encodedURLData, err := proto.Marshal(urlData) encodedURLData, err := proto.Marshal(urlData)
if err != nil { if err != nil {

View file

@ -15,7 +15,6 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"go.mau.fi/mautrix-gmessages/libgm/events" "go.mau.fi/mautrix-gmessages/libgm/events"
"go.mau.fi/mautrix-gmessages/libgm/payload"
"go.mau.fi/mautrix-gmessages/libgm/pblite" "go.mau.fi/mautrix-gmessages/libgm/pblite"
"go.mau.fi/mautrix-gmessages/libgm/binary" "go.mau.fi/mautrix-gmessages/libgm/binary"
@ -51,8 +50,8 @@ func (r *RPC) ListenReceiveMessages() {
receivePayload, err := pblite.Marshal(&binary.ReceiveMessagesRequest{ receivePayload, err := pblite.Marshal(&binary.ReceiveMessagesRequest{
Auth: &binary.AuthMessage{ Auth: &binary.AuthMessage{
RequestID: listenReqID, RequestID: listenReqID,
TachyonAuthToken: r.client.authData.TachyonAuthToken, TachyonAuthToken: r.client.AuthData.TachyonAuthToken,
ConfigVersion: payload.ConfigMessage, ConfigVersion: util.ConfigMessage,
}, },
Unknown: &binary.ReceiveMessagesRequest_UnknownEmptyObject2{ Unknown: &binary.ReceiveMessagesRequest_UnknownEmptyObject2{
Unknown: &binary.ReceiveMessagesRequest_UnknownEmptyObject1{}, Unknown: &binary.ReceiveMessagesRequest_UnknownEmptyObject1{},
@ -91,7 +90,7 @@ func (r *RPC) ListenReceiveMessages() {
} }
r.client.Logger.Debug().Int("statusCode", resp.StatusCode).Msg("Long polling opened") r.client.Logger.Debug().Int("statusCode", resp.StatusCode).Msg("Long polling opened")
r.conn = resp.Body r.conn = resp.Body
if r.client.authData.Browser != nil { if r.client.AuthData.Browser != nil {
go func() { go func() {
err := r.client.NotifyDittoActivity() err := r.client.NotifyDittoActivity()
if err != nil { if err != nil {

View file

@ -73,9 +73,9 @@ func (s *SessionHandler) sendMessage(actionType binary.ActionType, encryptedData
func (s *SessionHandler) buildMessage(actionType binary.ActionType, encryptedData proto.Message) (string, []byte, binary.ActionType, error) { func (s *SessionHandler) buildMessage(actionType binary.ActionType, encryptedData proto.Message) (string, []byte, binary.ActionType, error) {
var requestID string var requestID string
pairedDevice := s.client.authData.Mobile pairedDevice := s.client.AuthData.Mobile
sessionId := s.client.sessionHandler.sessionID sessionId := s.client.sessionHandler.sessionID
token := s.client.authData.TachyonAuthToken token := s.client.AuthData.TachyonAuthToken
routeInfo, ok := routes.Routes[actionType] routeInfo, ok := routes.Routes[actionType]
if !ok { if !ok {
@ -91,11 +91,11 @@ func (s *SessionHandler) buildMessage(actionType binary.ActionType, encryptedDat
tmpMessage := payload.NewSendMessageBuilder(token, pairedDevice, requestID, sessionId).SetRoute(routeInfo.Action).SetSessionId(s.sessionID) tmpMessage := payload.NewSendMessageBuilder(token, pairedDevice, requestID, sessionId).SetRoute(routeInfo.Action).SetSessionId(s.sessionID)
if encryptedData != nil { if encryptedData != nil {
tmpMessage.SetEncryptedProtoMessage(encryptedData, s.client.authData.RequestCrypto) tmpMessage.SetEncryptedProtoMessage(encryptedData, s.client.AuthData.RequestCrypto)
} }
if routeInfo.UseTTL { if routeInfo.UseTTL {
tmpMessage.SetTTL(s.client.authData.TachyonTTL) tmpMessage.SetTTL(s.client.AuthData.TachyonTTL)
} }
message, buildErr := tmpMessage.Build() message, buildErr := tmpMessage.Build()
@ -142,14 +142,14 @@ func (s *SessionHandler) sendAckRequest() {
for i, reqID := range dataToAck { for i, reqID := range dataToAck {
ackMessages[i] = &binary.AckMessageData{ ackMessages[i] = &binary.AckMessageData{
RequestID: reqID, RequestID: reqID,
Device: s.client.authData.Browser, Device: s.client.AuthData.Browser,
} }
} }
ackMessagePayload := &binary.AckMessagePayload{ ackMessagePayload := &binary.AckMessagePayload{
AuthData: &binary.AuthMessage{ AuthData: &binary.AuthMessage{
RequestID: uuid.NewString(), RequestID: uuid.NewString(),
TachyonAuthToken: s.client.authData.TachyonAuthToken, TachyonAuthToken: s.client.AuthData.TachyonAuthToken,
ConfigVersion: payload.ConfigMessage, ConfigVersion: util.ConfigMessage,
}, },
EmptyArr: &binary.EmptyArr{}, EmptyArr: &binary.EmptyArr{},
Acks: ackMessages, Acks: ackMessages,

View file

@ -1,8 +1,7 @@
package payload package util
import ( import (
"go.mau.fi/mautrix-gmessages/libgm/binary" "go.mau.fi/mautrix-gmessages/libgm/binary"
"go.mau.fi/mautrix-gmessages/libgm/util"
) )
var ConfigMessage = &binary.ConfigVersion{ var ConfigMessage = &binary.ConfigVersion{
@ -14,7 +13,7 @@ var ConfigMessage = &binary.ConfigVersion{
} }
var Network = "Bugle" var Network = "Bugle"
var BrowserDetailsMessage = &binary.BrowserDetails{ var BrowserDetailsMessage = &binary.BrowserDetails{
UserAgent: util.UserAgent, UserAgent: UserAgent,
BrowserType: binary.BrowserTypes_OTHER, BrowserType: binary.BrowserTypes_OTHER,
Os: "libgm", Os: "libgm",
SomeBool: true, SomeBool: true,

View file

@ -29,7 +29,7 @@ import (
"go.mau.fi/mautrix-gmessages/config" "go.mau.fi/mautrix-gmessages/config"
"go.mau.fi/mautrix-gmessages/database" "go.mau.fi/mautrix-gmessages/database"
"go.mau.fi/mautrix-gmessages/libgm/binary" "go.mau.fi/mautrix-gmessages/libgm/binary"
"go.mau.fi/mautrix-gmessages/libgm/payload" "go.mau.fi/mautrix-gmessages/libgm/util"
) )
// Information to find out exactly which commit the bridge was built from. // Information to find out exactly which commit the bridge was built from.
@ -67,12 +67,12 @@ func (br *GMBridge) Init() {
br.CommandProcessor = commands.NewProcessor(&br.Bridge) br.CommandProcessor = commands.NewProcessor(&br.Bridge)
br.RegisterCommands() br.RegisterCommands()
payload.BrowserDetailsMessage.Os = br.Config.GoogleMessages.OS util.BrowserDetailsMessage.Os = br.Config.GoogleMessages.OS
browserVal, ok := binary.BrowserTypes_value[br.Config.GoogleMessages.Browser] browserVal, ok := binary.BrowserTypes_value[br.Config.GoogleMessages.Browser]
if !ok { if !ok {
br.ZLog.Error().Str("browser_value", br.Config.GoogleMessages.Browser).Msg("Invalid browser value") br.ZLog.Error().Str("browser_value", br.Config.GoogleMessages.Browser).Msg("Invalid browser value")
} else { } else {
payload.BrowserDetailsMessage.BrowserType = binary.BrowserTypes(browserVal) util.BrowserDetailsMessage.BrowserType = binary.BrowserTypes(browserVal)
} }
Segment.log = br.ZLog.With().Str("component", "segment").Logger() Segment.log = br.ZLog.With().Str("component", "segment").Logger()

83
user.go
View file

@ -71,10 +71,9 @@ type User struct {
batteryLow bool batteryLow bool
mobileData bool mobileData bool
DoublePuppetIntent *appservice.IntentAPI pairSuccessChan chan struct{}
hackyLoginCommand *WrappedCommandEvent DoublePuppetIntent *appservice.IntentAPI
hackyLoginCommandPrevEvent id.EventID
} }
func (br *GMBridge) getUserByMXID(userID id.UserID, onlyIfExists bool) *User { func (br *GMBridge) getUserByMXID(userID id.UserID, onlyIfExists bool) *User {
@ -389,12 +388,11 @@ func (user *User) SetManagementRoom(roomID id.RoomID) {
} }
var ErrAlreadyLoggedIn = errors.New("already logged in") var ErrAlreadyLoggedIn = errors.New("already logged in")
var ErrLoginInProgress = errors.New("login already in progress")
var ErrLoginTimeout = errors.New("login timed out")
func (user *User) createClient() { func (user *User) createClient(sess *libgm.AuthData) {
if user.Session == nil { user.Client = libgm.NewClient(sess, user.zlog.With().Str("component", "libgm").Logger())
user.Session = libgm.NewAuthData()
}
user.Client = libgm.NewClient(user.Session, user.zlog.With().Str("component", "libgm").Logger())
user.Client.SetEventHandler(user.syncHandleEvent) user.Client.SetEventHandler(user.syncHandleEvent)
} }
@ -402,20 +400,66 @@ func (user *User) syncHandleEvent(ev any) {
go user.HandleEvent(ev) go user.HandleEvent(ev)
} }
func (user *User) Login(ctx context.Context) (<-chan string, error) { type qrChannelItem struct {
success bool
qr string
err error
}
func (user *User) Login(ctx context.Context, maxAttempts int) (<-chan qrChannelItem, error) {
user.connLock.Lock() user.connLock.Lock()
defer user.connLock.Unlock() defer user.connLock.Unlock()
if user.Session != nil { if user.Session != nil {
return nil, ErrAlreadyLoggedIn return nil, ErrAlreadyLoggedIn
} else if user.Client != nil { } else if user.Client != nil {
user.unlockedDeleteConnection() user.unlockedDeleteConnection()
} else if user.pairSuccessChan != nil {
return nil, ErrLoginInProgress
} }
user.createClient() pairSuccessChan := make(chan struct{})
err := user.Client.Connect() user.pairSuccessChan = pairSuccessChan
user.createClient(libgm.NewAuthData())
qr, err := user.Client.StartLogin()
if err != nil { if err != nil {
user.DeleteConnection()
user.pairSuccessChan = nil
return nil, fmt.Errorf("failed to connect to Google Messages: %w", err) return nil, fmt.Errorf("failed to connect to Google Messages: %w", err)
} }
return nil, nil ch := make(chan qrChannelItem, maxAttempts+2)
ch <- qrChannelItem{qr: qr}
go func() {
ticker := time.NewTicker(30 * time.Second)
success := false
defer func() {
ticker.Stop()
if !success {
user.zlog.Debug().Msg("Deleting connection as login wasn't successful")
user.DeleteConnection()
}
user.pairSuccessChan = nil
close(ch)
}()
for ; maxAttempts > 0; maxAttempts-- {
select {
case <-ctx.Done():
user.zlog.Debug().Err(ctx.Err()).Msg("Login context cancelled")
return
case <-ticker.C:
qr, err := user.Client.RefreshPhoneRelay()
if err != nil {
ch <- qrChannelItem{err: fmt.Errorf("failed to refresh QR code: %w", err)}
return
}
ch <- qrChannelItem{qr: qr}
case <-pairSuccessChan:
ch <- qrChannelItem{success: true}
success = true
return
}
}
ch <- qrChannelItem{err: ErrLoginTimeout}
}()
return ch, nil
} }
func (user *User) Connect() bool { func (user *User) Connect() bool {
@ -431,7 +475,7 @@ func (user *User) Connect() bool {
} }
user.zlog.Debug().Msg("Connecting to Google Messages") user.zlog.Debug().Msg("Connecting to Google Messages")
user.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnecting, Error: GMConnecting}) user.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnecting, Error: GMConnecting})
user.createClient() user.createClient(user.Session)
err := user.Client.Connect() err := user.Client.Connect()
if err != nil { if err != nil {
user.zlog.Err(err).Msg("Error connecting to Google Messages") user.zlog.Err(err).Msg("Error connecting to Google Messages")
@ -512,11 +556,6 @@ func (user *User) sendMarkdownBridgeAlert(formatString string, args ...interface
func (user *User) HandleEvent(event interface{}) { func (user *User) HandleEvent(event interface{}) {
switch v := event.(type) { switch v := event.(type) {
case *events.QR:
// This shouldn't be here
if user.hackyLoginCommand != nil {
user.hackyLoginCommandPrevEvent = user.sendQR(user.hackyLoginCommand, v.URL, user.hackyLoginCommandPrevEvent)
}
case *events.ListenFatalError: case *events.ListenFatalError:
user.Logout(status.BridgeState{ user.Logout(status.BridgeState{
StateEvent: status.StateBadCredentials, StateEvent: status.StateBadCredentials,
@ -534,15 +573,17 @@ func (user *User) HandleEvent(event interface{}) {
user.longPollingError = nil user.longPollingError = nil
user.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnected}) user.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnected})
case *events.PairSuccessful: case *events.PairSuccessful:
user.hackyLoginCommand = nil user.Session = user.Client.AuthData
user.hackyLoginCommandPrevEvent = ""
user.tryAutomaticDoublePuppeting()
user.Phone = v.GetMobile().GetSourceID() user.Phone = v.GetMobile().GetSourceID()
user.tryAutomaticDoublePuppeting()
user.addToPhoneMap() user.addToPhoneMap()
err := user.Update(context.TODO()) err := user.Update(context.TODO())
if err != nil { if err != nil {
user.zlog.Err(err).Msg("Failed to update session in database") user.zlog.Err(err).Msg("Failed to update session in database")
} }
if ch := user.pairSuccessChan; ch != nil {
close(ch)
}
case *binary.RevokePairData: case *binary.RevokePairData:
user.zlog.Info().Any("revoked_device", v.GetRevokedDevice()).Msg("Got pair revoked event") user.zlog.Info().Any("revoked_device", v.GetRevokedDevice()).Msg("Got pair revoked event")
user.Logout(status.BridgeState{ user.Logout(status.BridgeState{