2023-12-17 15:54:35 +02:00
|
|
|
// mautrix-signal - A Matrix-signal puppeting bridge.
|
|
|
|
|
// Copyright (C) 2023 Scott Weber
|
2025-05-12 14:27:16 +03:00
|
|
|
// Copyright (C) 2025 Tulir Asokan
|
2023-12-17 15:54:35 +02:00
|
|
|
//
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
2023-05-31 16:39:09 -04:00
|
|
|
package signalmeow
|
|
|
|
|
|
|
|
|
|
import (
|
2024-11-18 15:18:44 +02:00
|
|
|
"bytes"
|
2023-05-31 16:39:09 -04:00
|
|
|
"context"
|
|
|
|
|
"encoding/base64"
|
2025-05-12 16:39:54 +03:00
|
|
|
"errors"
|
2023-05-31 16:39:09 -04:00
|
|
|
"fmt"
|
2024-01-02 21:35:42 -07:00
|
|
|
"net/http"
|
2023-07-18 23:45:42 -04:00
|
|
|
"strings"
|
2025-05-12 18:37:19 +03:00
|
|
|
"time"
|
2023-05-31 16:39:09 -04:00
|
|
|
|
|
|
|
|
"github.com/google/uuid"
|
2024-01-06 10:01:15 -07:00
|
|
|
"github.com/rs/zerolog"
|
2025-12-11 17:53:48 +02:00
|
|
|
"go.mau.fi/util/ptr"
|
2023-12-17 15:21:21 +02:00
|
|
|
"google.golang.org/protobuf/proto"
|
|
|
|
|
|
2023-05-31 16:39:09 -04:00
|
|
|
"go.mau.fi/mautrix-signal/pkg/libsignalgo"
|
2024-01-02 18:58:40 +02:00
|
|
|
"go.mau.fi/mautrix-signal/pkg/signalmeow/events"
|
2023-05-31 16:39:09 -04:00
|
|
|
signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
|
2024-01-02 18:58:40 +02:00
|
|
|
"go.mau.fi/mautrix-signal/pkg/signalmeow/types"
|
2023-05-31 16:39:09 -04:00
|
|
|
"go.mau.fi/mautrix-signal/pkg/signalmeow/web"
|
|
|
|
|
)
|
|
|
|
|
|
2023-08-22 11:54:35 -04:00
|
|
|
type SignalConnectionEvent int
|
|
|
|
|
|
|
|
|
|
const (
|
2023-08-24 15:06:39 -04:00
|
|
|
SignalConnectionEventNone SignalConnectionEvent = iota
|
|
|
|
|
SignalConnectionEventConnected
|
2023-08-22 11:54:35 -04:00
|
|
|
SignalConnectionEventDisconnected
|
2023-08-24 15:06:39 -04:00
|
|
|
SignalConnectionEventLoggedOut
|
2023-08-22 11:54:35 -04:00
|
|
|
SignalConnectionEventError
|
2025-10-20 11:52:14 +03:00
|
|
|
SignalConnectionEventFatalError
|
2023-09-27 14:25:02 -04:00
|
|
|
SignalConnectionCleanShutdown
|
2023-08-22 11:54:35 -04:00
|
|
|
)
|
|
|
|
|
|
2023-11-03 16:55:02 -04:00
|
|
|
// mapping from SignalConnectionEvent to its string representation
|
|
|
|
|
var signalConnectionEventNames = map[SignalConnectionEvent]string{
|
|
|
|
|
SignalConnectionEventNone: "SignalConnectionEventNone",
|
|
|
|
|
SignalConnectionEventConnected: "SignalConnectionEventConnected",
|
|
|
|
|
SignalConnectionEventDisconnected: "SignalConnectionEventDisconnected",
|
|
|
|
|
SignalConnectionEventLoggedOut: "SignalConnectionEventLoggedOut",
|
|
|
|
|
SignalConnectionEventError: "SignalConnectionEventError",
|
2025-10-20 11:52:14 +03:00
|
|
|
SignalConnectionEventFatalError: "SignalConnectionEventFatalError",
|
2023-11-03 16:55:02 -04:00
|
|
|
SignalConnectionCleanShutdown: "SignalConnectionCleanShutdown",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Implement the fmt.Stringer interface
|
|
|
|
|
func (s SignalConnectionEvent) String() string {
|
|
|
|
|
return signalConnectionEventNames[s]
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-22 11:54:35 -04:00
|
|
|
type SignalConnectionStatus struct {
|
|
|
|
|
Event SignalConnectionEvent
|
|
|
|
|
Err error
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 18:43:57 +02:00
|
|
|
func (cli *Client) StartWebsockets(ctx context.Context) (authChan, unauthChan chan web.SignalWebsocketConnectionStatus, err error) {
|
|
|
|
|
authChan, unauthChan, _, _, err = cli.startWebsocketsInternal(ctx)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (cli *Client) startWebsocketsInternal(
|
|
|
|
|
ctx context.Context,
|
|
|
|
|
) (
|
|
|
|
|
authChan, unauthChan chan web.SignalWebsocketConnectionStatus,
|
2025-02-21 11:57:01 +00:00
|
|
|
loopCtx context.Context, loopCancel context.CancelFunc,
|
2025-02-12 18:43:57 +02:00
|
|
|
err error,
|
|
|
|
|
) {
|
2025-02-21 11:57:01 +00:00
|
|
|
loopCtx, loopCancel = context.WithCancel(ctx)
|
|
|
|
|
unauthChan, err = cli.connectUnauthedWS(loopCtx)
|
2025-01-16 16:29:51 +02:00
|
|
|
if err != nil {
|
2025-02-21 11:57:01 +00:00
|
|
|
loopCancel()
|
2025-02-12 18:43:57 +02:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
zerolog.Ctx(ctx).Info().Msg("Unauthed websocket connecting")
|
2025-02-21 11:57:01 +00:00
|
|
|
authChan, err = cli.connectAuthedWS(loopCtx, cli.incomingRequestHandler)
|
2025-02-12 18:43:57 +02:00
|
|
|
if err != nil {
|
2025-02-21 11:57:01 +00:00
|
|
|
loopCancel()
|
2025-02-12 18:43:57 +02:00
|
|
|
return
|
2025-01-16 16:29:51 +02:00
|
|
|
}
|
|
|
|
|
zerolog.Ctx(ctx).Info().Msg("Authed websocket connecting")
|
2025-02-21 11:57:01 +00:00
|
|
|
cli.loopCancel = loopCancel
|
2025-02-12 18:43:57 +02:00
|
|
|
return
|
2025-01-16 16:29:51 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-05 13:44:41 +02:00
|
|
|
func (cli *Client) StartReceiveLoops(ctx context.Context) (chan SignalConnectionStatus, error) {
|
2024-01-06 10:01:15 -07:00
|
|
|
log := zerolog.Ctx(ctx).With().Str("action", "start receive loops").Logger()
|
2025-05-12 18:37:19 +03:00
|
|
|
cbc := make(chan time.Time, 1)
|
|
|
|
|
cli.writeCallbackCounter = cbc
|
2025-02-21 11:57:01 +00:00
|
|
|
|
|
|
|
|
authChan, unauthChan, loopCtx, loopCancel, err := cli.startWebsocketsInternal(log.WithContext(ctx))
|
2023-06-26 10:43:33 -04:00
|
|
|
if err != nil {
|
2023-08-22 11:54:35 -04:00
|
|
|
return nil, err
|
2023-06-26 10:43:33 -04:00
|
|
|
}
|
2025-03-13 14:54:29 +02:00
|
|
|
statusChan := make(chan SignalConnectionStatus, 128)
|
2023-08-24 15:06:39 -04:00
|
|
|
|
2023-09-19 19:04:01 -04:00
|
|
|
initialConnectChan := make(chan struct{})
|
2025-05-12 18:55:32 +03:00
|
|
|
resetWriteCount := make(chan struct{}, 1)
|
2023-09-19 19:04:01 -04:00
|
|
|
|
2023-08-24 15:06:39 -04:00
|
|
|
// Combine both websocket status channels into a single, more generic "Signal" connection status channel
|
2025-05-12 18:37:19 +03:00
|
|
|
cli.loopWg.Add(2)
|
|
|
|
|
go func() {
|
|
|
|
|
defer cli.loopWg.Done()
|
|
|
|
|
writeCallbackTimer := time.Now()
|
|
|
|
|
callbackCount := 0
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case <-loopCtx.Done():
|
|
|
|
|
return
|
2025-05-12 18:55:32 +03:00
|
|
|
case <-resetWriteCount:
|
|
|
|
|
callbackCount = 0
|
2025-05-12 18:37:19 +03:00
|
|
|
case nextTS := <-cbc:
|
|
|
|
|
if callbackCount >= 4 && time.Since(writeCallbackTimer) > 1*time.Minute {
|
|
|
|
|
err := cli.Store.EventBuffer.DeleteBufferedEventsOlderThan(ctx, writeCallbackTimer)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Err(err).Msg("Failed to delete old buffered event hashes")
|
|
|
|
|
}
|
|
|
|
|
writeCallbackTimer = nextTS
|
|
|
|
|
} else {
|
|
|
|
|
callbackCount++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}()
|
2023-08-22 11:54:35 -04:00
|
|
|
go func() {
|
2025-02-21 11:57:01 +00:00
|
|
|
defer cli.loopWg.Done()
|
2023-08-22 11:54:35 -04:00
|
|
|
defer close(statusChan)
|
2025-02-21 11:57:01 +00:00
|
|
|
defer loopCancel()
|
2023-08-22 11:54:35 -04:00
|
|
|
var currentStatus, lastAuthStatus, lastUnauthStatus web.SignalWebsocketConnectionStatus
|
|
|
|
|
for {
|
|
|
|
|
select {
|
2025-02-21 11:57:01 +00:00
|
|
|
case <-loopCtx.Done():
|
2024-01-06 10:01:15 -07:00
|
|
|
log.Info().Msg("Context done, exiting websocket status loop")
|
2023-08-22 11:54:35 -04:00
|
|
|
return
|
|
|
|
|
case status := <-authChan:
|
|
|
|
|
lastAuthStatus = status
|
|
|
|
|
currentStatus = status
|
|
|
|
|
|
2024-01-02 18:58:40 +02:00
|
|
|
switch status.Event {
|
|
|
|
|
case web.SignalWebsocketConnectionEventConnecting:
|
|
|
|
|
// do nothing?
|
|
|
|
|
case web.SignalWebsocketConnectionEventConnected:
|
2024-01-06 10:01:15 -07:00
|
|
|
log.Info().Msg("Authed websocket connected")
|
2024-01-02 18:58:40 +02:00
|
|
|
case web.SignalWebsocketConnectionEventDisconnected:
|
2024-01-06 10:01:15 -07:00
|
|
|
log.Err(status.Err).Msg("Authed websocket disconnected")
|
2024-01-02 18:58:40 +02:00
|
|
|
case web.SignalWebsocketConnectionEventLoggedOut:
|
2024-01-06 10:01:15 -07:00
|
|
|
log.Err(status.Err).Msg("Authed websocket logged out")
|
2023-08-24 15:06:39 -04:00
|
|
|
// TODO: Also make sure unauthed websocket is disconnected
|
|
|
|
|
//StopReceiveLoops(d)
|
2024-01-02 18:58:40 +02:00
|
|
|
case web.SignalWebsocketConnectionEventError:
|
2024-01-06 10:01:15 -07:00
|
|
|
log.Err(status.Err).Msg("Authed websocket error")
|
2025-10-20 11:52:14 +03:00
|
|
|
case web.SignalWebsocketConnectionEventFatalError:
|
|
|
|
|
log.Err(status.Err).Msg("Authed websocket fatal error")
|
2024-01-02 18:58:40 +02:00
|
|
|
case web.SignalWebsocketConnectionEventCleanShutdown:
|
2024-01-06 10:01:15 -07:00
|
|
|
log.Info().Msg("Authed websocket clean shutdown")
|
2023-08-22 11:54:35 -04:00
|
|
|
}
|
2025-05-12 18:55:32 +03:00
|
|
|
if status.Event != web.SignalWebsocketConnectionEventConnected {
|
|
|
|
|
select {
|
|
|
|
|
case resetWriteCount <- struct{}{}:
|
|
|
|
|
default:
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-08-22 11:54:35 -04:00
|
|
|
case status := <-unauthChan:
|
|
|
|
|
lastUnauthStatus = status
|
|
|
|
|
currentStatus = status
|
|
|
|
|
|
2024-01-02 18:58:40 +02:00
|
|
|
switch status.Event {
|
|
|
|
|
case web.SignalWebsocketConnectionEventConnecting:
|
|
|
|
|
// do nothing?
|
|
|
|
|
case web.SignalWebsocketConnectionEventConnected:
|
2024-01-06 10:01:15 -07:00
|
|
|
log.Info().
|
|
|
|
|
Any("last_unauth_status", lastUnauthStatus).
|
|
|
|
|
Any("last_auth_status", lastAuthStatus).
|
|
|
|
|
Any("current_status", currentStatus).
|
|
|
|
|
Msg("Unauthed websocket connected")
|
2024-01-02 18:58:40 +02:00
|
|
|
case web.SignalWebsocketConnectionEventDisconnected:
|
2024-01-06 10:01:15 -07:00
|
|
|
log.Err(status.Err).Msg("Unauthed websocket disconnected")
|
2024-01-02 18:58:40 +02:00
|
|
|
case web.SignalWebsocketConnectionEventLoggedOut:
|
2024-01-06 10:01:15 -07:00
|
|
|
log.Err(status.Err).Msg("Unauthed websocket logged out ** THIS SHOULD BE IMPOSSIBLE **")
|
2024-01-02 18:58:40 +02:00
|
|
|
case web.SignalWebsocketConnectionEventError:
|
2024-01-06 10:01:15 -07:00
|
|
|
log.Err(status.Err).Msg("Unauthed websocket error")
|
2025-10-20 11:52:14 +03:00
|
|
|
case web.SignalWebsocketConnectionEventFatalError:
|
|
|
|
|
log.Err(status.Err).Msg("Unauthed websocket fatal error")
|
2024-01-02 18:58:40 +02:00
|
|
|
case web.SignalWebsocketConnectionEventCleanShutdown:
|
2024-01-06 10:01:15 -07:00
|
|
|
log.Info().Msg("Unauthed websocket clean shutdown")
|
2023-08-22 11:54:35 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-24 15:06:39 -04:00
|
|
|
var statusToSend SignalConnectionStatus
|
2023-08-22 11:54:35 -04:00
|
|
|
if lastAuthStatus.Event == web.SignalWebsocketConnectionEventConnected && lastUnauthStatus.Event == web.SignalWebsocketConnectionEventConnected {
|
2023-08-24 15:06:39 -04:00
|
|
|
statusToSend = SignalConnectionStatus{
|
2023-08-22 11:54:35 -04:00
|
|
|
Event: SignalConnectionEventConnected,
|
|
|
|
|
}
|
2023-09-19 19:04:01 -04:00
|
|
|
if initialConnectChan != nil {
|
|
|
|
|
close(initialConnectChan)
|
|
|
|
|
initialConnectChan = nil
|
|
|
|
|
}
|
2023-08-22 11:54:35 -04:00
|
|
|
} else if currentStatus.Event == web.SignalWebsocketConnectionEventDisconnected {
|
2023-08-24 15:06:39 -04:00
|
|
|
statusToSend = SignalConnectionStatus{
|
2023-08-22 11:54:35 -04:00
|
|
|
Event: SignalConnectionEventDisconnected,
|
|
|
|
|
Err: currentStatus.Err,
|
|
|
|
|
}
|
2023-08-24 15:06:39 -04:00
|
|
|
} else if currentStatus.Event == web.SignalWebsocketConnectionEventLoggedOut {
|
|
|
|
|
statusToSend = SignalConnectionStatus{
|
|
|
|
|
Event: SignalConnectionEventLoggedOut,
|
|
|
|
|
Err: currentStatus.Err,
|
|
|
|
|
}
|
2023-08-22 11:54:35 -04:00
|
|
|
} else if currentStatus.Event == web.SignalWebsocketConnectionEventError {
|
2023-08-24 15:06:39 -04:00
|
|
|
statusToSend = SignalConnectionStatus{
|
2023-08-22 11:54:35 -04:00
|
|
|
Event: SignalConnectionEventError,
|
|
|
|
|
Err: currentStatus.Err,
|
|
|
|
|
}
|
2025-10-20 11:52:14 +03:00
|
|
|
} else if currentStatus.Event == web.SignalWebsocketConnectionEventFatalError {
|
|
|
|
|
statusToSend = SignalConnectionStatus{
|
|
|
|
|
Event: SignalConnectionEventFatalError,
|
|
|
|
|
Err: currentStatus.Err,
|
|
|
|
|
}
|
2023-09-27 14:25:02 -04:00
|
|
|
} else if currentStatus.Event == web.SignalWebsocketConnectionEventCleanShutdown {
|
|
|
|
|
statusToSend = SignalConnectionStatus{
|
|
|
|
|
Event: SignalConnectionCleanShutdown,
|
|
|
|
|
}
|
2023-08-22 11:54:35 -04:00
|
|
|
}
|
2024-02-29 22:48:42 -05:00
|
|
|
if statusToSend.Event != 0 && statusToSend.Event != cli.lastConnectionStatus.Event {
|
2024-01-06 10:01:15 -07:00
|
|
|
log.Info().Any("status_to_send", statusToSend).Msg("Sending connection status")
|
2023-08-24 15:06:39 -04:00
|
|
|
statusChan <- statusToSend
|
2024-02-29 22:48:42 -05:00
|
|
|
cli.lastConnectionStatus = statusToSend
|
2023-08-24 15:06:39 -04:00
|
|
|
}
|
2023-08-22 11:54:35 -04:00
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
2023-09-19 19:04:01 -04:00
|
|
|
// Send sync message once both websockets are connected
|
2025-02-21 11:57:01 +00:00
|
|
|
cli.loopWg.Add(1)
|
2023-09-19 19:04:01 -04:00
|
|
|
go func() {
|
2025-02-21 11:57:01 +00:00
|
|
|
defer cli.loopWg.Done()
|
2025-11-24 17:43:32 +02:00
|
|
|
select {
|
|
|
|
|
case <-loopCtx.Done():
|
|
|
|
|
return
|
|
|
|
|
case <-initialConnectChan:
|
|
|
|
|
log.Info().Msg("Both websockets connected, sending contacts sync request")
|
|
|
|
|
err = cli.RegisterCapabilities(ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
zerolog.Ctx(ctx).Err(err).Msg("Failed to register capabilities")
|
|
|
|
|
} else {
|
|
|
|
|
zerolog.Ctx(ctx).Debug().Msg("Successfully registered capabilities")
|
|
|
|
|
}
|
|
|
|
|
// Start loop to check for and upload more prekeys
|
|
|
|
|
cli.loopWg.Add(1)
|
|
|
|
|
go func() {
|
|
|
|
|
defer cli.loopWg.Done()
|
|
|
|
|
cli.keyCheckLoop(loopCtx)
|
|
|
|
|
}()
|
|
|
|
|
// TODO hacky
|
|
|
|
|
if cli.SyncContactsOnConnect {
|
|
|
|
|
cli.SendContactSyncRequest(loopCtx)
|
|
|
|
|
}
|
|
|
|
|
if cli.Store.MasterKey == nil {
|
|
|
|
|
cli.SendStorageMasterKeyRequest(loopCtx)
|
2023-09-19 19:04:01 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
2023-08-22 11:54:35 -04:00
|
|
|
return statusChan, nil
|
2023-05-31 16:39:09 -04:00
|
|
|
}
|
|
|
|
|
|
2026-01-15 14:08:00 +02:00
|
|
|
func (cli *Client) ForceReconnect() {
|
|
|
|
|
cli.AuthedWS.ForceReconnect()
|
|
|
|
|
cli.UnauthedWS.ForceReconnect()
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-05 13:44:41 +02:00
|
|
|
func (cli *Client) StopReceiveLoops() error {
|
2023-08-24 15:06:39 -04:00
|
|
|
defer func() {
|
2024-01-05 13:44:41 +02:00
|
|
|
cli.AuthedWS = nil
|
|
|
|
|
cli.UnauthedWS = nil
|
2023-08-24 15:06:39 -04:00
|
|
|
}()
|
2024-01-05 13:44:41 +02:00
|
|
|
authErr := cli.AuthedWS.Close()
|
|
|
|
|
unauthErr := cli.UnauthedWS.Close()
|
2025-02-21 11:57:01 +00:00
|
|
|
if cli.loopCancel != nil {
|
|
|
|
|
cli.loopCancel()
|
|
|
|
|
cli.loopWg.Wait()
|
2023-12-30 21:01:37 +01:00
|
|
|
}
|
2023-08-24 15:06:39 -04:00
|
|
|
if authErr != nil {
|
|
|
|
|
return authErr
|
|
|
|
|
}
|
|
|
|
|
if unauthErr != nil {
|
|
|
|
|
return unauthErr
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-29 22:48:42 -05:00
|
|
|
func (cli *Client) LastConnectionStatus() SignalConnectionStatus {
|
|
|
|
|
return cli.lastConnectionStatus
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-05 13:44:41 +02:00
|
|
|
func (cli *Client) ClearKeysAndDisconnect(ctx context.Context) error {
|
|
|
|
|
// Essentially logout, clearing sessions and keys, and disconnecting websockets
|
|
|
|
|
// but don't clear ACI UUID or profile keys or contacts, or anything else that
|
|
|
|
|
// we can reuse if we reassociate with the same Signal account.
|
|
|
|
|
// To fully "logout" delete the device from the database.
|
|
|
|
|
clearErr := cli.Store.ClearDeviceKeys(ctx)
|
|
|
|
|
clearErr2 := cli.Store.ClearPassword(ctx)
|
|
|
|
|
stopLoopErr := cli.StopReceiveLoops()
|
|
|
|
|
|
|
|
|
|
if clearErr != nil {
|
|
|
|
|
return clearErr
|
|
|
|
|
}
|
|
|
|
|
if clearErr2 != nil {
|
|
|
|
|
return clearErr2
|
|
|
|
|
}
|
|
|
|
|
return stopLoopErr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (cli *Client) incomingRequestHandler(ctx context.Context, req *signalpb.WebSocketRequestMessage) (*web.SimpleResponse, error) {
|
2024-01-06 10:01:15 -07:00
|
|
|
log := zerolog.Ctx(ctx).With().
|
|
|
|
|
Str("handler", "incoming request handler").
|
|
|
|
|
Str("verb", *req.Verb).
|
|
|
|
|
Str("path", *req.Path).
|
2025-03-13 17:38:14 +02:00
|
|
|
Uint64("incoming_request_id", *req.Id).
|
2024-01-06 10:01:15 -07:00
|
|
|
Logger()
|
|
|
|
|
ctx = log.WithContext(ctx)
|
2024-01-02 21:35:42 -07:00
|
|
|
if *req.Verb == http.MethodPut && *req.Path == "/api/v1/message" {
|
2024-01-05 13:44:41 +02:00
|
|
|
return cli.incomingAPIMessageHandler(ctx, req)
|
2024-01-02 21:35:42 -07:00
|
|
|
} else if *req.Verb == http.MethodPut && *req.Path == "/api/v1/queue/empty" {
|
2025-02-26 16:47:51 +02:00
|
|
|
log.Debug().Msg("Received queue empty notice")
|
2025-01-16 16:29:51 +02:00
|
|
|
cli.handleEvent(&events.QueueEmpty{})
|
2023-12-22 13:49:38 +02:00
|
|
|
} else {
|
2024-01-06 10:01:15 -07:00
|
|
|
log.Warn().Any("req", req).Msg("Unknown websocket request message")
|
2023-12-22 13:49:38 +02:00
|
|
|
}
|
|
|
|
|
return &web.SimpleResponse{
|
|
|
|
|
Status: 200,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-05 13:44:41 +02:00
|
|
|
func (cli *Client) incomingAPIMessageHandler(ctx context.Context, req *signalpb.WebSocketRequestMessage) (*web.SimpleResponse, error) {
|
2025-06-09 21:13:54 +03:00
|
|
|
log := *zerolog.Ctx(ctx)
|
2023-12-22 13:49:38 +02:00
|
|
|
envelope := &signalpb.Envelope{}
|
|
|
|
|
err := proto.Unmarshal(req.Body, envelope)
|
|
|
|
|
if err != nil {
|
2024-01-06 10:01:15 -07:00
|
|
|
log.Err(err).Msg("Unmarshal error")
|
2023-12-22 13:49:38 +02:00
|
|
|
return nil, err
|
|
|
|
|
}
|
2025-06-09 21:13:54 +03:00
|
|
|
log = log.With().
|
|
|
|
|
Uint64("envelope_timestamp", envelope.GetTimestamp()).
|
2024-09-09 19:25:08 +03:00
|
|
|
Uint64("server_timestamp", envelope.GetServerTimestamp()).
|
2025-06-09 21:13:54 +03:00
|
|
|
Logger()
|
|
|
|
|
ctx = log.WithContext(ctx)
|
2026-02-23 15:11:56 +02:00
|
|
|
destinationServiceID, _ := ParseStringOrBinaryServiceID(envelope.GetDestinationServiceId(), envelope.GetDestinationServiceIdBinary())
|
|
|
|
|
sourceServiceID, _ := ParseStringOrBinaryServiceID(envelope.GetSourceServiceId(), envelope.GetSourceServiceIdBinary())
|
2025-06-09 21:13:54 +03:00
|
|
|
log.Debug().
|
2024-03-20 18:43:32 +02:00
|
|
|
Str("destination_service_id", envelope.GetDestinationServiceId()).
|
|
|
|
|
Str("source_service_id", envelope.GetSourceServiceId()).
|
2026-02-23 15:11:56 +02:00
|
|
|
Hex("destination_service_id_bytes", envelope.GetDestinationServiceIdBinary()).
|
|
|
|
|
Hex("source_service_id_bytes", envelope.GetSourceServiceIdBinary()).
|
2024-03-20 18:43:32 +02:00
|
|
|
Uint32("source_device_id", envelope.GetSourceDevice()).
|
|
|
|
|
Object("parsed_destination_service_id", destinationServiceID).
|
2026-02-23 15:11:56 +02:00
|
|
|
Object("parsed_source_service_id", sourceServiceID).
|
2024-03-20 18:43:32 +02:00
|
|
|
Int32("envelope_type_id", int32(envelope.GetType())).
|
|
|
|
|
Str("envelope_type", signalpb.Envelope_Type_name[int32(envelope.GetType())]).
|
|
|
|
|
Msg("Received envelope")
|
2023-12-22 13:49:38 +02:00
|
|
|
|
2026-02-23 15:11:56 +02:00
|
|
|
result := cli.decryptEnvelope(ctx, envelope, sourceServiceID, destinationServiceID)
|
2023-07-09 09:39:45 -04:00
|
|
|
|
2024-09-09 20:55:43 +03:00
|
|
|
err = cli.handleDecryptedResult(ctx, result, envelope, destinationServiceID)
|
2024-04-11 14:31:27 -04:00
|
|
|
if err != nil {
|
2024-09-09 20:55:43 +03:00
|
|
|
log.Err(err).Msg("Error handling decrypted result")
|
2024-04-11 14:31:27 -04:00
|
|
|
return nil, err
|
|
|
|
|
}
|
2023-12-22 13:49:38 +02:00
|
|
|
|
2024-04-11 14:31:27 -04:00
|
|
|
return &web.SimpleResponse{
|
2025-05-12 18:37:19 +03:00
|
|
|
Status: 200,
|
|
|
|
|
WriteCallback: cli.writeCallback,
|
2024-04-11 14:31:27 -04:00
|
|
|
}, nil
|
|
|
|
|
}
|
2023-12-22 13:49:38 +02:00
|
|
|
|
2025-05-12 18:37:19 +03:00
|
|
|
func (cli *Client) writeCallback(preWriteTime time.Time) {
|
|
|
|
|
ch := cli.writeCallbackCounter
|
|
|
|
|
if ch != nil {
|
|
|
|
|
select {
|
|
|
|
|
case ch <- preWriteTime:
|
|
|
|
|
default:
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-17 22:24:34 +05:30
|
|
|
var ErrHandlerFailed = errors.New("event handler returned non-success status")
|
|
|
|
|
|
2024-04-11 14:31:27 -04:00
|
|
|
// TODO: we should split this up into multiple functions
|
|
|
|
|
func (cli *Client) handleDecryptedResult(
|
|
|
|
|
ctx context.Context,
|
|
|
|
|
result DecryptionResult,
|
2024-09-09 20:55:43 +03:00
|
|
|
envelope *signalpb.Envelope,
|
2024-04-11 14:31:27 -04:00
|
|
|
destinationServiceID libsignalgo.ServiceID,
|
2025-12-08 23:11:45 +02:00
|
|
|
) (retErr error) {
|
2025-07-08 19:12:33 +03:00
|
|
|
if errors.Is(result.Err, context.Canceled) {
|
|
|
|
|
return result.Err
|
|
|
|
|
} else if ctx.Err() != nil {
|
|
|
|
|
return ctx.Err()
|
|
|
|
|
}
|
2024-04-11 14:31:27 -04:00
|
|
|
log := zerolog.Ctx(ctx)
|
2025-05-12 16:39:54 +03:00
|
|
|
if result.CiphertextHash != nil {
|
|
|
|
|
defer func() {
|
|
|
|
|
err := cli.Store.EventBuffer.ClearBufferedEventPlaintext(ctx, *result.CiphertextHash)
|
|
|
|
|
if err != nil {
|
2025-05-12 19:10:19 +03:00
|
|
|
log.Err(err).
|
|
|
|
|
Hex("ciphertext_hash", result.CiphertextHash[:]).
|
|
|
|
|
Msg("Failed to clear buffered event plaintext")
|
|
|
|
|
} else {
|
|
|
|
|
log.Debug().
|
|
|
|
|
Hex("ciphertext_hash", result.CiphertextHash[:]).
|
|
|
|
|
Msg("Deleted event plaintext from buffer")
|
2025-05-12 16:39:54 +03:00
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
}
|
2024-04-11 14:31:27 -04:00
|
|
|
|
2025-11-20 17:15:32 +02:00
|
|
|
var theirServiceID libsignalgo.ServiceID
|
|
|
|
|
var err error
|
|
|
|
|
if result.SenderAddress == nil {
|
|
|
|
|
log.Err(result.Err).
|
|
|
|
|
Bool("urgent", envelope.GetUrgent()).
|
|
|
|
|
Stringer("content_hint", result.ContentHint).
|
|
|
|
|
Uint64("server_ts", envelope.GetServerTimestamp()).
|
|
|
|
|
Uint64("client_ts", envelope.GetTimestamp()).
|
|
|
|
|
Msg("No sender address received")
|
|
|
|
|
return nil
|
|
|
|
|
} else if theirServiceID, err = result.SenderAddress.NameServiceID(); err != nil {
|
2025-11-20 13:16:20 +02:00
|
|
|
log.Warn().
|
|
|
|
|
Uint64("server_ts", envelope.GetServerTimestamp()).
|
|
|
|
|
Uint64("client_ts", envelope.GetTimestamp()).
|
|
|
|
|
Msg("Failed to get sender name as service ID")
|
|
|
|
|
return fmt.Errorf("failed to get sender name as service ID: %w", err)
|
|
|
|
|
} else if theirServiceID.Type != libsignalgo.ServiceIDTypeACI {
|
|
|
|
|
log.Warn().
|
|
|
|
|
Any("their_service_id", theirServiceID).
|
|
|
|
|
Uint64("server_ts", envelope.GetServerTimestamp()).
|
|
|
|
|
Uint64("client_ts", envelope.GetTimestamp()).
|
|
|
|
|
Msg("Dropping message from non-ACI sender")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2025-11-28 16:51:51 +02:00
|
|
|
cli.Store.RecipientStore.MarkUnregistered(ctx, theirServiceID, false)
|
2025-11-20 13:16:20 +02:00
|
|
|
|
2025-06-17 22:24:34 +05:30
|
|
|
handlerSuccess := true
|
2025-12-08 23:11:45 +02:00
|
|
|
defer func() {
|
|
|
|
|
if retErr == nil && !handlerSuccess {
|
|
|
|
|
retErr = ErrHandlerFailed
|
|
|
|
|
}
|
|
|
|
|
}()
|
2024-04-11 14:31:27 -04:00
|
|
|
// result.Err is set if there was an error during decryption and we
|
|
|
|
|
// should notifiy the user that the message could not be decrypted
|
|
|
|
|
if result.Err != nil {
|
2025-05-26 15:33:49 +03:00
|
|
|
if errors.Is(result.Err, EventAlreadyProcessed) {
|
|
|
|
|
log.Debug().Err(result.Err).
|
|
|
|
|
Bool("urgent", envelope.GetUrgent()).
|
|
|
|
|
Stringer("content_hint", result.ContentHint).
|
|
|
|
|
Uint64("server_ts", envelope.GetServerTimestamp()).
|
|
|
|
|
Uint64("client_ts", envelope.GetTimestamp()).
|
|
|
|
|
Stringer("sender", theirServiceID).
|
|
|
|
|
Msg("Ignoring already processed event")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2025-11-20 17:15:32 +02:00
|
|
|
log.Err(result.Err).
|
|
|
|
|
Bool("urgent", envelope.GetUrgent()).
|
|
|
|
|
Stringer("content_hint", result.ContentHint).
|
|
|
|
|
Uint64("server_ts", envelope.GetServerTimestamp()).
|
|
|
|
|
Uint64("client_ts", envelope.GetTimestamp()).
|
|
|
|
|
Stringer("sender", theirServiceID).
|
|
|
|
|
Msg("Decryption error with known sender")
|
2024-04-11 14:31:27 -04:00
|
|
|
// Only send decryption error event if the message was urgent,
|
|
|
|
|
// to prevent spamming errors for typing notifications and whatnot
|
2024-09-24 17:19:17 +03:00
|
|
|
if envelope.GetUrgent() &&
|
|
|
|
|
result.ContentHint != signalpb.UnidentifiedSenderMessage_Message_IMPLICIT &&
|
2025-05-26 15:33:49 +03:00
|
|
|
!strings.Contains(result.Err.Error(), "message with old counter") {
|
2025-06-17 22:24:34 +05:30
|
|
|
handlerSuccess = cli.handleEvent(&events.DecryptionError{
|
2024-09-09 19:25:08 +03:00
|
|
|
Sender: theirServiceID.UUID,
|
|
|
|
|
Err: result.Err,
|
2024-09-09 20:55:43 +03:00
|
|
|
Timestamp: envelope.GetTimestamp(),
|
2024-03-25 15:18:25 +02:00
|
|
|
})
|
2024-04-11 14:31:27 -04:00
|
|
|
}
|
2025-12-08 23:11:45 +02:00
|
|
|
if result.Retriable {
|
|
|
|
|
go func() {
|
|
|
|
|
err := cli.sendRetryRequest(ctx, result, envelope.GetTimestamp())
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Err(err).Msg("Failed to send retry request in background")
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
}
|
2025-06-17 22:24:34 +05:30
|
|
|
if !handlerSuccess {
|
|
|
|
|
return ErrHandlerFailed
|
|
|
|
|
}
|
|
|
|
|
return nil
|
2024-04-11 14:31:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
content := result.Content
|
2024-09-09 20:55:43 +03:00
|
|
|
if content == nil {
|
|
|
|
|
log.Warn().Msg("Decrypted content is nil")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2024-04-11 14:31:27 -04:00
|
|
|
|
2025-11-20 13:16:20 +02:00
|
|
|
deviceID, _ := result.SenderAddress.DeviceID()
|
2025-12-08 23:11:45 +02:00
|
|
|
log.Trace().
|
|
|
|
|
Any("raw_data", content).
|
|
|
|
|
Stringer("sender", theirServiceID).
|
|
|
|
|
Uint("sender_device", deviceID).
|
|
|
|
|
Msg("Raw event data")
|
2024-04-11 14:31:27 -04:00
|
|
|
newLog := log.With().
|
2025-11-20 13:16:20 +02:00
|
|
|
Stringer("sender_name", theirServiceID).
|
|
|
|
|
Uint("sender_device_id", deviceID).
|
2024-04-11 14:31:27 -04:00
|
|
|
Str("destination_service_id", destinationServiceID.String()).
|
|
|
|
|
Logger()
|
|
|
|
|
log = &newLog
|
|
|
|
|
ctx = log.WithContext(ctx)
|
2025-06-09 21:13:54 +03:00
|
|
|
logEvt := log.Debug()
|
|
|
|
|
if result.CiphertextHash != nil {
|
|
|
|
|
logEvt = logEvt.Hex("ciphertext_hash", result.CiphertextHash[:])
|
|
|
|
|
}
|
2025-12-08 23:11:45 +02:00
|
|
|
logEvt.Bool("unencrypted", result.Unencrypted).Msg("Decrypted message")
|
|
|
|
|
|
|
|
|
|
if content.DecryptionErrorMessage != nil {
|
|
|
|
|
handlerSuccess = true
|
|
|
|
|
dem, err := libsignalgo.DeserializeDecryptionErrorMessage(content.DecryptionErrorMessage)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Warn().Err(err).Msg("Failed to unmarshal decryption error message")
|
|
|
|
|
} else {
|
|
|
|
|
go func() {
|
|
|
|
|
err := cli.handleRetryRequest(ctx, result, dem)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Err(err).Msg("Failed to handle decryption error message in background")
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
} else if result.Unencrypted {
|
|
|
|
|
log.Warn().Msg("Unexpected non-decryption-error content in unencrypted message")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2024-04-11 14:31:27 -04:00
|
|
|
|
|
|
|
|
// If there's a sender key distribution message, process it
|
|
|
|
|
if content.GetSenderKeyDistributionMessage() != nil {
|
|
|
|
|
log.Debug().Msg("content includes sender key distribution message")
|
|
|
|
|
skdm, err := libsignalgo.DeserializeSenderKeyDistributionMessage(content.GetSenderKeyDistributionMessage())
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Err(err).Msg("DeserializeSenderKeyDistributionMessage error")
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
err = libsignalgo.ProcessSenderKeyDistributionMessage(
|
|
|
|
|
ctx,
|
|
|
|
|
skdm,
|
|
|
|
|
result.SenderAddress,
|
|
|
|
|
cli.Store.SenderKeyStore,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Err(err).Msg("ProcessSenderKeyDistributionMessage error")
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if destinationServiceID == cli.Store.PNIServiceID() {
|
|
|
|
|
_, err = cli.Store.RecipientStore.LoadAndUpdateRecipient(ctx, theirServiceID.UUID, uuid.Nil, func(recipient *types.Recipient) (changed bool, err error) {
|
2025-12-11 17:53:48 +02:00
|
|
|
if recipient.Whitelisted == nil {
|
|
|
|
|
log.Debug().Msg("Marking recipient as not whitelisted")
|
|
|
|
|
recipient.Whitelisted = ptr.Ptr(false)
|
|
|
|
|
changed = true
|
|
|
|
|
}
|
2024-04-11 14:31:27 -04:00
|
|
|
if !recipient.NeedsPNISignature {
|
|
|
|
|
log.Debug().Msg("Marking recipient as needing PNI signature")
|
|
|
|
|
recipient.NeedsPNISignature = true
|
2025-12-11 17:53:48 +02:00
|
|
|
changed = true
|
2024-03-25 15:18:25 +02:00
|
|
|
}
|
2025-12-11 17:53:48 +02:00
|
|
|
return
|
2024-04-11 14:31:27 -04:00
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Err(err).Msg("Failed to set needs_pni_signature flag after receiving message to PNI service ID")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-08 23:11:45 +02:00
|
|
|
if content.PniSignatureMessage != nil {
|
2024-04-11 14:31:27 -04:00
|
|
|
log.Debug().Msg("Content includes PNI signature message")
|
2025-12-08 23:11:45 +02:00
|
|
|
err = cli.handlePNISignatureMessage(ctx, theirServiceID, content.PniSignatureMessage)
|
2024-04-11 14:31:27 -04:00
|
|
|
if err != nil {
|
|
|
|
|
log.Err(err).
|
2025-12-08 23:11:45 +02:00
|
|
|
Hex("pni_raw", content.PniSignatureMessage.GetPni()).
|
2024-04-11 14:31:27 -04:00
|
|
|
Stringer("aci", theirServiceID.UUID).
|
|
|
|
|
Msg("Failed to verify ACI-PNI mapping")
|
2024-03-25 15:18:25 +02:00
|
|
|
}
|
2024-04-11 14:31:27 -04:00
|
|
|
}
|
2024-03-25 15:18:25 +02:00
|
|
|
|
2025-11-20 13:16:20 +02:00
|
|
|
if content.SyncMessage != nil && theirServiceID == cli.Store.ACIServiceID() {
|
2025-12-08 23:11:45 +02:00
|
|
|
handlerSuccess = cli.handleSyncMessage(ctx, content.SyncMessage, envelope)
|
|
|
|
|
return nil
|
2024-04-11 14:31:27 -04:00
|
|
|
}
|
|
|
|
|
|
2025-11-20 13:29:38 +02:00
|
|
|
isBlocked, err := cli.Store.RecipientStore.IsBlocked(ctx, theirServiceID.UUID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Err(err).Stringer("sender", theirServiceID).Msg("Failed to check if sender is blocked")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var sendDeliveryReceipt bool
|
2024-04-11 14:31:27 -04:00
|
|
|
if content.DataMessage != nil {
|
2025-11-20 13:29:38 +02:00
|
|
|
handlerSuccess, sendDeliveryReceipt = cli.incomingDataMessage(
|
|
|
|
|
ctx, content.DataMessage, theirServiceID.UUID, theirServiceID, envelope.GetServerTimestamp(), isBlocked,
|
|
|
|
|
)
|
2024-04-11 14:31:27 -04:00
|
|
|
} else if content.EditMessage != nil {
|
2025-11-20 13:29:38 +02:00
|
|
|
handlerSuccess, sendDeliveryReceipt = cli.incomingEditMessage(
|
|
|
|
|
ctx, content.EditMessage, theirServiceID.UUID, theirServiceID, envelope.GetServerTimestamp(), isBlocked,
|
|
|
|
|
)
|
2024-04-11 14:31:27 -04:00
|
|
|
}
|
2025-06-17 22:24:34 +05:30
|
|
|
if sendDeliveryReceipt && handlerSuccess {
|
2024-04-11 14:31:27 -04:00
|
|
|
err = cli.sendDeliveryReceipts(ctx, []uint64{content.DataMessage.GetTimestamp()}, theirServiceID.UUID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Err(err).Msg("sendDeliveryReceipts error")
|
2023-12-22 13:49:38 +02:00
|
|
|
}
|
2024-04-11 14:31:27 -04:00
|
|
|
}
|
2023-12-22 13:49:38 +02:00
|
|
|
|
2025-11-20 13:29:38 +02:00
|
|
|
if content.TypingMessage != nil && (!isBlocked || content.TypingMessage.GetGroupId() != nil) {
|
2024-04-11 14:31:27 -04:00
|
|
|
var groupID types.GroupIdentifier
|
|
|
|
|
if content.TypingMessage.GetGroupId() != nil {
|
|
|
|
|
gidBytes := content.TypingMessage.GetGroupId()
|
|
|
|
|
groupID = types.GroupIdentifier(base64.StdEncoding.EncodeToString(gidBytes))
|
2023-12-22 13:49:38 +02:00
|
|
|
}
|
2025-06-17 22:24:34 +05:30
|
|
|
// No handler success check here, nobody cares if typing notifications are dropped
|
2024-04-11 14:31:27 -04:00
|
|
|
cli.handleEvent(&events.ChatEvent{
|
|
|
|
|
Info: events.MessageInfo{
|
2024-11-13 16:20:28 -05:00
|
|
|
Sender: theirServiceID.UUID,
|
|
|
|
|
ChatID: groupOrUserID(groupID, theirServiceID),
|
|
|
|
|
ServerTimestamp: envelope.GetServerTimestamp(),
|
2024-04-11 14:31:27 -04:00
|
|
|
},
|
|
|
|
|
Event: content.TypingMessage,
|
|
|
|
|
})
|
|
|
|
|
}
|
2023-09-27 23:31:24 -04:00
|
|
|
|
2024-04-11 14:31:27 -04:00
|
|
|
// DM call message (group call is an opaque callMessage and a groupCallUpdate in a dataMessage)
|
2025-11-20 13:29:38 +02:00
|
|
|
if content.CallMessage != nil && (content.CallMessage.Offer != nil || content.CallMessage.Hangup != nil) && !isBlocked {
|
2025-06-17 22:24:34 +05:30
|
|
|
handlerSuccess = cli.handleEvent(&events.Call{
|
2024-04-11 14:31:27 -04:00
|
|
|
Info: events.MessageInfo{
|
2024-11-13 16:20:28 -05:00
|
|
|
Sender: theirServiceID.UUID,
|
|
|
|
|
ChatID: theirServiceID.String(),
|
|
|
|
|
ServerTimestamp: envelope.GetServerTimestamp(),
|
2024-04-11 14:31:27 -04:00
|
|
|
},
|
2024-10-14 12:37:30 +03:00
|
|
|
// CallMessage doesn't have its own timestamp, use one from the envelope
|
|
|
|
|
Timestamp: envelope.GetTimestamp(),
|
2024-04-11 14:31:27 -04:00
|
|
|
IsRinging: content.CallMessage.Offer != nil,
|
2025-06-17 22:24:34 +05:30
|
|
|
}) && handlerSuccess
|
2024-04-11 14:31:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read and delivery receipts
|
|
|
|
|
if content.ReceiptMessage != nil {
|
|
|
|
|
if content.GetReceiptMessage().GetType() == signalpb.ReceiptMessage_DELIVERY && theirServiceID == cli.Store.ACIServiceID() {
|
|
|
|
|
// Ignore delivery receipts from other own devices
|
|
|
|
|
return nil
|
2023-05-31 16:39:09 -04:00
|
|
|
}
|
2025-06-17 22:24:34 +05:30
|
|
|
handlerSuccess = cli.handleEvent(&events.Receipt{
|
2024-04-11 14:31:27 -04:00
|
|
|
Sender: theirServiceID.UUID,
|
|
|
|
|
Content: content.ReceiptMessage,
|
2025-06-17 22:24:34 +05:30
|
|
|
}) && handlerSuccess
|
|
|
|
|
}
|
2024-04-11 14:31:27 -04:00
|
|
|
return nil
|
2023-05-31 16:39:09 -04:00
|
|
|
}
|
|
|
|
|
|
2024-03-20 18:43:32 +02:00
|
|
|
func groupOrUserID(groupID types.GroupIdentifier, userID libsignalgo.ServiceID) string {
|
2024-01-02 18:58:40 +02:00
|
|
|
if groupID == "" {
|
|
|
|
|
return userID.String()
|
|
|
|
|
}
|
|
|
|
|
return string(groupID)
|
|
|
|
|
}
|
2023-12-17 18:14:09 +02:00
|
|
|
|
2025-12-08 23:11:45 +02:00
|
|
|
func (cli *Client) handleSyncMessage(ctx context.Context, msg *signalpb.SyncMessage, envelope *signalpb.Envelope) (handlerSuccess bool) {
|
2025-11-20 13:16:20 +02:00
|
|
|
// TODO: handle more sync messages
|
|
|
|
|
handlerSuccess = true
|
|
|
|
|
log := zerolog.Ctx(ctx)
|
|
|
|
|
if msg.Keys != nil {
|
|
|
|
|
aep := libsignalgo.AccountEntropyPool(msg.Keys.GetAccountEntropyPool())
|
|
|
|
|
cli.Store.MasterKey = msg.Keys.GetMaster()
|
|
|
|
|
if aep != "" {
|
|
|
|
|
aepMasterKey, err := aep.DeriveSVRKey()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Err(err).Msg("Failed to derive master key from account entropy pool")
|
|
|
|
|
} else if cli.Store.MasterKey == nil {
|
|
|
|
|
cli.Store.MasterKey = aepMasterKey
|
|
|
|
|
log.Debug().Msg("Derived master key from account entropy pool (no master key in sync message)")
|
|
|
|
|
} else if !bytes.Equal(aepMasterKey, cli.Store.MasterKey) {
|
|
|
|
|
log.Warn().Msg("Derived master key doesn't match one in sync message")
|
|
|
|
|
} else {
|
|
|
|
|
log.Debug().Msg("Derived master key matches one in sync message")
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
log.Debug().Msg("No account entropy pool in sync message")
|
|
|
|
|
}
|
2025-12-08 23:11:45 +02:00
|
|
|
err := cli.Store.DeviceStore.PutDevice(ctx, &cli.Store.DeviceData)
|
2025-11-20 13:16:20 +02:00
|
|
|
if err != nil {
|
|
|
|
|
log.Err(err).Msg("Failed to save device after receiving master key")
|
|
|
|
|
} else {
|
|
|
|
|
log.Info().Msg("Received master key")
|
|
|
|
|
go cli.SyncStorage(ctx)
|
|
|
|
|
}
|
|
|
|
|
} else if msg.GetFetchLatest().GetType() == signalpb.SyncMessage_FetchLatest_STORAGE_MANIFEST {
|
|
|
|
|
log.Debug().Msg("Received storage manifest fetch latest notice")
|
|
|
|
|
go cli.SyncStorage(ctx)
|
|
|
|
|
}
|
|
|
|
|
syncSent := msg.GetSent()
|
|
|
|
|
if syncSent.GetMessage() != nil || syncSent.GetEditMessage() != nil {
|
2026-02-24 13:48:39 +02:00
|
|
|
syncDestinationServiceID, err := ParseStringOrBinaryServiceID(syncSent.GetDestinationServiceId(), syncSent.GetDestinationServiceIdBinary())
|
|
|
|
|
if err != nil && !errors.Is(err, ErrEmptyUUIDInput) {
|
|
|
|
|
log.Err(err).Msg("Sync message destination parse error")
|
|
|
|
|
}
|
|
|
|
|
if syncSent.GetDestinationE164() != "" && !syncDestinationServiceID.IsEmpty() {
|
|
|
|
|
aci, pni := syncDestinationServiceID.ToACIAndPNI()
|
|
|
|
|
_, err = cli.Store.RecipientStore.UpdateRecipientE164(ctx, aci, pni, syncSent.GetDestinationE164())
|
2025-11-20 13:16:20 +02:00
|
|
|
if err != nil {
|
2026-02-24 13:48:39 +02:00
|
|
|
log.Err(err).Msg("Failed to update recipient E164 after receiving sync message")
|
2025-11-20 13:16:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-12-28 20:14:46 +02:00
|
|
|
for _, unident := range syncSent.GetUnidentifiedStatus() {
|
2026-02-24 14:05:11 +02:00
|
|
|
serviceID, err := ParseStringOrBinaryServiceID(unident.GetDestinationServiceId(), unident.GetDestinationServiceIdBinary())
|
2025-12-28 20:14:46 +02:00
|
|
|
if err != nil {
|
|
|
|
|
log.Err(err).
|
|
|
|
|
Str("destination_service_id", unident.GetDestinationServiceId()).
|
2026-02-24 14:05:11 +02:00
|
|
|
Hex("destination_service_id_bytes", unident.GetDestinationServiceIdBinary()).
|
|
|
|
|
Msg("Failed to parse destination service ID of unidentified send")
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
changed, err := cli.saveSyncPNIIdentityKey(ctx, serviceID, unident.GetDestinationPniIdentityKey())
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Err(err).
|
|
|
|
|
Stringer("destination_service_id", serviceID).
|
2025-12-28 20:14:46 +02:00
|
|
|
Msg("Failed to save PNI identity key from sync message")
|
|
|
|
|
} else if changed {
|
|
|
|
|
log.Debug().
|
2026-02-24 14:05:11 +02:00
|
|
|
Stringer("destination_service_id", serviceID).
|
2025-12-28 20:14:46 +02:00
|
|
|
Msg("Saved new PNI identity key from sync message")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-24 13:48:39 +02:00
|
|
|
if syncDestinationServiceID.IsEmpty() && syncSent.GetMessage().GetGroupV2() == nil && syncSent.GetEditMessage().GetDataMessage().GetGroupV2() == nil {
|
2025-11-20 13:16:20 +02:00
|
|
|
log.Warn().Msg("sync message sent destination is nil")
|
|
|
|
|
} else if msg.Sent.Message != nil {
|
|
|
|
|
// TODO handle expiration start ts, and maybe the sync message ts?
|
2025-11-20 13:29:38 +02:00
|
|
|
cli.incomingDataMessage(ctx, msg.Sent.Message, cli.Store.ACI, syncDestinationServiceID, envelope.GetServerTimestamp(), false)
|
2025-11-20 13:16:20 +02:00
|
|
|
} else if msg.Sent.EditMessage != nil {
|
2025-11-20 13:29:38 +02:00
|
|
|
cli.incomingEditMessage(ctx, msg.Sent.EditMessage, cli.Store.ACI, syncDestinationServiceID, envelope.GetServerTimestamp(), false)
|
2025-11-20 13:16:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if msg.Contacts != nil {
|
|
|
|
|
log.Debug().Msg("Recieved sync message contacts")
|
|
|
|
|
blob := msg.Contacts.Blob
|
|
|
|
|
if blob != nil {
|
2026-03-06 00:12:23 +02:00
|
|
|
// TODO roundtrip via disk to save memory
|
|
|
|
|
contactsBytes, err := DownloadAttachmentWithPointer(ctx, blob, nil, nil)
|
2025-11-20 13:16:20 +02:00
|
|
|
if err != nil {
|
|
|
|
|
log.Err(err).Msg("Contacts Sync DownloadAttachment error")
|
|
|
|
|
}
|
|
|
|
|
// unmarshall contacts
|
|
|
|
|
contacts, avatars, err := unmarshalContactDetailsMessages(contactsBytes)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Err(err).Msg("Contacts Sync unmarshalContactDetailsMessages error")
|
|
|
|
|
}
|
|
|
|
|
log.Debug().Int("contact_count", len(contacts)).Msg("Contacts Sync received contacts")
|
|
|
|
|
convertedContacts := make([]*types.Recipient, 0, len(contacts))
|
|
|
|
|
err = cli.Store.DoContactTxn(ctx, func(ctx context.Context) error {
|
|
|
|
|
for i, signalContact := range contacts {
|
2026-03-24 15:24:03 +02:00
|
|
|
if (signalContact.Aci == nil || *signalContact.Aci == "") && len(signalContact.AciBinary) != 16 {
|
2025-11-20 13:16:20 +02:00
|
|
|
// TODO lookup PNI via CDSI and store that when ACI is missing?
|
|
|
|
|
log.Info().
|
|
|
|
|
Any("contact", signalContact).
|
|
|
|
|
Msg("Signal Contact UUID is nil, skipping")
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
contact, err := cli.StoreContactDetailsAsContact(ctx, signalContact, &avatars[i])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
convertedContacts = append(convertedContacts, contact)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Err(err).Msg("Error storing contacts")
|
|
|
|
|
} else {
|
|
|
|
|
handlerSuccess = cli.handleEvent(&events.ContactList{
|
|
|
|
|
Contacts: convertedContacts,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if msg.Read != nil {
|
|
|
|
|
handlerSuccess = cli.handleEvent(&events.ReadSelf{
|
|
|
|
|
Timestamp: envelope.GetTimestamp(),
|
|
|
|
|
Messages: msg.GetRead(),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
if msg.DeleteForMe != nil {
|
|
|
|
|
handlerSuccess = cli.handleEvent(&events.DeleteForMe{
|
|
|
|
|
Timestamp: envelope.GetTimestamp(),
|
|
|
|
|
SyncMessage_DeleteForMe: msg.DeleteForMe,
|
|
|
|
|
})
|
|
|
|
|
}
|
2025-12-09 16:42:57 +02:00
|
|
|
if msg.MessageRequestResponse != nil {
|
2026-02-28 12:27:46 +02:00
|
|
|
aciUUID, _ := ParseStringOrBinaryUUID(msg.MessageRequestResponse.GetThreadAci(), msg.MessageRequestResponse.GetThreadAciBinary())
|
2025-12-09 16:42:57 +02:00
|
|
|
if aciUUID != uuid.Nil && msg.MessageRequestResponse.GetType() == signalpb.SyncMessage_MessageRequestResponse_ACCEPT {
|
|
|
|
|
_, err := cli.Store.RecipientStore.LoadAndUpdateRecipient(ctx, aciUUID, uuid.Nil, func(recipient *types.Recipient) (changed bool, err error) {
|
2025-12-11 17:53:48 +02:00
|
|
|
changed = !ptr.Val(recipient.Whitelisted) || recipient.NeedsPNISignature
|
|
|
|
|
recipient.Whitelisted = ptr.Ptr(true)
|
|
|
|
|
recipient.NeedsPNISignature = false
|
|
|
|
|
return
|
2025-12-09 16:42:57 +02:00
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Err(err).Msg("Failed to clear needs_pni_signature flag after message request accept")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var groupID *libsignalgo.GroupIdentifier
|
|
|
|
|
if len(msg.MessageRequestResponse.GroupId) == libsignalgo.GroupIdentifierLength {
|
|
|
|
|
groupID = (*libsignalgo.GroupIdentifier)(msg.MessageRequestResponse.GroupId)
|
|
|
|
|
}
|
|
|
|
|
handlerSuccess = cli.handleEvent(&events.MessageRequestResponse{
|
|
|
|
|
Timestamp: envelope.GetTimestamp(),
|
|
|
|
|
ThreadACI: aciUUID,
|
|
|
|
|
GroupID: groupID,
|
|
|
|
|
Type: msg.MessageRequestResponse.GetType(),
|
|
|
|
|
Raw: msg.MessageRequestResponse,
|
|
|
|
|
})
|
|
|
|
|
}
|
2025-11-20 13:16:20 +02:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-24 14:05:11 +02:00
|
|
|
func (cli *Client) saveSyncPNIIdentityKey(ctx context.Context, serviceID libsignalgo.ServiceID, identityKeyBytes []byte) (bool, error) {
|
|
|
|
|
if identityKeyBytes == nil || serviceID.Type != libsignalgo.ServiceIDTypePNI {
|
2025-12-28 20:14:46 +02:00
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
identityKey, err := libsignalgo.DeserializeIdentityKey(identityKeyBytes)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false, fmt.Errorf("failed to deserialize PNI identity key: %w", err)
|
|
|
|
|
}
|
|
|
|
|
changed, err := cli.Store.IdentityKeyStore.SaveIdentityKey(ctx, serviceID, identityKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false, fmt.Errorf("failed to save PNI identity key: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return changed, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-22 15:37:21 +02:00
|
|
|
func (cli *Client) handlePNISignatureMessage(ctx context.Context, sender libsignalgo.ServiceID, msg *signalpb.PniSignatureMessage) error {
|
|
|
|
|
if sender.Type != libsignalgo.ServiceIDTypeACI {
|
|
|
|
|
return fmt.Errorf("PNI signature message sender is not an ACI")
|
|
|
|
|
}
|
|
|
|
|
pniBytes := msg.GetPni()
|
|
|
|
|
if len(pniBytes) != 16 {
|
|
|
|
|
return fmt.Errorf("unexpected PNI length %d (expected 16)", len(pniBytes))
|
|
|
|
|
}
|
|
|
|
|
pni := uuid.UUID(pniBytes)
|
|
|
|
|
pniServiceID := libsignalgo.NewPNIServiceID(pni)
|
2024-03-25 21:25:00 +02:00
|
|
|
pniIdentity, err := cli.Store.IdentityKeyStore.GetIdentityKey(ctx, pniServiceID)
|
2024-03-22 15:37:21 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to get identity for PNI %s: %w", pni, err)
|
|
|
|
|
} else if pniIdentity == nil {
|
2024-07-14 19:19:02 +03:00
|
|
|
zerolog.Ctx(ctx).Debug().
|
|
|
|
|
Stringer("aci", sender.UUID).
|
|
|
|
|
Stringer("pni", pni).
|
|
|
|
|
Msg("Fetching PNI identity for signature verification as it wasn't found in store")
|
|
|
|
|
err = cli.FetchAndProcessPreKey(ctx, pniServiceID, 0)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch prekey for PNI %s after identity wasn't found in store: %w", pni, err)
|
|
|
|
|
} else if pniIdentity, err = cli.Store.IdentityKeyStore.GetIdentityKey(ctx, pniServiceID); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to get identity for PNI %s after fetching: %w", pni, err)
|
|
|
|
|
} else if pniIdentity == nil {
|
|
|
|
|
return fmt.Errorf("identity not found for PNI %s even after fetching", pni)
|
|
|
|
|
}
|
2024-03-22 15:37:21 +02:00
|
|
|
}
|
2024-03-25 21:25:00 +02:00
|
|
|
aciIdentity, err := cli.Store.IdentityKeyStore.GetIdentityKey(ctx, sender)
|
2024-03-22 15:37:21 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to get identity for ACI %s: %w", sender, err)
|
|
|
|
|
} else if aciIdentity == nil {
|
|
|
|
|
return fmt.Errorf("identity not found for ACI %s", sender)
|
|
|
|
|
}
|
|
|
|
|
if ok, err := pniIdentity.VerifyAlternateIdentity(aciIdentity, msg.GetSignature()); err != nil {
|
|
|
|
|
return fmt.Errorf("signature validation failed: %w", err)
|
|
|
|
|
} else if !ok {
|
|
|
|
|
return fmt.Errorf("signature is invalid")
|
|
|
|
|
}
|
|
|
|
|
zerolog.Ctx(ctx).Debug().
|
|
|
|
|
Stringer("aci", sender.UUID).
|
|
|
|
|
Stringer("pni", pni).
|
|
|
|
|
Msg("Verified ACI-PNI mapping")
|
2024-03-22 21:24:30 +02:00
|
|
|
_, err = cli.Store.RecipientStore.LoadAndUpdateRecipient(ctx, sender.UUID, pni, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
zerolog.Ctx(ctx).Err(err).Msg("Failed to update aci/pni mapping in store")
|
|
|
|
|
}
|
2024-03-22 15:37:21 +02:00
|
|
|
cli.handleEvent(&events.ACIFound{ACI: sender, PNI: pniServiceID})
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-20 13:29:38 +02:00
|
|
|
func (cli *Client) incomingEditMessage(
|
|
|
|
|
ctx context.Context,
|
|
|
|
|
editMessage *signalpb.EditMessage,
|
|
|
|
|
messageSenderACI uuid.UUID,
|
|
|
|
|
chatRecipient libsignalgo.ServiceID,
|
|
|
|
|
serverTimestamp uint64,
|
|
|
|
|
isBlocked bool,
|
|
|
|
|
) (handlerSuccess, sendDeliveryReceipt bool) {
|
2024-01-02 18:58:40 +02:00
|
|
|
// If it's a group message, get the ID and invalidate cache if necessary
|
|
|
|
|
var groupID types.GroupIdentifier
|
2024-01-05 20:37:47 +02:00
|
|
|
var groupRevision uint32
|
2024-01-02 18:58:40 +02:00
|
|
|
if editMessage.GetDataMessage().GetGroupV2() != nil {
|
|
|
|
|
// Pull out the master key then store it ASAP - we should pass around GroupIdentifier
|
|
|
|
|
groupMasterKeyBytes := editMessage.GetDataMessage().GetGroupV2().GetMasterKey()
|
|
|
|
|
masterKey := masterKeyFromBytes(libsignalgo.GroupMasterKey(groupMasterKeyBytes))
|
|
|
|
|
var err error
|
2024-01-05 13:44:41 +02:00
|
|
|
groupID, err = cli.StoreMasterKey(ctx, masterKey)
|
2024-01-02 18:58:40 +02:00
|
|
|
if err != nil {
|
2024-01-06 11:37:52 -07:00
|
|
|
zerolog.Ctx(ctx).Err(err).Msg("StoreMasterKey error")
|
2025-11-20 13:29:38 +02:00
|
|
|
return
|
2024-01-02 18:58:40 +02:00
|
|
|
}
|
2024-01-05 20:37:47 +02:00
|
|
|
groupRevision = editMessage.GetDataMessage().GetGroupV2().GetRevision()
|
2025-11-20 13:29:38 +02:00
|
|
|
} else if isBlocked {
|
|
|
|
|
zerolog.Ctx(ctx).Debug().Msg("Dropping direct message from blocked user")
|
|
|
|
|
return true, false
|
2024-01-02 18:58:40 +02:00
|
|
|
}
|
2025-06-17 22:24:34 +05:30
|
|
|
return cli.handleEvent(&events.ChatEvent{
|
2024-01-02 18:58:40 +02:00
|
|
|
Info: events.MessageInfo{
|
2024-11-13 16:20:28 -05:00
|
|
|
Sender: messageSenderACI,
|
|
|
|
|
ChatID: groupOrUserID(groupID, chatRecipient),
|
|
|
|
|
GroupRevision: groupRevision,
|
|
|
|
|
ServerTimestamp: serverTimestamp,
|
2024-01-02 18:58:40 +02:00
|
|
|
},
|
|
|
|
|
Event: editMessage,
|
2025-11-20 13:29:38 +02:00
|
|
|
}), true
|
2024-01-02 18:58:40 +02:00
|
|
|
}
|
2023-10-11 14:47:11 -04:00
|
|
|
|
2025-11-20 13:29:38 +02:00
|
|
|
func (cli *Client) incomingDataMessage(
|
|
|
|
|
ctx context.Context,
|
|
|
|
|
dataMessage *signalpb.DataMessage,
|
|
|
|
|
messageSenderACI uuid.UUID,
|
|
|
|
|
chatRecipient libsignalgo.ServiceID,
|
|
|
|
|
serverTimestamp uint64,
|
|
|
|
|
isBlocked bool,
|
|
|
|
|
) (handlerSuccess, sendDeliveryReceipt bool) {
|
2023-07-19 14:44:13 -04:00
|
|
|
// If there's a profile key, save it
|
|
|
|
|
if dataMessage.ProfileKey != nil {
|
|
|
|
|
profileKey := libsignalgo.ProfileKey(dataMessage.ProfileKey)
|
2024-03-22 21:24:30 +02:00
|
|
|
err := cli.Store.RecipientStore.StoreProfileKey(ctx, messageSenderACI, profileKey)
|
2023-07-19 14:44:13 -04:00
|
|
|
if err != nil {
|
2024-01-06 11:37:52 -07:00
|
|
|
zerolog.Ctx(ctx).Err(err).Msg("StoreProfileKey error")
|
2025-11-20 13:29:38 +02:00
|
|
|
return
|
2023-07-19 14:44:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-25 12:56:39 -04:00
|
|
|
// If it's a group message, get the ID and invalidate cache if necessary
|
2024-01-02 18:58:40 +02:00
|
|
|
var groupID types.GroupIdentifier
|
2024-01-05 20:37:47 +02:00
|
|
|
var groupRevision uint32
|
2023-08-25 12:56:39 -04:00
|
|
|
if dataMessage.GetGroupV2() != nil {
|
2023-09-14 16:33:40 -04:00
|
|
|
// Pull out the master key then store it ASAP - we should pass around GroupIdentifier
|
2023-08-25 12:56:39 -04:00
|
|
|
groupMasterKeyBytes := dataMessage.GetGroupV2().GetMasterKey()
|
2023-09-14 16:33:40 -04:00
|
|
|
masterKey := masterKeyFromBytes(libsignalgo.GroupMasterKey(groupMasterKeyBytes))
|
2024-01-02 18:58:40 +02:00
|
|
|
var err error
|
2024-01-05 13:44:41 +02:00
|
|
|
groupID, err = cli.StoreMasterKey(ctx, masterKey)
|
2023-09-14 16:33:40 -04:00
|
|
|
if err != nil {
|
2024-01-06 11:37:52 -07:00
|
|
|
zerolog.Ctx(ctx).Err(err).Msg("StoreMasterKey error")
|
2025-11-20 13:29:38 +02:00
|
|
|
return
|
2023-09-14 16:33:40 -04:00
|
|
|
}
|
2024-01-05 20:37:47 +02:00
|
|
|
groupRevision = dataMessage.GetGroupV2().GetRevision()
|
2025-11-20 13:29:38 +02:00
|
|
|
} else if isBlocked {
|
|
|
|
|
zerolog.Ctx(ctx).Debug().Msg("Dropping direct message from blocked user")
|
|
|
|
|
return true, false
|
2023-09-04 22:00:03 -04:00
|
|
|
}
|
|
|
|
|
|
2024-01-02 18:58:40 +02:00
|
|
|
evtInfo := events.MessageInfo{
|
2024-11-13 16:20:28 -05:00
|
|
|
Sender: messageSenderACI,
|
|
|
|
|
ChatID: groupOrUserID(groupID, chatRecipient),
|
|
|
|
|
GroupRevision: groupRevision,
|
|
|
|
|
ServerTimestamp: serverTimestamp,
|
2023-10-22 20:26:56 -04:00
|
|
|
}
|
2024-01-02 18:58:40 +02:00
|
|
|
// Hacky special case for group calls to cache the state
|
2023-09-27 23:31:24 -04:00
|
|
|
if dataMessage.GroupCallUpdate != nil {
|
2025-11-25 20:15:52 +02:00
|
|
|
isRinging := cli.GroupCache.UpdateActiveCall(groupID, dataMessage.GroupCallUpdate.GetEraId())
|
2025-06-17 22:24:34 +05:30
|
|
|
return cli.handleEvent(&events.Call{
|
2024-01-02 18:58:40 +02:00
|
|
|
Info: evtInfo,
|
|
|
|
|
Timestamp: dataMessage.GetTimestamp(),
|
2023-09-27 23:31:24 -04:00
|
|
|
IsRinging: isRinging,
|
2025-11-20 13:29:38 +02:00
|
|
|
}), true
|
2024-01-02 18:58:40 +02:00
|
|
|
} else {
|
2025-06-17 22:24:34 +05:30
|
|
|
return cli.handleEvent(&events.ChatEvent{
|
2024-01-02 18:58:40 +02:00
|
|
|
Info: evtInfo,
|
|
|
|
|
Event: dataMessage,
|
2025-11-20 13:29:38 +02:00
|
|
|
}), true
|
2023-10-22 20:26:56 -04:00
|
|
|
}
|
2023-09-28 00:19:22 -04:00
|
|
|
}
|
|
|
|
|
|
2024-01-05 13:44:41 +02:00
|
|
|
func (cli *Client) sendDeliveryReceipts(ctx context.Context, deliveredTimestamps []uint64, senderUUID uuid.UUID) error {
|
2023-09-28 00:19:22 -04:00
|
|
|
// Send delivery receipts
|
|
|
|
|
if len(deliveredTimestamps) > 0 {
|
|
|
|
|
receipt := DeliveredReceiptMessageForTimestamps(deliveredTimestamps)
|
2024-03-15 15:30:44 +02:00
|
|
|
result := cli.SendMessage(ctx, libsignalgo.NewACIServiceID(senderUUID), receipt)
|
2023-09-28 00:19:22 -04:00
|
|
|
if !result.WasSuccessful {
|
2024-01-06 10:01:15 -07:00
|
|
|
return fmt.Errorf("failed to send delivery receipts: %v", result)
|
2023-08-25 12:56:39 -04:00
|
|
|
}
|
2023-07-19 14:44:13 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|