diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index df15747..8b988db 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -30,7 +30,6 @@ jobs: run: | go install golang.org/x/tools/cmd/goimports@latest go install honnef.co/go/tools/cmd/staticcheck@latest - go install github.com/beeper/pre-commit-go/bridgelint@latest export PATH="$HOME/go/bin:$PATH" - name: Run pre-commit diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1f1fe9c..c9cc6a3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,10 +22,7 @@ repos: # TODO: reenable this and fix all the problems - repo: https://github.com/beeper/pre-commit-go - rev: v0.5.0 + rev: v0.4.2 hooks: - id: zerolog-ban-msgf - id: zerolog-use-stringer - - id: bridgelint - language: system - verbose: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e00a62..54ba2ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,3 @@ -# v26.11 (unreleased) - -* Updated libsignal to v0.106.1. -* Added support for sending more than 2048 characters of text to Signal by - uploading the rest of the text as an attachment, like official clients do. -* Added automatic retrying of group message sends if the server returns a 500 - or 503 error. -* Added support for encrypting direct media with FLOE ([MSC4571]). -* Fixed memory leak in libsignal store implementation. - -[MSC4571]: https://github.com/matrix-org/matrix-spec-proposals/pull/4571 - # v26.10 * Updated libsignal to v0.105.0. diff --git a/go.mod b/go.mod index 2847afc..89ed63d 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( google.golang.org/grpc v1.84.0 google.golang.org/protobuf v1.36.12 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.32.1-0.20261026145030-920bc073ba8d + maunium.net/go/mautrix v0.32.0 ) require ( diff --git a/go.sum b/go.sum index 0da95ea..e5a7857 100644 --- a/go.sum +++ b/go.sum @@ -95,5 +95,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M= maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.32.1-0.20261026145030-920bc073ba8d h1:DsMn0i7cBKcQZ0AVxJm9hfWAu6xJdjRrf457EtXxyNY= -maunium.net/go/mautrix v0.32.1-0.20261026145030-920bc073ba8d/go.mod h1:tZSwPzw+/0pa0dxGufkh0uJqM84BNxB6e2I0+S0UyjU= +maunium.net/go/mautrix v0.32.0 h1:xuAZzzyWce0pFkBhV/MCLFiJdVs2rFkY3Eep7hNng4I= +maunium.net/go/mautrix v0.32.0/go.mod h1:tZSwPzw+/0pa0dxGufkh0uJqM84BNxB6e2I0+S0UyjU= diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index b566850..b7b2a23 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -77,8 +77,8 @@ func (s *SignalClient) FetchMessages(ctx context.Context, params bridgev2.FetchM } // GetBackupChatItems returns in reverse chronological order, so flip the list slices.Reverse(items) + var firstDirectionfulProcessed bool var isRead bool - var readUpTo time.Time convertedMessages := make([]*bridgev2.BackfillMessage, 0, len(items)) attMap := make(msgconv.AttachmentMap) recipientMap := make(map[uint64]*backuppb.Recipient) @@ -123,17 +123,20 @@ func (s *SignalClient) FetchMessages(ctx context.Context, params bridgev2.FetchM case *backuppb.ChatItem_Incoming: streamOrder = int64(dt.Incoming.GetDateServerSent()) prevStreamOrder = streamOrder - isRead = dt.Incoming.Read - if isRead { - readUpTo = time.UnixMilli(int64(item.DateSent)) + if !firstDirectionfulProcessed { + firstDirectionfulProcessed = true + isRead = dt.Incoming.Read } case *backuppb.ChatItem_Outgoing: streamOrder = int64(item.GetDateSent()) // Ensure stream order is higher than previous incoming item, but lower than next incoming item streamOrder = min(streamOrder, findNextStreamOrder(i+1)-1) streamOrder = max(streamOrder, prevStreamOrder+1) - isRead = true - readUpTo = time.UnixMilli(int64(item.DateSent)) + + if !firstDirectionfulProcessed { + firstDirectionfulProcessed = true + isRead = true + } } if len(attMap) > 0 { clear(attMap) @@ -175,15 +178,11 @@ func (s *SignalClient) FetchMessages(ctx context.Context, params bridgev2.FetchM Reactions: convertedReactions, }) } - if isRead { - readUpTo = time.Time{} - } return &bridgev2.FetchMessagesResponse{ Messages: convertedMessages, HasMore: len(items) >= params.Count, Forward: params.Forward, MarkRead: isRead, - ReadUpTo: readUpTo, ApproxTotalCount: chat.TotalMessages, CompleteCallback: func() { // When reaching the last backwards backfill batch, delete the chat from the backup store. diff --git a/pkg/connector/capabilities.go b/pkg/connector/capabilities.go index 4ff4b3c..05c7e60 100644 --- a/pkg/connector/capabilities.go +++ b/pkg/connector/capabilities.go @@ -40,7 +40,7 @@ func supportedIfFFmpeg() event.CapabilitySupportLevel { } func capID() string { - base := "fi.mau.signal.capabilities.2026_10_21" + base := "fi.mau.signal.capabilities.2026_10_06" if ffmpeg.Supported() { return base + "+ffmpeg" } @@ -48,7 +48,7 @@ func capID() string { } const MaxFileSize = 100 * 1024 * 1024 -const MaxTextLength = 64 * 1024 +const MaxTextLength = 2000 var signalCaps = &event.RoomFeatures{ ID: capID(), @@ -105,8 +105,6 @@ var signalCaps = &event.RoomFeatures{ "audio/mpeg": event.CapLevelFullySupported, "audio/mp3": event.CapLevelFullySupported, "audio/flac": event.CapLevelFullySupported, - "audio/mp4": event.CapLevelFullySupported, - "audio/wav": event.CapLevelFullySupported, }, MaxSize: MaxFileSize, }, @@ -160,7 +158,7 @@ var signalCaps = &event.RoomFeatures{ event.MemberActionBan: event.CapLevelFullySupported, event.MemberActionKick: event.CapLevelFullySupported, }, - MaxTextLength: MaxTextLength, + MaxTextLength: MaxTextLength, // TODO support arbitrary sized text messages with files LocationMessage: event.CapLevelPartialSupport, Poll: event.CapLevelFullySupported, PollEnd: event.CapLevelUnsupported, @@ -265,5 +263,5 @@ func (s *SignalConnector) GetCapabilities() *bridgev2.NetworkGeneralCapabilities } func (s *SignalConnector) GetBridgeInfoVersion() (info, capabilities int) { - return 1, 15 + return 1, 13 } diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index 669c015..759cdf9 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -125,10 +125,9 @@ func (s *SignalClient) contactToUserInfo(ctx context.Context, contact *types.Rec } name := s.Main.Config.FormatDisplayname(contact, username) ui.Name = &name - contactAvatarID := networkid.AvatarID("hash:" + contact.ContactAvatar.Hash) - if s.Main.Config.UseContactAvatars && contact.ContactAvatar.Hash != "" && s.canUseContactAvatar(ctx, contact, contactAvatarID) { + if s.Main.Config.UseContactAvatars && contact.ContactAvatar.Hash != "" { ui.Avatar = &bridgev2.Avatar{ - ID: contactAvatarID, + ID: networkid.AvatarID("hash:" + contact.ContactAvatar.Hash), Get: func(ctx context.Context) ([]byte, error) { if contact.ContactAvatar.Image == nil { return nil, fmt.Errorf("contact avatar not available") @@ -173,20 +172,6 @@ func (s *SignalClient) contactToUserInfo(ctx context.Context, contact *types.Rec return ui, nil } -func (s *SignalClient) canUseContactAvatar(ctx context.Context, contact *types.Recipient, avatarID networkid.AvatarID) bool { - if contact.ContactAvatar.Image != nil { - return true - } else if contact.ACI == uuid.Nil { - return false - } - ghost, err := s.Main.Bridge.GetExistingGhostByID(ctx, signalid.MakeUserID(contact.ACI)) - if err != nil { - zerolog.Ctx(ctx).Err(err).Msg("Failed to get ghost to check contact avatar") - return true - } - return ghost != nil && ghost.AvatarID == avatarID && ghost.AvatarMXC != "" && ghost.AvatarSet -} - func (s *SignalConnector) ValidateUserID(id networkid.UserID) bool { _, err := signalid.ParseUserIDAsServiceID(id) return err == nil @@ -235,7 +220,7 @@ func (s *SignalClient) resolvePhone(ctx context.Context, number string) (aci, pn aci = recipient.ACI pni = recipient.PNI } else if resp, lookupErr := s.Client.LookupPhone(ctx, e164Number); lookupErr != nil { - err = wrapRespError(fmt.Errorf("error looking up number on server: %w", lookupErr)) + err = fmt.Errorf("error looking up number on server: %w", lookupErr) } else { aci = resp[e164Number].ACI pni = resp[e164Number].PNI @@ -258,7 +243,6 @@ func (s *SignalClient) resolvePhone(ctx context.Context, number string) (aci, pn func (s *SignalClient) resolveUsername(ctx context.Context, username string) (aci, pni uuid.UUID, err error) { serviceID, err := s.Client.ResolveUsername(ctx, username) if err != nil { - err = wrapRespError(fmt.Errorf("error looking up username on server: %w", err)) return } aci, pni = serviceID.ToACIAndPNI() @@ -404,7 +388,7 @@ func (s *SignalClient) CreateGroup(ctx context.Context, params *bridgev2.GroupCr } group.AvatarPath, err = s.Client.UploadGroupAvatar(ctx, avatarBytes, group.GroupIdentifier, group.GroupMasterKey) if err != nil { - return nil, wrapRespError(fmt.Errorf("failed to upload avatar: %w", err)) + return nil, fmt.Errorf("failed to upload avatar: %w", err) } } portal, err := s.Main.Bridge.GetPortalByKey(ctx, s.makePortalKey(string(group.GroupIdentifier))) @@ -434,7 +418,7 @@ func (s *SignalClient) CreateGroup(ctx context.Context, params *bridgev2.GroupCr } resp, err := s.Client.CreateGroup(ctx, group) if err != nil { - return nil, wrapRespError(fmt.Errorf("failed to create group: %w", err)) + return nil, fmt.Errorf("failed to create group: %w", err) } if params.RoomID != "" { // UpdateMatrixRoomID could do this for us if we passed ChatInfoSource to it, @@ -460,15 +444,11 @@ func (s *SignalClient) GetContactList(ctx context.Context) ([]*bridgev2.ResolveI } resp := make([]*bridgev2.ResolveIdentifierResponse, len(recipients)) for i, recipient := range recipients { - if err = ctx.Err(); err != nil { - return nil, err + recipientResp := &bridgev2.ResolveIdentifierResponse{ + Chat: s.makeCreateDMResponse(ctx, recipient, nil), } - recipientResp := &bridgev2.ResolveIdentifierResponse{} var username string if recipient.ACI != uuid.Nil { - recipientResp.Chat = &bridgev2.CreateChatResponse{ - PortalKey: s.makeDMPortalKey(libsignalgo.NewACIServiceID(recipient.ACI)), - } recipientResp.UserID = signalid.MakeUserID(recipient.ACI) ghost, err := s.Main.Bridge.GetGhostByID(ctx, recipientResp.UserID) if err != nil { @@ -477,11 +457,7 @@ func (s *SignalClient) GetContactList(ctx context.Context) ([]*bridgev2.ResolveI recipientResp.Ghost = ghost username = ghost.Metadata.(*signalid.GhostMetadata).Username } else { - pniServiceID := libsignalgo.NewPNIServiceID(recipient.PNI) - recipientResp.Chat = &bridgev2.CreateChatResponse{ - PortalKey: s.makeDMPortalKey(pniServiceID), - } - recipientResp.UserID = signalid.MakeUserIDFromServiceID(pniServiceID) + recipientResp.UserID = signalid.MakeUserIDFromServiceID(libsignalgo.NewPNIServiceID(recipient.PNI)) } recipientResp.UserInfo, err = s.contactToUserInfo(ctx, recipient, username, false) if err != nil { diff --git a/pkg/connector/chatsync.go b/pkg/connector/chatsync.go index ec05b36..47963d7 100644 --- a/pkg/connector/chatsync.go +++ b/pkg/connector/chatsync.go @@ -19,7 +19,6 @@ package connector import ( "context" "encoding/base64" - "errors" "time" "github.com/google/uuid" @@ -29,7 +28,6 @@ import ( "go.mau.fi/mautrix-signal/pkg/libsignalgo" "go.mau.fi/mautrix-signal/pkg/signalid" - "go.mau.fi/mautrix-signal/pkg/signalmeow" "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/backuppb" "go.mau.fi/mautrix-signal/pkg/signalmeow/types" ) @@ -50,15 +48,7 @@ func (s *SignalClient) syncChats(ctx context.Context, cancel context.CancelFunc) if s.Client.Store.EphemeralBackupKey != nil { zerolog.Ctx(ctx).Info().Msg("Fetching transfer archive before syncing chats") meta, err := s.Client.WaitForTransfer(ctx) - if errors.Is(err, signalmeow.ErrTransferTimedOut) { - zerolog.Ctx(ctx).Warn().Msg("Transfer archive wasn't uploaded in time, giving up") - s.UserLogin.Metadata.(*signalid.UserLoginMetadata).ChatsSynced = true - err = s.UserLogin.Save(ctx) - if err != nil { - zerolog.Ctx(ctx).Err(err).Msg("Failed to save user login metadata after transfer archive timed out") - } - return - } else if err != nil { + if err != nil { zerolog.Ctx(ctx).Err(err).Msg("Failed to request transfer archive") return } else if meta.Error != "" { diff --git a/pkg/connector/client.go b/pkg/connector/client.go index b5531b5..d915576 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -182,9 +182,9 @@ func (s *SignalClient) bridgeStateLoop(statusChan <-chan signalmeow.SignalConnec peekedConnectionStatus.Event == signalmeow.SignalConnectionEventNone { s.UserLogin.Log.Debug().Msg("Sending TransientDisconnect BridgeState") if err == nil { - s.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateTransientDisconnect, Error: "signal-disconnected"}) + s.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateTransientDisconnect}) } else { - s.UserLogin.BridgeState.Send(connectionErrorState(err)) + s.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateTransientDisconnect, Error: "unknown-websocket-error", Message: err.Error()}) } } } @@ -192,7 +192,11 @@ func (s *SignalClient) bridgeStateLoop(statusChan <-chan signalmeow.SignalConnec case signalmeow.SignalConnectionEventLoggedOut: s.stopChatSync() s.UserLogin.Log.Debug().Msg("Sending BadCredentials BridgeState") - s.sendLoggedOutState(err) + if err == nil { + s.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateBadCredentials, Message: "You have been logged out of Signal, please reconnect"}) + } else { + s.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateBadCredentials, Message: err.Error()}) + } err = s.Client.ClearKeysAndDisconnect(context.TODO()) if err != nil { s.UserLogin.Log.Error().Err(err).Msg("Failed to clear keys and disconnect") @@ -200,18 +204,18 @@ func (s *SignalClient) bridgeStateLoop(statusChan <-chan signalmeow.SignalConnec case signalmeow.SignalConnectionEventError: s.UserLogin.Log.Debug().Msg("Sending TransientDisconnect BridgeState") - s.UserLogin.BridgeState.Send(connectionErrorState(err)) + s.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateTransientDisconnect, Error: "unknown-websocket-error", Message: err.Error()}) case signalmeow.SignalConnectionEventFatalError: s.UserLogin.Log.Debug().Msg("Sending UnknownError BridgeState") - s.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateUnknownError, Error: "signal-connection-failed", Info: map[string]any{"go_error": err.Error()}}) + s.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateUnknownError, Error: "unknown-websocket-error", Message: err.Error()}) case signalmeow.SignalConnectionCleanShutdown: if s.Client.IsLoggedIn() { s.UserLogin.Log.Debug().Msg("Clean Shutdown - sending no BridgeState") } else { s.UserLogin.Log.Debug().Msg("Clean Shutdown, but logged out - Sending BadCredentials BridgeState") - s.sendLoggedOutState(nil) + s.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateBadCredentials, Message: "You have been logged out of Signal, please reconnect"}) } } } @@ -219,10 +223,7 @@ func (s *SignalClient) bridgeStateLoop(statusChan <-chan signalmeow.SignalConnec func (s *SignalClient) Connect(ctx context.Context) { if s.Client == nil { - s.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateBadCredentials, Error: "signal-not-logged-in", UserAction: status.UserActionRelogin}) - return - } else if !s.Client.IsLoggedIn() { - s.sendLoggedOutState(nil) + s.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateBadCredentials, Message: "You're not logged into Signal"}) return } s.updateRemoteProfile(ctx, false) @@ -230,9 +231,6 @@ func (s *SignalClient) Connect(ctx context.Context) { } func (s *SignalClient) ConnectBackground(ctx context.Context, _ *bridgev2.ConnectBackgroundParams) error { - if !s.IsLoggedIn() { - return bridgev2.ErrNotLoggedIn - } s.queueEmptyWaiter.Clear() ch, unauthCh, err := s.Client.StartWebsockets(ctx) if err != nil { @@ -253,11 +251,6 @@ func (s *SignalClient) ConnectBackground(ctx context.Context, _ *bridgev2.Connec log.Err(status.Err).Msg("Authed websocket disconnected") case web.SignalWebsocketConnectionEventLoggedOut: log.Err(status.Err).Msg("Authed websocket logged out") - s.sendLoggedOutState(status.Err) - err = s.Client.ClearKeysAndDisconnect(ctx) - if err != nil { - log.Err(err).Msg("Failed to clear keys and disconnect") - } return fmt.Errorf("authed websocket logged out: %w", status.Err) case web.SignalWebsocketConnectionEventError, web.SignalWebsocketConnectionEventFatalError: log.Err(status.Err).Msg("Authed websocket error") @@ -322,7 +315,7 @@ func (s *SignalClient) tryConnect(ctx context.Context, retryCount int, noLoginSy ch, err := s.Client.StartReceiveLoops(ctx) if err != nil { zerolog.Ctx(ctx).Err(err).Msg("Failed to start receive loops") - s.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateTransientDisconnect, Error: "signal-connect-failed", Info: map[string]any{"go_error": err.Error()}}) + s.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateTransientDisconnect, Error: "unknown-websocket-error", Message: err.Error()}) retryInSeconds := 2 << retryCount if retryInSeconds > 150 { retryInSeconds = 150 diff --git a/pkg/connector/config.go b/pkg/connector/config.go index 9a0d65a..6e9f2e0 100644 --- a/pkg/connector/config.go +++ b/pkg/connector/config.go @@ -42,7 +42,6 @@ type SignalConfig struct { NoteToSelfAvatar id.ContentURIString `yaml:"note_to_self_avatar"` LocationFormat string `yaml:"location_format"` ExtEvPolls bool `yaml:"extev_polls"` - IdentityChangeNotices bool `yaml:"identity_change_notices"` displaynameTemplate *template.Template `yaml:"-"` } @@ -107,7 +106,6 @@ func upgradeConfig(helper up.Helper) { helper.Copy(up.Str, "note_to_self_avatar") helper.Copy(up.Str, "location_format") helper.Copy(up.Bool, "extev_polls") - helper.Copy(up.Bool, "identity_change_notices") } func (s *SignalConnector) GetConfig() (string, any, up.Upgrader) { diff --git a/pkg/connector/connector.go b/pkg/connector/connector.go index 3a3823a..392a100 100644 --- a/pkg/connector/connector.go +++ b/pkg/connector/connector.go @@ -94,11 +94,8 @@ func (s *SignalConnector) ResetHTTPTransport() { settings = hs.GetHTTPClientSettings() } oldClient := web.SignalHTTPClient - oldUploadClient := web.SignalUploadHTTPClient web.SignalHTTPClient = settings.WithTLSConfig(web.SignalTLSConfig).Compile() - web.SignalUploadHTTPClient = settings.WithTLSConfig(web.SignalTLSConfig).WithResponseHeaderTimeout(2 * time.Minute).Compile() oldClient.CloseIdleConnections() - oldUploadClient.CloseIdleConnections() } func (s *SignalConnector) ResetNetworkConnections() { diff --git a/pkg/connector/directmedia.go b/pkg/connector/directmedia.go index 82ed16d..05e2a07 100644 --- a/pkg/connector/directmedia.go +++ b/pkg/connector/directmedia.go @@ -79,7 +79,7 @@ func (s *SignalConnector) Download(ctx context.Context, mediaID networkid.MediaI rawDataResp, err = client.Client.DownloadGroupAvatar(ctx, info.GroupAvatarPath, groupMasterKey) if err != nil { log.Err(err).Msg("Direct download failed") - return nil, wrapDirectMediaError(err) + return nil, err } case *signalid.DirectMediaProfileAvatar: log.Info(). @@ -107,7 +107,7 @@ func (s *SignalConnector) Download(ctx context.Context, mediaID networkid.MediaI rawDataResp, err = client.Client.DownloadUserAvatar(ctx, info.ProfileAvatarPath, *profileKey) if err != nil { log.Err(err).Msg("Direct download failed") - return nil, wrapDirectMediaError(err) + return nil, err } case *signalid.DirectMediaSticker: log.Info(). @@ -118,7 +118,7 @@ func (s *SignalConnector) Download(ctx context.Context, mediaID networkid.MediaI rawDataResp, err = signalmeow.DownloadStickerPackItem(ctx, info.PackID, info.PackKey, info.StickerID) if err != nil { log.Err(err).Msg("Direct download failed") - return nil, wrapDirectMediaError(err) + return nil, err } default: return nil, fmt.Errorf("no downloader for direct media type: %T", info) diff --git a/pkg/connector/errors.go b/pkg/connector/errors.go deleted file mode 100644 index c06ee17..0000000 --- a/pkg/connector/errors.go +++ /dev/null @@ -1,183 +0,0 @@ -// mautrix-signal - A Matrix-Signal puppeting bridge. -// Copyright (C) 2026 Tulir Asokan -// -// 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 . - -package connector - -import ( - "errors" - "fmt" - "net/http" - - "google.golang.org/grpc/codes" - grpcstatus "google.golang.org/grpc/status" - "maunium.net/go/mautrix" - "maunium.net/go/mautrix/bridgev2" - "maunium.net/go/mautrix/bridgev2/status" - "maunium.net/go/mautrix/event" - - "go.mau.fi/mautrix-signal/pkg/signalmeow" - "go.mau.fi/mautrix-signal/pkg/signalmeow/web" -) - -func init() { - status.BridgeStateHumanErrors.Update(status.BridgeStateErrorMap{ - "signal-logged-out": "You have been logged out of Signal, please reconnect", - "signal-not-logged-in": "You're not logged into Signal", - "signal-disconnected": "Disconnected from Signal, reconnecting", - "signal-connection-error": "Connection to Signal failed, reconnecting", - "signal-server-error": "Signal server error, reconnecting", - "signal-rate-limited": "Rate limited by Signal, reconnecting", - "signal-connect-failed": "Failed to connect to Signal, retrying", - "signal-connection-failed": "Connection to Signal failed", - }) -} - -var ( - errEditOthersMessage = bridgev2.WrapErrorInStatus(errors.New("you can only edit your own messages")). - WithErrorAsMessage(). - WithErrorReason(event.MessageStatusNoPermission). - WithStatus(event.MessageStatusFail). - WithIsCertain(true). - WithSendNotice(true) - errDeleteOthersMessage = bridgev2.WrapErrorInStatus(errors.New("you can only delete your own messages")). - WithErrorAsMessage(). - WithErrorReason(event.MessageStatusNoPermission). - WithStatus(event.MessageStatusFail). - WithIsCertain(true). - WithSendNotice(true) - errInviteToDM = bridgev2.WrapErrorInStatus(errors.New("can't invite more users to Signal DMs")). - WithErrorAsMessage(). - WithErrorReason(event.MessageStatusUnsupported). - WithStatus(event.MessageStatusFail). - WithIsCertain(true). - WithSendNotice(true) - errUnsupportedDisappearingType = bridgev2.WrapErrorInStatus(errors.New("only disappearing timers that start after reading are supported on Signal")). - WithErrorAsMessage(). - WithErrorReason(event.MessageStatusUnsupported). - WithStatus(event.MessageStatusFail). - WithIsCertain(true). - WithSendNotice(true) -) - -func wrapConversionError(err error) error { - if err == nil || errors.As(err, &bridgev2.MessageStatus{}) { - return err - } - return bridgev2.WrapErrorInStatus(err). - WithMessage("failed to convert message for Signal"). - WithIsCertain(true). - WithSendNotice(true) -} - -func wrapSendError(err error) error { - if err == nil || errors.As(err, &bridgev2.MessageStatus{}) { - return err - } - switch { - case errors.Is(err, signalmeow.ErrUnregisteredUser): - return bridgev2.WrapErrorInStatus(err). - WithMessage("recipient isn't registered on Signal"). - WithErrorReason(event.MessageStatusNetworkError). - WithStatus(event.MessageStatusFail). - WithIsCertain(true). - WithSendNotice(true) - case errors.Is(err, signalmeow.ErrRateLimited), errors.Is(err, signalmeow.RateLimitError): - return bridgev2.WrapErrorInStatus(err). - WithMessage("rate limited by Signal, try again later"). - WithErrorReason(event.MessageStatusNetworkError). - WithIsCertain(true). - WithSendNotice(true) - case errors.Is(err, signalmeow.AuthorizationFailedError): - return bridgev2.WrapErrorInStatus(err). - WithMessage("you don't have permission to do that in this Signal group"). - WithErrorReason(event.MessageStatusNoPermission). - WithStatus(event.MessageStatusFail). - WithIsCertain(true). - WithSendNotice(true) - case errors.Is(err, signalmeow.GroupPatchNotAcceptedError): - return bridgev2.WrapErrorInStatus(err). - WithMessage("Signal rejected the group change"). - WithErrorReason(event.MessageStatusNetworkError). - WithStatus(event.MessageStatusFail). - WithIsCertain(true). - WithSendNotice(true) - default: - return bridgev2.WrapErrorInStatus(err). - WithMessage("failed to send to Signal"). - WithSendNotice(true) - } -} - -func wrapRespError(err error) error { - var mapped bridgev2.RespError - var cdsRateLimit signalmeow.ContactDiscoveryRateLimitError - switch { - case errors.As(err, &cdsRateLimit), errors.Is(err, signalmeow.RateLimitError), errors.Is(err, signalmeow.ErrRateLimited), - grpcstatus.Code(err) == codes.ResourceExhausted: - mapped = bridgev2.RespError(mautrix.MLimitExceeded.WithMessage("Rate limited by Signal, please try again later").WithStatus(http.StatusBadRequest)) - case errors.Is(err, signalmeow.AuthorizationFailedError): - mapped = bridgev2.RespError(mautrix.MForbidden.WithMessage("Signal denied the request")) - default: - mapped = bridgev2.RespError(mautrix.MUnknown.WithMessage("Request to Signal failed")) - } - return fmt.Errorf("%w: %w", mapped, err) -} - -func wrapDirectMediaError(err error) error { - var statusErr web.HTTPStatusError - if !errors.As(err, &statusErr) { - return err - } - switch statusErr.StatusCode { - case http.StatusNotFound, http.StatusGone: - return mautrix.MNotFound.WithMessage("Media not found on Signal") - case http.StatusUnauthorized, http.StatusForbidden: - return mautrix.MForbidden.WithMessage("Signal denied access to the media") - case http.StatusTooManyRequests: - return mautrix.MLimitExceeded.WithMessage("Rate limited by Signal, please try again later").WithCanRetry(true) - default: - return mautrix.MUnknown.WithMessage("Unexpected response from Signal").WithStatus(http.StatusBadGateway).WithCanRetry(true) - } -} - -func connectionErrorState(err error) status.BridgeState { - state := status.BridgeState{ - StateEvent: status.StateTransientDisconnect, - Error: "signal-connection-error", - Info: map[string]any{"go_error": err.Error()}, - } - var statusErr web.HTTPStatusError - if errors.As(err, &statusErr) { - if statusErr.StatusCode == http.StatusTooManyRequests { - state.Error = "signal-rate-limited" - } else if statusErr.StatusCode >= 500 { - state.Error = "signal-server-error" - } - } - return state -} - -func (s *SignalClient) sendLoggedOutState(err error) { - state := status.BridgeState{ - StateEvent: status.StateBadCredentials, - Error: "signal-logged-out", - UserAction: status.UserActionRelogin, - } - if err != nil { - state.Info = map[string]any{"go_error": err.Error()} - } - s.UserLogin.BridgeState.Send(state) -} diff --git a/pkg/connector/example-config.yaml b/pkg/connector/example-config.yaml index 0a818d1..f67fbc8 100644 --- a/pkg/connector/example-config.yaml +++ b/pkg/connector/example-config.yaml @@ -25,5 +25,3 @@ note_to_self_avatar: mxc://maunium.net/REBIVrqjZwmaWpssCZpBlmlL location_format: 'https://www.google.com/maps/place/%[1]s,%[2]s' # Should polls be sent using unstable MSC3381 event types? extev_polls: false -# Should another user's safety number changing send a message to Matrix? -identity_change_notices: false diff --git a/pkg/connector/groupinfo.go b/pkg/connector/groupinfo.go index fa75743..d958463 100644 --- a/pkg/connector/groupinfo.go +++ b/pkg/connector/groupinfo.go @@ -39,7 +39,6 @@ import ( var defaultPL = 0 var moderatorPL = 50 -var terminatedPL = 100 func roleToPL(role signalmeow.GroupMemberRole) *int { switch role { @@ -71,21 +70,6 @@ func applyAttributesAccess(plc *bridgev2.PowerLevelOverrides, attributeAccess si plc.Events[event.StateBeeperDisappearingTimer] = attributePL } -func applyTerminated(plc *bridgev2.PowerLevelOverrides) { - plc.EventsDefault = &terminatedPL - plc.StateDefault = &terminatedPL - plc.Invite = &terminatedPL - plc.Kick = &terminatedPL - plc.Ban = &terminatedPL - plc.Redact = &terminatedPL - for _, evtType := range []event.Type{ - event.EventReaction, event.EventRedaction, event.StatePowerLevels, event.StateRoomName, - event.StateRoomAvatar, event.StateTopic, event.StateBeeperDisappearingTimer, - } { - plc.Events[evtType] = terminatedPL - } -} - func applyMembersAccess(plc *bridgev2.PowerLevelOverrides, memberAccess signalmeow.AccessControl) { if memberAccess == signalmeow.AccessControl_ADMINISTRATOR { plc.Invite = &moderatorPL @@ -128,8 +112,6 @@ func (s *SignalClient) wrapGroupInfo(ctx context.Context, groupInfo *signalmeow. PowerLevels: &bridgev2.PowerLevelOverrides{ Events: map[event.Type]int{ event.StatePowerLevels: moderatorPL, - event.EventReaction: defaultPL, - event.EventRedaction: defaultPL, }, }, ExcludeChangesFromTimeline: true, @@ -141,9 +123,6 @@ func (s *SignalClient) wrapGroupInfo(ctx context.Context, groupInfo *signalmeow. applyMembersAccess(members.PowerLevels, groupInfo.AccessControl.Members) joinRule = inviteLinkToJoinRule(groupInfo.AccessControl.AddFromInviteLink) } - if groupInfo.Terminated { - applyTerminated(members.PowerLevels) - } for _, member := range groupInfo.RequestingMembers { members.MemberMap.Set(bridgev2.ChatMember{ EventSender: s.makeEventSender(member.ACI), @@ -203,12 +182,9 @@ func addMemberToMap(mc map[networkid.UserID]bridgev2.ChatMember, member bridgev2 mc[member.EventSender.Sender] = member } -const currentPortalInfoVersion = 1 - func updatePortalSyncMeta(ctx context.Context, portal *bridgev2.Portal) bool { meta := portal.Metadata.(*signalid.PortalMetadata) meta.LastSync = jsontime.UnixNow() - meta.InfoVersion = currentPortalInfoVersion return true } @@ -284,8 +260,7 @@ func (s *SignalClient) groupChangeToChatInfoChange(ctx context.Context, groupID var pls *bridgev2.PowerLevelOverrides if groupChange.ModifyAnnouncementsOnly != nil || groupChange.ModifyAttributesAccess != nil || - groupChange.ModifyMemberAccess != nil || - groupChange.TerminateGroup { + groupChange.ModifyMemberAccess != nil { pls = &bridgev2.PowerLevelOverrides{Events: make(map[event.Type]int)} if groupChange.ModifyAnnouncementsOnly != nil { applyAnnouncementsOnly(pls, *groupChange.ModifyAnnouncementsOnly) @@ -296,9 +271,6 @@ func (s *SignalClient) groupChangeToChatInfoChange(ctx context.Context, groupID if groupChange.ModifyMemberAccess != nil { applyMembersAccess(pls, *groupChange.ModifyMemberAccess) } - if groupChange.TerminateGroup { - applyTerminated(pls) - } } if groupChange.ModifyAddFromInviteLinkAccess != nil { ic.ChatInfo.JoinRule = &event.JoinRulesEventContent{ diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index ca7af09..7360d67 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -37,7 +37,6 @@ import ( "maunium.net/go/mautrix/event" "go.mau.fi/mautrix-signal/pkg/libsignalgo" - "go.mau.fi/mautrix-signal/pkg/msgconv" "go.mau.fi/mautrix-signal/pkg/signalid" "go.mau.fi/mautrix-signal/pkg/signalmeow" "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" @@ -82,7 +81,7 @@ func (s *SignalClient) sendMessage(ctx context.Context, portalID networkid.Porta log.Debug().Msg("No successes or failures - Probably sent to myself") } else if len(result.SuccessfullySentTo) == 0 { log.Error().Msg("Failed to send event to all members of Signal group") - return fmt.Errorf("failed to send to any members of Signal group: %w", result.FailureError()) + return errors.New("failed to send to any members of Signal group") } else if len(result.SuccessfullySentTo) < totalRecipients { if len(result.FailedToSendTo) > 0 { log.Warn().Msg("Failed to send event to some members of Signal group") @@ -124,10 +123,10 @@ func (s *SignalClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2.Ma ctx, s.Client, msg.Portal, msg.Event, msg.Content, msg.OrigSender != nil, msg.ReplyTo, ) if err != nil { - return nil, wrapConversionError(err) + return nil, err } return s.doSendMessage(ctx, msg, converted, &signalid.MessageMetadata{ - ContainsAttachments: msgconv.ContainsAttachments(converted), + ContainsAttachments: len(converted.Attachments) > 0, ViewOnce: converted.GetIsViewOnce(), }) } @@ -147,7 +146,7 @@ func (s *SignalClient) doSendMessage( msg.AddPendingToIgnore(networkid.TransactionID(msgID)) err := s.sendMessage(ctx, msg.Portal.ID, signalmeow.WrapDataMessage(converted)) if err != nil { - return nil, wrapSendError(err) + return nil, bridgev2.WrapErrorInStatus(err).WithSendNotice(true) } dbMsg := &database.Message{ ID: msgID, @@ -170,14 +169,14 @@ func (s *SignalClient) HandleMatrixViewLimitedMedia(ctx context.Context, msg *br if err != nil { return err } - return wrapSendError(s.sendMessage(ctx, signalid.MakeDMPortalID(libsignalgo.NewACIServiceID(sender)), &signalpb.Content{ + return s.sendMessage(ctx, signalid.MakeDMPortalID(libsignalgo.NewACIServiceID(sender)), &signalpb.Content{ Content: &signalpb.Content_ReceiptMessage{ ReceiptMessage: &signalpb.ReceiptMessage{ Type: signalpb.ReceiptMessage_VIEWED.Enum(), Timestamp: []uint64{timestamp}, }, }, - })) + }) } func (s *SignalClient) HandleMatrixEdit(ctx context.Context, msg *bridgev2.MatrixEdit) error { @@ -188,7 +187,7 @@ func (s *SignalClient) HandleMatrixEdit(ctx context.Context, msg *bridgev2.Matri if err != nil { return fmt.Errorf("failed to parse target message ID: %w", err) } else if msg.EditTarget.SenderID != signalid.MakeUserID(s.Client.Store.ACI) { - return errEditOthersMessage + return fmt.Errorf("cannot edit other people's messages") } var replyTo *database.Message if msg.EditTarget.ReplyTo.MessageID != "" { @@ -199,7 +198,7 @@ func (s *SignalClient) HandleMatrixEdit(ctx context.Context, msg *bridgev2.Matri } converted, err := s.Main.MsgConv.ToSignal(ctx, s.Client, msg.Portal, msg.Event, msg.Content, msg.OrigSender != nil, replyTo) if err != nil { - return wrapConversionError(err) + return err } ts := getTimestampForEvent(msg.InputTransactionID, msg.Event, msg.OrigSender) converted.Timestamp = &ts @@ -208,11 +207,11 @@ func (s *SignalClient) HandleMatrixEdit(ctx context.Context, msg *bridgev2.Matri DataMessage: converted, })) if err != nil { - return wrapSendError(err) + return bridgev2.WrapErrorInStatus(err).WithSendNotice(true) } prevID := msg.EditTarget.ID msg.EditTarget.ID = signalid.MakeMessageID(s.Client.Store.ACI, ts) - msg.EditTarget.Metadata = &signalid.MessageMetadata{ContainsAttachments: msgconv.ContainsAttachments(converted)} + msg.EditTarget.Metadata = &signalid.MessageMetadata{ContainsAttachments: len(converted.Attachments) > 0} msg.EditTarget.EditCount++ if prevID != msg.EditTarget.ID { err = s.Main.Bridge.DB.DoTxn(ctx, nil, func(ctx context.Context) error { @@ -277,7 +276,7 @@ func (s *SignalClient) HandleMatrixReaction(ctx context.Context, msg *bridgev2.M }, })) if err != nil { - return nil, wrapSendError(err) + return nil, err } return &database.Reaction{}, nil } @@ -298,52 +297,30 @@ func (s *SignalClient) HandleMatrixReactionRemove(ctx context.Context, msg *brid TargetSentTimestamp: proto.Uint64(targetSentTimestamp), }, })) - return wrapSendError(err) + if err != nil { + return err + } + return nil } func (s *SignalClient) HandleMatrixMessageRemove(ctx context.Context, msg *bridgev2.MatrixMessageRemove) error { - targetAuthorACI, targetSentTimestamp, err := signalid.ParseMessageID(msg.TargetMessage.ID) + _, targetSentTimestamp, err := signalid.ParseMessageID(msg.TargetMessage.ID) if err != nil { return fmt.Errorf("failed to parse target message ID: %w", err) + } else if msg.TargetMessage.SenderID != signalid.MakeUserID(s.Client.Store.ACI) { + return fmt.Errorf("cannot delete other people's messages") } - dm := &signalpb.DataMessage{ - Timestamp: proto.Uint64(getTimestampForEvent(msg.InputTransactionID, msg.Event, msg.OrigSender)), - } - if msg.TargetMessage.SenderID == signalid.MakeUserID(s.Client.Store.ACI) { - dm.Delete = &signalpb.DataMessage_Delete{ + ts := getTimestampForEvent(msg.InputTransactionID, msg.Event, msg.OrigSender) + err = s.sendMessage(ctx, msg.Portal.ID, signalmeow.WrapDataMessage(&signalpb.DataMessage{ + Timestamp: proto.Uint64(ts), + Delete: &signalpb.DataMessage_Delete{ TargetSentTimestamp: proto.Uint64(targetSentTimestamp), - } - } else if isAdmin, err := s.isGroupAdmin(ctx, msg.Portal); err != nil { - return wrapSendError(err) - } else if !isAdmin { - return errDeleteOthersMessage - } else { - dm.AdminDelete = &signalpb.DataMessage_AdminDelete{ - TargetAuthorAciBinary: targetAuthorACI[:], - TargetSentTimestamp: proto.Uint64(targetSentTimestamp), - } - } - err = s.sendMessage(ctx, msg.Portal.ID, signalmeow.WrapDataMessage(dm)) - return wrapSendError(err) -} - -func (s *SignalClient) isGroupAdmin(ctx context.Context, portal *bridgev2.Portal) (bool, error) { - _, groupID, err := signalid.ParsePortalID(portal.ID) + }, + })) if err != nil { - return false, err - } else if groupID == "" { - return false, nil + return err } - group, _, err := s.Client.RetrieveGroupByID(ctx, groupID, portal.Metadata.(*signalid.PortalMetadata).Revision) - if err != nil { - return false, fmt.Errorf("failed to get group info: %w", err) - } - for _, member := range group.Members { - if member.ACI == s.Client.Store.ACI { - return member.Role == signalmeow.GroupMember_ADMINISTRATOR, nil - } - } - return false, nil + return nil } func (s *SignalClient) HandleMatrixReadReceipt(ctx context.Context, receipt *bridgev2.MatrixReadReceipt) error { @@ -426,7 +403,7 @@ func (s *SignalClient) handleMatrixRoomMeta(ctx context.Context, portal *bridgev gc.Revision = portal.Metadata.(*signalid.PortalMetadata).Revision + 1 revision, err := s.Client.UpdateGroup(ctx, gc, groupID) if err != nil { - return false, wrapSendError(err) + return false, err } if gc.ModifyTitle != nil { portal.Name = *gc.ModifyTitle @@ -468,7 +445,7 @@ func (s *SignalClient) HandleMatrixRoomAvatar(ctx context.Context, msg *bridgev2 avatarHash = sha256.Sum256(data) avatarPath, err = s.Client.UploadGroupAvatar(ctx, data, groupID, "") if err != nil { - return false, wrapSendError(fmt.Errorf("failed to reupload avatar: %w", err)) + return false, fmt.Errorf("failed to reupload avatar: %w", err) } } return s.handleMatrixRoomMeta(ctx, msg.Portal, &signalmeow.GroupChange{ @@ -495,7 +472,7 @@ func (s *SignalClient) HandleMatrixMembership(ctx context.Context, msg *bridgev2 if msg.Portal.RoomType == database.RoomTypeDM { switch msg.Type { case bridgev2.Invite: - return nil, errInviteToDM + return nil, fmt.Errorf("cannot invite additional user to dm") default: return nil, nil } @@ -624,7 +601,7 @@ func (s *SignalClient) HandleMatrixMembership(ctx context.Context, msg *bridgev2 gc.Revision = msg.Portal.Metadata.(*signalid.PortalMetadata).Revision + 1 revision, err := s.Client.UpdateGroup(ctx, gc, groupID) if err != nil { - return nil, wrapSendError(err) + return nil, err } if (msg.Type == bridgev2.Invite || msg.Type == bridgev2.AcceptKnock) && targetSignalID.Type != libsignalgo.ServiceIDTypePNI { err = targetIntent.EnsureJoined(ctx, msg.Portal.MXID) @@ -695,7 +672,7 @@ func (s *SignalClient) HandleMatrixPowerLevels(ctx context.Context, msg *bridgev } revision, err := s.Client.UpdateGroup(ctx, gc, groupID) if err != nil { - return false, wrapSendError(err) + return false, err } msg.Portal.Metadata.(*signalid.PortalMetadata).Revision = revision return true, nil @@ -746,7 +723,7 @@ func (s *SignalClient) HandleMatrixViewingChat(ctx context.Context, msg *bridgev func (s *SignalClient) HandleMatrixDisappearingTimer(ctx context.Context, msg *bridgev2.MatrixDisappearingTimer) (bool, error) { if msg.Content.Type != event.DisappearingTypeAfterRead && msg.Content.Timer.Duration != 0 { - return false, errUnsupportedDisappearingType + return false, fmt.Errorf("unsupported disappearing timer type: %s", msg.Content.Type) } userID, groupID, err := signalid.ParsePortalID(msg.Portal.ID) if err != nil { @@ -773,7 +750,7 @@ func (s *SignalClient) HandleMatrixDisappearingTimer(ctx context.Context, msg *b ExpireTimer: ptr.Ptr(uint32(msg.Content.Timer.Seconds())), })) if !res.WasSuccessful { - return false, wrapSendError(res.Error) + return false, res.Error } msg.Portal.Disappear = newSetting return true, nil @@ -798,7 +775,7 @@ func (s *SignalClient) HandleMatrixDeleteChat(ctx context.Context, msg *bridgev2 Revision: portalMeta.Revision + 1, }, groupID) if err != nil { - return wrapSendError(fmt.Errorf("failed to leave group before deleting chat: %w", err)) + return fmt.Errorf("failed to leave group before deleting chat: %w", err) } portalMeta.Revision = revision } @@ -808,7 +785,7 @@ func (s *SignalClient) HandleMatrixDeleteChat(ctx context.Context, msg *bridgev2 // TODO block and delete support? err = s.syncMessageRequestResponse(ctx, msg.Portal, signalpb.SyncMessage_MessageRequestResponse_DELETE) if err != nil { - return wrapSendError(fmt.Errorf("failed to send message request delete sync: %w", err)) + return fmt.Errorf("failed to send message request delete sync: %w", err) } } @@ -886,7 +863,7 @@ func (s *SignalClient) HandleMatrixDeleteChat(ctx context.Context, msg *bridgev2 Msg("Sent conversation deletion to Signal") if !result.WasSuccessful { - return wrapSendError(fmt.Errorf("failed to send delete conversation sync message: %w %s %s", result.Error, userID, groupID)) + return fmt.Errorf("failed to send delete conversation sync message: %w %s %s", result.Error, userID, groupID) } return nil @@ -990,7 +967,7 @@ func (s *SignalClient) HandleMatrixAcceptMessageRequest(ctx context.Context, msg } err = s.syncMessageRequestResponse(ctx, msg.Portal, signalpb.SyncMessage_MessageRequestResponse_ACCEPT) if err != nil { - return wrapSendError(fmt.Errorf("failed to sync message request acceptance: %w", err)) + return fmt.Errorf("failed to sync message request acceptance: %w", err) } if userID.Type == libsignalgo.ServiceIDTypeACI { profileKey, err := s.Client.ProfileKeyForSignalID(ctx, s.Client.Store.ACI) @@ -1019,7 +996,7 @@ func (s *SignalClient) HandleMatrixAcceptMessageRequest(ctx context.Context, msg PniSignatureMessage: pniSig, }) if !res.WasSuccessful { - return wrapSendError(fmt.Errorf("failed to share profile key to accept message request: %w", res.Error)) + return fmt.Errorf("failed to share profile key to accept message request: %w", res.Error) } // TODO send read receipts too? } @@ -1056,7 +1033,7 @@ func (s *SignalClient) HandleMatrixBlockUser(ctx context.Context, msg *bridgev2. } } if err = s.syncMessageRequestResponse(ctx, msg.Portal, respType); err != nil { - return wrapSendError(err) + return err } _, err = s.Client.Store.RecipientStore.LoadAndUpdateRecipient(ctx, aci, pni, func(recipient *types.Recipient) (bool, error) { changed := recipient.Blocked != msg.Content.Block @@ -1078,7 +1055,7 @@ func (s *SignalClient) reportSpam(ctx context.Context, portal *bridgev2.Portal, continue } if err = s.Client.ReportSpam(ctx, aci, meta.ServerGUID, meta.ReportingToken); err != nil { - return wrapSendError(err) + return err } seen[meta.ServerGUID] = true if len(seen) == 3 { diff --git a/pkg/connector/handlesignal.go b/pkg/connector/handlesignal.go index b6e746f..a25b77a 100644 --- a/pkg/connector/handlesignal.go +++ b/pkg/connector/handlesignal.go @@ -34,6 +34,7 @@ import ( "maunium.net/go/mautrix/bridgev2/database" "maunium.net/go/mautrix/bridgev2/networkid" "maunium.net/go/mautrix/bridgev2/simplevent" + "maunium.net/go/mautrix/bridgev2/status" "maunium.net/go/mautrix/event" "go.mau.fi/mautrix-signal/pkg/libsignalgo" @@ -73,9 +74,9 @@ func (s *SignalClient) handleSignalEvent(rawEvt events.SignalEvent) bool { case *events.QueueEmpty: s.queueEmptyWaiter.Set() case *events.LoggedOut: - s.sendLoggedOutState(evt.Error) + s.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateBadCredentials, Message: evt.Error.Error()}) case *events.IdentityChange: - s.handleSignalIdentityChange(evt) + // TODO send notice default: s.UserLogin.Log.Warn().Type("event_type", evt).Msg("Unrecognized signalmeow event type") } @@ -161,44 +162,6 @@ func convertDecryptionError(_ context.Context, _ *bridgev2.Portal, _ bridgev2.Ma }, nil } -func (s *SignalClient) handleSignalIdentityChange(evt *events.IdentityChange) { - if !s.Main.Config.IdentityChangeNotices || evt.ServiceID.Type != libsignalgo.ServiceIDTypeACI || evt.ServiceID.UUID == s.Client.Store.ACI { - return - } - s.UserLogin.QueueRemoteEvent(&simplevent.Message[*events.IdentityChange]{ - EventMeta: simplevent.EventMeta{ - Type: bridgev2.RemoteEventMessage, - LogContext: func(c zerolog.Context) zerolog.Context { - return c.Stringer("sender_id", evt.ServiceID) - }, - PortalKey: s.makeDMPortalKey(evt.ServiceID), - Sender: s.makeEventSender(evt.ServiceID.UUID), - }, - Data: evt, - ID: "idchange|" + signalid.MakeMessageID(evt.ServiceID.UUID, uint64(time.Now().UnixMilli())), - ConvertMessageFunc: convertIdentityChange, - }) -} - -func convertIdentityChange(ctx context.Context, portal *bridgev2.Portal, _ bridgev2.MatrixAPI, evt *events.IdentityChange) (*bridgev2.ConvertedMessage, error) { - name := evt.ServiceID.UUID.String() - ghost, err := portal.Bridge.GetGhostByID(ctx, signalid.MakeUserID(evt.ServiceID.UUID)) - if err != nil { - return nil, err - } else if ghost.Name != "" { - name = ghost.Name - } - return &bridgev2.ConvertedMessage{ - Parts: []*bridgev2.ConvertedMessagePart{{ - Type: event.EventMessage, - Content: &event.MessageEventContent{ - MsgType: event.MsgNotice, - Body: fmt.Sprintf("Your safety number with %s has changed.", name), - }, - }}, - }, nil -} - type Bv2ChatEvent struct { *events.ChatEvent s *SignalClient @@ -223,7 +186,6 @@ func (evt *Bv2ChatEvent) GetType() bridgev2.RemoteEventType { switch { case innerEvt.GetIsViewOnce(), innerEvt.Body != nil, innerEvt.Attachments != nil, innerEvt.Contact != nil, innerEvt.Sticker != nil, innerEvt.Payment != nil, innerEvt.GiftBadge != nil, innerEvt.PollCreate != nil, innerEvt.PollVote != nil, - innerEvt.PinMessage != nil, innerEvt.UnpinMessage != nil, innerEvt.GetRequiredProtocolVersion() > uint32(signalpb.DataMessage_CURRENT), innerEvt.GetFlags()&uint32(signalpb.DataMessage_EXPIRATION_TIMER_UPDATE) != 0: return bridgev2.RemoteEventMessage @@ -272,16 +234,13 @@ func (evt *Bv2ChatEvent) PreHandle(ctx context.Context, portal *bridgev2.Portal) if !ok || dataMsg.GroupV2 == nil { return } - portalMeta := portal.Metadata.(*signalid.PortalMetadata) - portalRev := portalMeta.Revision + portalRev := portal.Metadata.(*signalid.PortalMetadata).Revision if evt.Info.GroupRevision > portalRev { toRevision := evt.Info.GroupRevision if dataMsg.GetGroupV2().GetGroupChange() != nil { toRevision-- } evt.s.catchUpGroup(ctx, portal, portalRev, toRevision, dataMsg.GetTimestamp()) - } else if portalMeta.InfoVersion < currentPortalInfoVersion && portal.MXID != "" { - evt.s.catchUpGroup(ctx, portal, 0, portalRev, dataMsg.GetTimestamp()) } } diff --git a/pkg/connector/login.go b/pkg/connector/login.go index dd636b3..8c6bdab 100644 --- a/pkg/connector/login.go +++ b/pkg/connector/login.go @@ -107,7 +107,7 @@ var ( ErrDeviceLinkRateLimited = bridgev2.RespError{ ErrCode: "FI.MAU.SIGNAL.DEVICE_LINK_RATE_LIMITED", Err: "Signal rate-limited the linking attempt, please wait a few minutes and try again", - StatusCode: http.StatusBadRequest, + StatusCode: http.StatusTooManyRequests, } ErrDeviceLinkRejected = bridgev2.RespError{ ErrCode: "FI.MAU.SIGNAL.DEVICE_LINK_REJECTED", diff --git a/pkg/libsignalgo/backupkey.go b/pkg/libsignalgo/backupkey.go index 47cce90..b5f53f2 100644 --- a/pkg/libsignalgo/backupkey.go +++ b/pkg/libsignalgo/backupkey.go @@ -49,8 +49,8 @@ func (bk *BackupKey) cFixedArray() *C.SignalType_FixedArray32_uint8_t { return (*C.SignalType_FixedArray32_uint8_t)(unsafe.Pointer(bk)) } -func (bk *BackupKey) cConstFixedArray() C.SignalType_ConstPointer_FixedArray32_uint8_t { - return (C.SignalType_ConstPointer_FixedArray32_uint8_t)(unsafe.Pointer(bk)) +func (bk *BackupKey) cConstFixedArray() cFixedArray32Compat { + return cFixedArray32Compat(bk.cFixedArray()) } func GenerateRandomBackupKey() *BackupKey { diff --git a/pkg/libsignalgo/fixedarray.go b/pkg/libsignalgo/fixedarray.go index ad94514..949e808 100644 --- a/pkg/libsignalgo/fixedarray.go +++ b/pkg/libsignalgo/fixedarray.go @@ -42,118 +42,118 @@ func (a *fixedArray15) cFixedArray() *C.SignalType_FixedArray15_uint8_t { return (*C.SignalType_FixedArray15_uint8_t)(unsafe.Pointer(a)) } -func (a *fixedArray15) cConstFixedArray() C.SignalType_ConstPointer_FixedArray15_uint8_t { - return (C.SignalType_ConstPointer_FixedArray15_uint8_t)(unsafe.Pointer(a)) +func (a *fixedArray15) cConstFixedArray() cFixedArray15Compat { + return cFixedArray15Compat(a.cFixedArray()) } func (a *fixedArray16) cFixedArray() *C.SignalType_FixedArray16_uint8_t { return (*C.SignalType_FixedArray16_uint8_t)(unsafe.Pointer(a)) } -func (a *fixedArray16) cConstFixedArray() C.SignalType_ConstPointer_FixedArray16_uint8_t { - return (C.SignalType_ConstPointer_FixedArray16_uint8_t)(unsafe.Pointer(a)) +func (a *fixedArray16) cConstFixedArray() cFixedArray16Compat { + return cFixedArray16Compat(a.cFixedArray()) } func (a *fixedArray17) cFixedArray() *C.SignalType_FixedArray17_uint8_t { return (*C.SignalType_FixedArray17_uint8_t)(unsafe.Pointer(a)) } -func (a *fixedArray17) cConstFixedArray() C.SignalType_ConstPointer_FixedArray17_uint8_t { - return (C.SignalType_ConstPointer_FixedArray17_uint8_t)(unsafe.Pointer(a)) +func (a *fixedArray17) cConstFixedArray() cFixedArray17Compat { + return cFixedArray17Compat(a.cFixedArray()) } func (a *fixedArray32) cFixedArray() *C.SignalType_FixedArray32_uint8_t { return (*C.SignalType_FixedArray32_uint8_t)(unsafe.Pointer(a)) } -func (a *fixedArray32) cConstFixedArray() C.SignalType_ConstPointer_FixedArray32_uint8_t { - return (C.SignalType_ConstPointer_FixedArray32_uint8_t)(unsafe.Pointer(a)) +func (a *fixedArray32) cConstFixedArray() cFixedArray32Compat { + return cFixedArray32Compat(a.cFixedArray()) } func (a *fixedArray64) cFixedArray() *C.SignalType_FixedArray64_uint8_t { return (*C.SignalType_FixedArray64_uint8_t)(unsafe.Pointer(a)) } -func (a *fixedArray64) cConstFixedArray() C.SignalType_ConstPointer_FixedArray64_uint8_t { - return (C.SignalType_ConstPointer_FixedArray64_uint8_t)(unsafe.Pointer(a)) +func (a *fixedArray64) cConstFixedArray() cFixedArray64Compat { + return cFixedArray64Compat(a.cFixedArray()) } func (a *fixedArray65) cFixedArray() *C.SignalType_FixedArray65_uint8_t { return (*C.SignalType_FixedArray65_uint8_t)(unsafe.Pointer(a)) } -func (a *fixedArray65) cConstFixedArray() C.SignalType_ConstPointer_FixedArray65_uint8_t { - return (C.SignalType_ConstPointer_FixedArray65_uint8_t)(unsafe.Pointer(a)) +func (a *fixedArray65) cConstFixedArray() cFixedArray65Compat { + return cFixedArray65Compat(a.cFixedArray()) } func (a *fixedArray97) cFixedArray() *C.SignalType_FixedArray97_uint8_t { return (*C.SignalType_FixedArray97_uint8_t)(unsafe.Pointer(a)) } -func (a *fixedArray97) cConstFixedArray() C.SignalType_ConstPointer_FixedArray97_uint8_t { - return (C.SignalType_ConstPointer_FixedArray97_uint8_t)(unsafe.Pointer(a)) +func (a *fixedArray97) cConstFixedArray() cFixedArray97Compat { + return cFixedArray97Compat(a.cFixedArray()) } func (a *fixedArray129) cFixedArray() *C.SignalType_FixedArray129_uint8_t { return (*C.SignalType_FixedArray129_uint8_t)(unsafe.Pointer(a)) } -func (a *fixedArray129) cConstFixedArray() C.SignalType_ConstPointer_FixedArray129_uint8_t { - return (C.SignalType_ConstPointer_FixedArray129_uint8_t)(unsafe.Pointer(a)) +func (a *fixedArray129) cConstFixedArray() cFixedArray129Compat { + return cFixedArray129Compat(a.cFixedArray()) } func (a *fixedArray153) cFixedArray() *C.SignalType_FixedArray153_uint8_t { return (*C.SignalType_FixedArray153_uint8_t)(unsafe.Pointer(a)) } -func (a *fixedArray153) cConstFixedArray() C.SignalType_ConstPointer_FixedArray153_uint8_t { - return (C.SignalType_ConstPointer_FixedArray153_uint8_t)(unsafe.Pointer(a)) +func (a *fixedArray153) cConstFixedArray() cFixedArray153Compat { + return cFixedArray153Compat(a.cFixedArray()) } func (a *fixedArray177) cFixedArray() *C.SignalType_FixedArray177_uint8_t { return (*C.SignalType_FixedArray177_uint8_t)(unsafe.Pointer(a)) } -func (a *fixedArray177) cConstFixedArray() C.SignalType_ConstPointer_FixedArray177_uint8_t { - return (C.SignalType_ConstPointer_FixedArray177_uint8_t)(unsafe.Pointer(a)) +func (a *fixedArray177) cConstFixedArray() cFixedArray177Compat { + return cFixedArray177Compat(a.cFixedArray()) } func (a *fixedArray289) cFixedArray() *C.SignalType_FixedArray289_uint8_t { return (*C.SignalType_FixedArray289_uint8_t)(unsafe.Pointer(a)) } -func (a *fixedArray289) cConstFixedArray() C.SignalType_ConstPointer_FixedArray289_uint8_t { - return (C.SignalType_ConstPointer_FixedArray289_uint8_t)(unsafe.Pointer(a)) +func (a *fixedArray289) cConstFixedArray() cFixedArray289Compat { + return cFixedArray289Compat(a.cFixedArray()) } func (a *fixedArray329) cFixedArray() *C.SignalType_FixedArray329_uint8_t { return (*C.SignalType_FixedArray329_uint8_t)(unsafe.Pointer(a)) } -func (a *fixedArray329) cConstFixedArray() C.SignalType_ConstPointer_FixedArray329_uint8_t { - return (C.SignalType_ConstPointer_FixedArray329_uint8_t)(unsafe.Pointer(a)) +func (a *fixedArray329) cConstFixedArray() cFixedArray329Compat { + return cFixedArray329Compat(a.cFixedArray()) } func (a *fixedArray409) cFixedArray() *C.SignalType_FixedArray409_uint8_t { return (*C.SignalType_FixedArray409_uint8_t)(unsafe.Pointer(a)) } -func (a *fixedArray409) cConstFixedArray() C.SignalType_ConstPointer_FixedArray409_uint8_t { - return (C.SignalType_ConstPointer_FixedArray409_uint8_t)(unsafe.Pointer(a)) +func (a *fixedArray409) cConstFixedArray() cFixedArray409Compat { + return cFixedArray409Compat(a.cFixedArray()) } func (a *fixedArray473) cFixedArray() *C.SignalType_FixedArray473_uint8_t { return (*C.SignalType_FixedArray473_uint8_t)(unsafe.Pointer(a)) } -func (a *fixedArray473) cConstFixedArray() C.SignalType_ConstPointer_FixedArray473_uint8_t { - return (C.SignalType_ConstPointer_FixedArray473_uint8_t)(unsafe.Pointer(a)) +func (a *fixedArray473) cConstFixedArray() cFixedArray473Compat { + return cFixedArray473Compat(a.cFixedArray()) } func (a *fixedArray497) cFixedArray() *C.SignalType_FixedArray497_uint8_t { return (*C.SignalType_FixedArray497_uint8_t)(unsafe.Pointer(a)) } -func (a *fixedArray497) cConstFixedArray() C.SignalType_ConstPointer_FixedArray497_uint8_t { - return (C.SignalType_ConstPointer_FixedArray497_uint8_t)(unsafe.Pointer(a)) +func (a *fixedArray497) cConstFixedArray() cFixedArray497Compat { + return cFixedArray497Compat(a.cFixedArray()) } diff --git a/pkg/libsignalgo/fixedarray_clang.go b/pkg/libsignalgo/fixedarray_clang.go new file mode 100644 index 0000000..a364eb5 --- /dev/null +++ b/pkg/libsignalgo/fixedarray_clang.go @@ -0,0 +1,24 @@ +//go:build darwin || android || ios || (windows && arm64) || clang + +package libsignalgo + +/* +#include "./libsignal-ffi.h" +*/ +import "C" + +type cFixedArray15Compat = *C.SignalType_FixedArray15_uint8_t +type cFixedArray16Compat = *C.SignalType_FixedArray16_uint8_t +type cFixedArray17Compat = *C.SignalType_FixedArray17_uint8_t +type cFixedArray32Compat = *C.SignalType_FixedArray32_uint8_t +type cFixedArray64Compat = *C.SignalType_FixedArray64_uint8_t +type cFixedArray65Compat = *C.SignalType_FixedArray65_uint8_t +type cFixedArray97Compat = *C.SignalType_FixedArray97_uint8_t +type cFixedArray129Compat = *C.SignalType_FixedArray129_uint8_t +type cFixedArray153Compat = *C.SignalType_FixedArray153_uint8_t +type cFixedArray177Compat = *C.SignalType_FixedArray177_uint8_t +type cFixedArray289Compat = *C.SignalType_FixedArray289_uint8_t +type cFixedArray329Compat = *C.SignalType_FixedArray329_uint8_t +type cFixedArray409Compat = *C.SignalType_FixedArray409_uint8_t +type cFixedArray473Compat = *C.SignalType_FixedArray473_uint8_t +type cFixedArray497Compat = *C.SignalType_FixedArray497_uint8_t diff --git a/pkg/libsignalgo/fixedarray_gcc.go b/pkg/libsignalgo/fixedarray_gcc.go new file mode 100644 index 0000000..14bd230 --- /dev/null +++ b/pkg/libsignalgo/fixedarray_gcc.go @@ -0,0 +1,26 @@ +//go:build !(darwin || android || ios || (windows && arm64) || clang) + +package libsignalgo + +/* +#include "./libsignal-ffi.h" +*/ +import "C" + +// Hack for https://github.com/golang/go/issues/7270 +// The clang version is more correct, but doesn't work with gcc. +type cFixedArray15Compat = *[15]C.uint8_t +type cFixedArray16Compat = *[16]C.uint8_t +type cFixedArray17Compat = *[17]C.uint8_t +type cFixedArray32Compat = *[32]C.uint8_t +type cFixedArray64Compat = *[64]C.uint8_t +type cFixedArray65Compat = *[65]C.uint8_t +type cFixedArray97Compat = *[97]C.uint8_t +type cFixedArray129Compat = *[129]C.uint8_t +type cFixedArray153Compat = *[153]C.uint8_t +type cFixedArray177Compat = *[177]C.uint8_t +type cFixedArray289Compat = *[289]C.uint8_t +type cFixedArray329Compat = *[329]C.uint8_t +type cFixedArray409Compat = *[409]C.uint8_t +type cFixedArray473Compat = *[473]C.uint8_t +type cFixedArray497Compat = *[497]C.uint8_t diff --git a/pkg/libsignalgo/groupsecretparams.go b/pkg/libsignalgo/groupsecretparams.go index 9402d0c..90149b4 100644 --- a/pkg/libsignalgo/groupsecretparams.go +++ b/pkg/libsignalgo/groupsecretparams.go @@ -57,24 +57,24 @@ func (gmk *GroupMasterKey) cFixedArray() *C.SignalType_FixedArray32_uint8_t { return (*C.SignalType_FixedArray32_uint8_t)(unsafe.Pointer(gmk)) } -func (gmk *GroupMasterKey) cConstFixedArray() C.SignalType_ConstPointer_FixedArray32_uint8_t { - return (C.SignalType_ConstPointer_FixedArray32_uint8_t)(unsafe.Pointer(gmk)) +func (gmk *GroupMasterKey) cConstFixedArray() cFixedArray32Compat { + return cFixedArray32Compat(gmk.cFixedArray()) } func (gsp *GroupSecretParams) cFixedArray() *C.SignalType_FixedArray289_uint8_t { return (*C.SignalType_FixedArray289_uint8_t)(unsafe.Pointer(gsp)) } -func (gsp *GroupSecretParams) cConstFixedArray() C.SignalType_ConstPointer_FixedArray289_uint8_t { - return (C.SignalType_ConstPointer_FixedArray289_uint8_t)(unsafe.Pointer(gsp)) +func (gsp *GroupSecretParams) cConstFixedArray() cFixedArray289Compat { + return cFixedArray289Compat(gsp.cFixedArray()) } func (gid *GroupIdentifier) cFixedArray() *C.SignalType_FixedArray32_uint8_t { return (*C.SignalType_FixedArray32_uint8_t)(unsafe.Pointer(gid)) } -func (gid *GroupIdentifier) cConstFixedArray() C.SignalType_ConstPointer_FixedArray32_uint8_t { - return (C.SignalType_ConstPointer_FixedArray32_uint8_t)(unsafe.Pointer(gid)) +func (gid *GroupIdentifier) cConstFixedArray() cFixedArray32Compat { + return cFixedArray32Compat(gid.cFixedArray()) } func (gid *GroupIdentifier) String() string { diff --git a/pkg/libsignalgo/identitykeystore.go b/pkg/libsignalgo/identitykeystore.go index eb80056..ba26f06 100644 --- a/pkg/libsignalgo/identitykeystore.go +++ b/pkg/libsignalgo/identitykeystore.go @@ -90,8 +90,12 @@ func signal_get_local_registration_id_callback(storeCtx unsafe.Pointer, idp *C.u //export signal_save_identity_key_callback func signal_save_identity_key_callback(storeCtx unsafe.Pointer, out *C.uint8_t, address C.SignalMutPointerProtocolAddress, publicKey C.SignalMutPointerPublicKey) C.int { return wrapStoreCallback(storeCtx, func(store IdentityKeyStore, ctx context.Context) error { - addr := wrapAddress(address.raw) - pub := wrapPublicKey(publicKey.raw) + publicKeyStruct := PublicKey{ptr: publicKey.raw} + cloned, err := publicKeyStruct.Clone() + if err != nil { + return err + } + addr := &Address{ptr: address.raw} theirServiceID, err := addr.NameServiceID() if err != nil { return err @@ -99,7 +103,7 @@ func signal_save_identity_key_callback(storeCtx unsafe.Pointer, out *C.uint8_t, replaced, err := store.SaveIdentityKey( ctx, theirServiceID, - &IdentityKey{pub}, + &IdentityKey{cloned}, ) if err != nil { return err @@ -116,7 +120,7 @@ func signal_save_identity_key_callback(storeCtx unsafe.Pointer, out *C.uint8_t, //export signal_get_identity_key_callback func signal_get_identity_key_callback(storeCtx unsafe.Pointer, public_keyp *C.SignalMutPointerPublicKey, address C.SignalMutPointerProtocolAddress) C.int { return wrapStoreCallback(storeCtx, func(store IdentityKeyStore, ctx context.Context) error { - addr := wrapAddress(address.raw) + addr := &Address{ptr: address.raw} theirServiceID, err := addr.NameServiceID() if err != nil { return err @@ -133,13 +137,12 @@ func signal_get_identity_key_callback(storeCtx unsafe.Pointer, public_keyp *C.Si //export signal_is_trusted_identity_callback func signal_is_trusted_identity_callback(storeCtx unsafe.Pointer, out *C.bool, address C.SignalMutPointerProtocolAddress, public_key C.SignalMutPointerPublicKey, direction C.uint32_t) C.int { return wrapStoreCallback(storeCtx, func(store IdentityKeyStore, ctx context.Context) error { - addr := wrapAddress(address.raw) - pub := wrapPublicKey(public_key.raw) + addr := &Address{ptr: address.raw} theirServiceID, err := addr.NameServiceID() if err != nil { return err } - trusted, err := store.IsTrustedIdentity(ctx, theirServiceID, &IdentityKey{pub}, SignalDirection(direction)) + trusted, err := store.IsTrustedIdentity(ctx, theirServiceID, &IdentityKey{&PublicKey{ptr: public_key.raw}}, SignalDirection(direction)) if err != nil { return err } diff --git a/pkg/libsignalgo/kyberprekeystore.go b/pkg/libsignalgo/kyberprekeystore.go index 78ec925..9deea17 100644 --- a/pkg/libsignalgo/kyberprekeystore.go +++ b/pkg/libsignalgo/kyberprekeystore.go @@ -52,15 +52,18 @@ func signal_load_kyber_pre_key_callback(storeCtx unsafe.Pointer, keyp *C.SignalM //export signal_store_kyber_pre_key_callback func signal_store_kyber_pre_key_callback(storeCtx unsafe.Pointer, id C.uint32_t, preKeyRecord C.SignalMutPointerKyberPreKeyRecord) C.int { return wrapStoreCallback(storeCtx, func(store KyberPreKeyStore, ctx context.Context) error { - record := wrapKyberPreKeyRecord(preKeyRecord.raw) - return store.StoreKyberPreKey(ctx, uint32(id), record) + record := KyberPreKeyRecord{ptr: preKeyRecord.raw} + cloned, err := record.Clone() + if err != nil { + return err + } + return store.StoreKyberPreKey(ctx, uint32(id), cloned) }) } //export signal_mark_kyber_pre_key_used_callback func signal_mark_kyber_pre_key_used_callback(storeCtx unsafe.Pointer, id C.uint32_t, ecPrekeyID C.uint32_t, baseKey C.SignalMutPointerPublicKey) C.int { return wrapStoreCallback(storeCtx, func(store KyberPreKeyStore, ctx context.Context) error { - _ = wrapPublicKey(baseKey.raw) // TODO use ecPrekeyID and baseKey? return store.MarkKyberPreKeyUsed(ctx, uint32(id)) }) diff --git a/pkg/libsignalgo/libsignal b/pkg/libsignalgo/libsignal index 904c8dc..4beb029 160000 --- a/pkg/libsignalgo/libsignal +++ b/pkg/libsignalgo/libsignal @@ -1 +1 @@ -Subproject commit 904c8dc8216073b078a5f2f5090e3da8382e6a92 +Subproject commit 4beb029d8a941f81e7d9c6d8af1ed25a677569a8 diff --git a/pkg/libsignalgo/libsignal-ffi.h b/pkg/libsignalgo/libsignal-ffi.h index d8e981e..b8cae20 100644 --- a/pkg/libsignalgo/libsignal-ffi.h +++ b/pkg/libsignalgo/libsignal-ffi.h @@ -520,22 +520,6 @@ static_assert_64bit(alignof(SignalType_ConstPointer_SignalOwnedBufferOfc_uchar17 typedef const SignalOwnedBuffer* SignalType_ConstPointer_SignalOwnedBuffer; static_assert_64bit(sizeof(SignalType_ConstPointer_SignalOwnedBuffer) == 8); static_assert_64bit(alignof(SignalType_ConstPointer_SignalOwnedBuffer) == 8); -typedef SignalOwnedBuffer* SignalType_MutPointer_SignalOwnedBuffer; -static_assert_64bit(sizeof(SignalType_MutPointer_SignalOwnedBuffer) == 8); -static_assert_64bit(alignof(SignalType_MutPointer_SignalOwnedBuffer) == 8); -typedef struct { - SignalOwnedBuffer* base; - size_t length; - size_t size_bytes; -} SignalOwnedBufferOfMaxAlignedOwnedBuffer; -static_assert_64bit(offsetof(SignalOwnedBufferOfMaxAlignedOwnedBuffer, base) == 0); -static_assert_64bit(offsetof(SignalOwnedBufferOfMaxAlignedOwnedBuffer, length) == 8); -static_assert_64bit(offsetof(SignalOwnedBufferOfMaxAlignedOwnedBuffer, size_bytes) == 16); -static_assert_64bit(sizeof(SignalOwnedBufferOfMaxAlignedOwnedBuffer) == 24); -static_assert_64bit(alignof(SignalOwnedBufferOfMaxAlignedOwnedBuffer) == 8); -typedef const SignalOwnedBufferOfMaxAlignedOwnedBuffer* SignalType_ConstPointer_SignalOwnedBufferOfMaxAlignedOwnedBuffer; -static_assert_64bit(sizeof(SignalType_ConstPointer_SignalOwnedBufferOfMaxAlignedOwnedBuffer) == 8); -static_assert_64bit(alignof(SignalType_ConstPointer_SignalOwnedBufferOfMaxAlignedOwnedBuffer) == 8); typedef enum { SignalAuthCheckResultFfiResultMatch, SignalAuthCheckResultFfiResultNoMatch, @@ -623,33 +607,6 @@ static_assert_64bit(alignof(SignalOwnedBufferOfMaxAlignedBridgeConfirmedMfaKeyFf typedef const SignalOwnedBufferOfMaxAlignedBridgeConfirmedMfaKeyFfiResult* SignalType_ConstPointer_SignalOwnedBufferOfMaxAlignedBridgeConfirmedMfaKeyFfiResult; static_assert_64bit(sizeof(SignalType_ConstPointer_SignalOwnedBufferOfMaxAlignedBridgeConfirmedMfaKeyFfiResult) == 8); static_assert_64bit(alignof(SignalType_ConstPointer_SignalOwnedBufferOfMaxAlignedBridgeConfirmedMfaKeyFfiResult) == 8); -typedef enum { - SignalDeviceCapabilityInternalFfiResultStorage, - SignalDeviceCapabilityInternalFfiResultTransfer, - SignalDeviceCapabilityInternalFfiResultAttachmentBackfill, - SignalDeviceCapabilityInternalFfiResultSparsePostQuantumRatchet, - SignalDeviceCapabilityInternalFfiResultProfilesV2, - SignalDeviceCapabilityInternalFfiResultUsernameChangeSyncMessage, - SignalDeviceCapabilityInternalFfiResultOptionalPhoneNumber, -} SignalDeviceCapabilityInternalFfiResult; -static_assert_64bit(sizeof(SignalDeviceCapabilityInternalFfiResult) == 4); -static_assert_64bit(alignof(SignalDeviceCapabilityInternalFfiResult) == 4); -typedef SignalDeviceCapabilityInternalFfiResult* SignalType_MutPointer_SignalDeviceCapabilityInternalFfiResult; -static_assert_64bit(sizeof(SignalType_MutPointer_SignalDeviceCapabilityInternalFfiResult) == 8); -static_assert_64bit(alignof(SignalType_MutPointer_SignalDeviceCapabilityInternalFfiResult) == 8); -typedef struct { - SignalDeviceCapabilityInternalFfiResult* base; - size_t length; - size_t size_bytes; -} SignalOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult; -static_assert_64bit(offsetof(SignalOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult, base) == 0); -static_assert_64bit(offsetof(SignalOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult, length) == 8); -static_assert_64bit(offsetof(SignalOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult, size_bytes) == 16); -static_assert_64bit(sizeof(SignalOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult) == 24); -static_assert_64bit(alignof(SignalOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult) == 8); -typedef const SignalOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult* SignalType_ConstPointer_SignalOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult; -static_assert_64bit(sizeof(SignalType_ConstPointer_SignalOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult) == 8); -static_assert_64bit(alignof(SignalType_ConstPointer_SignalOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult) == 8); typedef struct { uint8_t id; SignalOwnedBuffer encrypted_name; @@ -912,6 +869,19 @@ static_assert_64bit(offsetof(SignalOwnedBufferOfMaxAlignedi32, length) == 8); static_assert_64bit(offsetof(SignalOwnedBufferOfMaxAlignedi32, size_bytes) == 16); static_assert_64bit(sizeof(SignalOwnedBufferOfMaxAlignedi32) == 24); static_assert_64bit(alignof(SignalOwnedBufferOfMaxAlignedi32) == 8); +typedef SignalOwnedBuffer* SignalType_MutPointer_SignalOwnedBuffer; +static_assert_64bit(sizeof(SignalType_MutPointer_SignalOwnedBuffer) == 8); +static_assert_64bit(alignof(SignalType_MutPointer_SignalOwnedBuffer) == 8); +typedef struct { + SignalOwnedBuffer* base; + size_t length; + size_t size_bytes; +} SignalOwnedBufferOfMaxAlignedOwnedBuffer; +static_assert_64bit(offsetof(SignalOwnedBufferOfMaxAlignedOwnedBuffer, base) == 0); +static_assert_64bit(offsetof(SignalOwnedBufferOfMaxAlignedOwnedBuffer, length) == 8); +static_assert_64bit(offsetof(SignalOwnedBufferOfMaxAlignedOwnedBuffer, size_bytes) == 16); +static_assert_64bit(sizeof(SignalOwnedBufferOfMaxAlignedOwnedBuffer) == 24); +static_assert_64bit(alignof(SignalOwnedBufferOfMaxAlignedOwnedBuffer) == 8); typedef struct { SignalOwnedBuffer user_handle; SignalOwnedBufferOfMaxAlignedi32 allowed_algorithms; @@ -1102,59 +1072,6 @@ static_assert_64bit(alignof(SignalType_ConstPointer_SignalServerMessageAck) == 8 typedef const SignalUnauthenticatedChatConnection* SignalType_ConstPointer_SignalUnauthenticatedChatConnection; static_assert_64bit(sizeof(SignalType_ConstPointer_SignalUnauthenticatedChatConnection) == 8); static_assert_64bit(alignof(SignalType_ConstPointer_SignalUnauthenticatedChatConnection) == 8); -typedef struct { - int64_t level; - uint64_t expiration; -} SignalBackupEntitlementFfiResult; -static_assert_64bit(offsetof(SignalBackupEntitlementFfiResult, level) == 0); -static_assert_64bit(offsetof(SignalBackupEntitlementFfiResult, expiration) == 8); -static_assert_64bit(sizeof(SignalBackupEntitlementFfiResult) == 16); -static_assert_64bit(alignof(SignalBackupEntitlementFfiResult) == 8); -typedef SignalBackupEntitlementFfiResult MaybeUninitOfBackupEntitlementFfiResult; -static_assert_64bit(sizeof(MaybeUninitOfBackupEntitlementFfiResult) == 16); -static_assert_64bit(alignof(MaybeUninitOfBackupEntitlementFfiResult) == 8); -typedef struct { - bool present; - MaybeUninitOfBackupEntitlementFfiResult value; -} SignalOptionalOfBackupEntitlementFfiResult; -static_assert_64bit(offsetof(SignalOptionalOfBackupEntitlementFfiResult, present) == 0); -static_assert_64bit(offsetof(SignalOptionalOfBackupEntitlementFfiResult, value) == 8); -static_assert_64bit(sizeof(SignalOptionalOfBackupEntitlementFfiResult) == 24); -static_assert_64bit(alignof(SignalOptionalOfBackupEntitlementFfiResult) == 8); -typedef struct { - const int8_t* badge_id; - uint64_t expiration; - bool visible; -} SignalBadgeEntitlementFfiResult; -static_assert_64bit(offsetof(SignalBadgeEntitlementFfiResult, badge_id) == 0); -static_assert_64bit(offsetof(SignalBadgeEntitlementFfiResult, expiration) == 8); -static_assert_64bit(offsetof(SignalBadgeEntitlementFfiResult, visible) == 16); -static_assert_64bit(sizeof(SignalBadgeEntitlementFfiResult) == 24); -static_assert_64bit(alignof(SignalBadgeEntitlementFfiResult) == 8); -typedef SignalBadgeEntitlementFfiResult* SignalType_MutPointer_SignalBadgeEntitlementFfiResult; -static_assert_64bit(sizeof(SignalType_MutPointer_SignalBadgeEntitlementFfiResult) == 8); -static_assert_64bit(alignof(SignalType_MutPointer_SignalBadgeEntitlementFfiResult) == 8); -typedef struct { - SignalBadgeEntitlementFfiResult* base; - size_t length; - size_t size_bytes; -} SignalOwnedBufferOfMaxAlignedBadgeEntitlementFfiResult; -static_assert_64bit(offsetof(SignalOwnedBufferOfMaxAlignedBadgeEntitlementFfiResult, base) == 0); -static_assert_64bit(offsetof(SignalOwnedBufferOfMaxAlignedBadgeEntitlementFfiResult, length) == 8); -static_assert_64bit(offsetof(SignalOwnedBufferOfMaxAlignedBadgeEntitlementFfiResult, size_bytes) == 16); -static_assert_64bit(sizeof(SignalOwnedBufferOfMaxAlignedBadgeEntitlementFfiResult) == 24); -static_assert_64bit(alignof(SignalOwnedBufferOfMaxAlignedBadgeEntitlementFfiResult) == 8); -typedef struct { - SignalOwnedBufferOfMaxAlignedBadgeEntitlementFfiResult badges; - SignalOptionalOfBackupEntitlementFfiResult backup; -} SignalAccountEntitlementsFfiResult; -static_assert_64bit(offsetof(SignalAccountEntitlementsFfiResult, badges) == 0); -static_assert_64bit(offsetof(SignalAccountEntitlementsFfiResult, backup) == 24); -static_assert_64bit(sizeof(SignalAccountEntitlementsFfiResult) == 48); -static_assert_64bit(alignof(SignalAccountEntitlementsFfiResult) == 8); -typedef const SignalAccountEntitlementsFfiResult* SignalType_ConstPointer_SignalAccountEntitlementsFfiResult; -static_assert_64bit(sizeof(SignalType_ConstPointer_SignalAccountEntitlementsFfiResult) == 8); -static_assert_64bit(alignof(SignalType_ConstPointer_SignalAccountEntitlementsFfiResult) == 8); typedef SignalPairOfCStringPtrCStringPtr* SignalType_MutPointer_SignalPairOfCStringPtrCStringPtr; static_assert_64bit(sizeof(SignalType_MutPointer_SignalPairOfCStringPtrCStringPtr) == 8); static_assert_64bit(alignof(SignalType_MutPointer_SignalPairOfCStringPtrCStringPtr) == 8); @@ -2750,22 +2667,6 @@ static_assert_64bit(alignof(SignalCPromiseOwnedBuffer) == 8); typedef SignalCPromiseOwnedBuffer* SignalType_MutPointer_SignalCPromiseOwnedBuffer; static_assert_64bit(sizeof(SignalType_MutPointer_SignalCPromiseOwnedBuffer) == 8); static_assert_64bit(alignof(SignalType_MutPointer_SignalCPromiseOwnedBuffer) == 8); -typedef void (*SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalOwnedBufferOfMaxAlignedOwnedBuffer_ConstPointer_void)(SignalType_MutPointer_SignalFfiError, SignalType_ConstPointer_SignalOwnedBufferOfMaxAlignedOwnedBuffer, SignalType_ConstPointer_void); -static_assert_64bit(sizeof(SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalOwnedBufferOfMaxAlignedOwnedBuffer_ConstPointer_void) == 8); -static_assert_64bit(alignof(SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalOwnedBufferOfMaxAlignedOwnedBuffer_ConstPointer_void) == 8); -typedef struct { - SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalOwnedBufferOfMaxAlignedOwnedBuffer_ConstPointer_void complete; - const void* context; - uint64_t cancellation_id; -} SignalCPromiseOwnedBufferOfMaxAlignedOwnedBuffer; -static_assert_64bit(offsetof(SignalCPromiseOwnedBufferOfMaxAlignedOwnedBuffer, complete) == 0); -static_assert_64bit(offsetof(SignalCPromiseOwnedBufferOfMaxAlignedOwnedBuffer, context) == 8); -static_assert_64bit(offsetof(SignalCPromiseOwnedBufferOfMaxAlignedOwnedBuffer, cancellation_id) == 16); -static_assert_64bit(sizeof(SignalCPromiseOwnedBufferOfMaxAlignedOwnedBuffer) == 24); -static_assert_64bit(alignof(SignalCPromiseOwnedBufferOfMaxAlignedOwnedBuffer) == 8); -typedef SignalCPromiseOwnedBufferOfMaxAlignedOwnedBuffer* SignalType_MutPointer_SignalCPromiseOwnedBufferOfMaxAlignedOwnedBuffer; -static_assert_64bit(sizeof(SignalType_MutPointer_SignalCPromiseOwnedBufferOfMaxAlignedOwnedBuffer) == 8); -static_assert_64bit(alignof(SignalType_MutPointer_SignalCPromiseOwnedBufferOfMaxAlignedOwnedBuffer) == 8); typedef void (*SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalOwnedBufferOfMaxAlignedPairOfCStringPtrAuthCheckResultFfiResult_ConstPointer_void)(SignalType_MutPointer_SignalFfiError, SignalType_ConstPointer_SignalOwnedBufferOfMaxAlignedPairOfCStringPtrAuthCheckResultFfiResult, SignalType_ConstPointer_void); static_assert_64bit(sizeof(SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalOwnedBufferOfMaxAlignedPairOfCStringPtrAuthCheckResultFfiResult_ConstPointer_void) == 8); static_assert_64bit(alignof(SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalOwnedBufferOfMaxAlignedPairOfCStringPtrAuthCheckResultFfiResult_ConstPointer_void) == 8); @@ -2798,22 +2699,6 @@ static_assert_64bit(alignof(SignalCPromiseOwnedBufferOfMaxAlignedBridgeConfirmed typedef SignalCPromiseOwnedBufferOfMaxAlignedBridgeConfirmedMfaKeyFfiResult* SignalType_MutPointer_SignalCPromiseOwnedBufferOfMaxAlignedBridgeConfirmedMfaKeyFfiResult; static_assert_64bit(sizeof(SignalType_MutPointer_SignalCPromiseOwnedBufferOfMaxAlignedBridgeConfirmedMfaKeyFfiResult) == 8); static_assert_64bit(alignof(SignalType_MutPointer_SignalCPromiseOwnedBufferOfMaxAlignedBridgeConfirmedMfaKeyFfiResult) == 8); -typedef void (*SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult_ConstPointer_void)(SignalType_MutPointer_SignalFfiError, SignalType_ConstPointer_SignalOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult, SignalType_ConstPointer_void); -static_assert_64bit(sizeof(SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult_ConstPointer_void) == 8); -static_assert_64bit(alignof(SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult_ConstPointer_void) == 8); -typedef struct { - SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult_ConstPointer_void complete; - const void* context; - uint64_t cancellation_id; -} SignalCPromiseOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult; -static_assert_64bit(offsetof(SignalCPromiseOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult, complete) == 0); -static_assert_64bit(offsetof(SignalCPromiseOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult, context) == 8); -static_assert_64bit(offsetof(SignalCPromiseOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult, cancellation_id) == 16); -static_assert_64bit(sizeof(SignalCPromiseOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult) == 24); -static_assert_64bit(alignof(SignalCPromiseOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult) == 8); -typedef SignalCPromiseOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult* SignalType_MutPointer_SignalCPromiseOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult; -static_assert_64bit(sizeof(SignalType_MutPointer_SignalCPromiseOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult) == 8); -static_assert_64bit(alignof(SignalType_MutPointer_SignalCPromiseOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult) == 8); typedef void (*SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalOwnedBufferOfMaxAlignedLinkedDeviceInternalFfiResult_ConstPointer_void)(SignalType_MutPointer_SignalFfiError, SignalType_ConstPointer_SignalOwnedBufferOfMaxAlignedLinkedDeviceInternalFfiResult, SignalType_ConstPointer_void); static_assert_64bit(sizeof(SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalOwnedBufferOfMaxAlignedLinkedDeviceInternalFfiResult_ConstPointer_void) == 8); static_assert_64bit(alignof(SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalOwnedBufferOfMaxAlignedLinkedDeviceInternalFfiResult_ConstPointer_void) == 8); @@ -3006,22 +2891,6 @@ static_assert_64bit(alignof(SignalCPromiseDeleteBackupMediaNextChunkFfiResult) = typedef SignalCPromiseDeleteBackupMediaNextChunkFfiResult* SignalType_MutPointer_SignalCPromiseDeleteBackupMediaNextChunkFfiResult; static_assert_64bit(sizeof(SignalType_MutPointer_SignalCPromiseDeleteBackupMediaNextChunkFfiResult) == 8); static_assert_64bit(alignof(SignalType_MutPointer_SignalCPromiseDeleteBackupMediaNextChunkFfiResult) == 8); -typedef void (*SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalAccountEntitlementsFfiResult_ConstPointer_void)(SignalType_MutPointer_SignalFfiError, SignalType_ConstPointer_SignalAccountEntitlementsFfiResult, SignalType_ConstPointer_void); -static_assert_64bit(sizeof(SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalAccountEntitlementsFfiResult_ConstPointer_void) == 8); -static_assert_64bit(alignof(SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalAccountEntitlementsFfiResult_ConstPointer_void) == 8); -typedef struct { - SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalAccountEntitlementsFfiResult_ConstPointer_void complete; - const void* context; - uint64_t cancellation_id; -} SignalCPromiseAccountEntitlementsFfiResult; -static_assert_64bit(offsetof(SignalCPromiseAccountEntitlementsFfiResult, complete) == 0); -static_assert_64bit(offsetof(SignalCPromiseAccountEntitlementsFfiResult, context) == 8); -static_assert_64bit(offsetof(SignalCPromiseAccountEntitlementsFfiResult, cancellation_id) == 16); -static_assert_64bit(sizeof(SignalCPromiseAccountEntitlementsFfiResult) == 24); -static_assert_64bit(alignof(SignalCPromiseAccountEntitlementsFfiResult) == 8); -typedef SignalCPromiseAccountEntitlementsFfiResult* SignalType_MutPointer_SignalCPromiseAccountEntitlementsFfiResult; -static_assert_64bit(sizeof(SignalType_MutPointer_SignalCPromiseAccountEntitlementsFfiResult) == 8); -static_assert_64bit(alignof(SignalType_MutPointer_SignalCPromiseAccountEntitlementsFfiResult) == 8); typedef void (*SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalCurrencyConversionsInternalFfiResult_ConstPointer_void)(SignalType_MutPointer_SignalFfiError, SignalType_ConstPointer_SignalCurrencyConversionsInternalFfiResult, SignalType_ConstPointer_void); static_assert_64bit(sizeof(SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalCurrencyConversionsInternalFfiResult_ConstPointer_void) == 8); static_assert_64bit(alignof(SignalType_FunctionPointer_void_MutPointer_SignalFfiError_ConstPointer_SignalCurrencyConversionsInternalFfiResult_ConstPointer_void) == 8); @@ -3741,7 +3610,6 @@ typedef enum { SignalErrorCodeMfaKeyNotFound = 237, SignalErrorCodeWebAuthnRegistrationUnsuccessful = 238, SignalErrorCodeProfileNotFound = 239, - SignalErrorCodeReceiptAlreadyRedeemed = 240, } SignalErrorCode; static_assert_64bit(sizeof(SignalErrorCode) == 4); static_assert_64bit(alignof(SignalErrorCode) == 4); @@ -3765,13 +3633,6 @@ enum SignalSvr2CredentialsResult { typedef uint8_t SignalSvr2CredentialsResult; static_assert_64bit(sizeof(SignalSvr2CredentialsResult) == 1); static_assert_64bit(alignof(SignalSvr2CredentialsResult) == 1); -typedef enum { - SignalBadgeVisibilityFfiArgDoNotSet, - SignalBadgeVisibilityFfiArgSetVisible, - SignalBadgeVisibilityFfiArgSetPrimary, -} SignalBadgeVisibilityFfiArg; -static_assert_64bit(sizeof(SignalBadgeVisibilityFfiArg) == 4); -static_assert_64bit(alignof(SignalBadgeVisibilityFfiArg) == 4); typedef enum { SignalBridgeMfaVerificationCredentialFfiArgTotp, SignalBridgeMfaVerificationCredentialFfiArgWebAuthn, @@ -4054,13 +3915,6 @@ SignalFfiError* signal_authenticated_chat_connection_connect( bool receive_stories, SignalBorrowedBytestringArray languages ); -SignalFfiError* signal_authenticated_chat_connection_create_donation_permits( - SignalCPromiseOwnedBufferOfMaxAlignedOwnedBuffer* promise, - SignalConstPointerTokioAsyncContext async_runtime, - SignalConstPointerAuthenticatedChatConnection chat, - SignalBorrowedBuffer request_context, - SignalConstPointerServerPublicParams server_params -); SignalFfiError* signal_authenticated_chat_connection_delete_account( SignalCPromisebool* promise, SignalConstPointerTokioAsyncContext async_runtime, @@ -4106,11 +3960,6 @@ SignalFfiError* signal_authenticated_chat_connection_generate_totp_key( SignalConstPointerTokioAsyncContext async_runtime, SignalConstPointerAuthenticatedChatConnection chat ); -SignalFfiError* signal_authenticated_chat_connection_get_capabilities( - SignalCPromiseOwnedBufferOfMaxAlignedDeviceCapabilityInternalFfiResult* promise, - SignalConstPointerTokioAsyncContext async_runtime, - SignalConstPointerAuthenticatedChatConnection chat -); SignalFfiError* signal_authenticated_chat_connection_get_currency_conversions( SignalCPromiseCurrencyConversionsInternalFfiResult* promise, SignalConstPointerTokioAsyncContext async_runtime, @@ -4121,11 +3970,6 @@ SignalFfiError* signal_authenticated_chat_connection_get_devices( SignalConstPointerTokioAsyncContext async_runtime, SignalConstPointerAuthenticatedChatConnection chat ); -SignalFfiError* signal_authenticated_chat_connection_get_entitlements( - SignalCPromiseAccountEntitlementsFfiResult* promise, - SignalConstPointerTokioAsyncContext async_runtime, - SignalConstPointerAuthenticatedChatConnection chat -); SignalFfiError* signal_authenticated_chat_connection_get_pre_key_count( SignalCPromiseBridgePreKeyCountsFfiResult* promise, SignalConstPointerTokioAsyncContext async_runtime, @@ -4168,13 +4012,6 @@ SignalFfiError* signal_authenticated_chat_connection_redeem_backup_receipt( SignalConstPointerAuthenticatedChatConnection chat, const SignalType_FixedArray329_uint8_t* presentation ); -SignalFfiError* signal_authenticated_chat_connection_redeem_donation_receipt( - SignalCPromisebool* promise, - SignalConstPointerTokioAsyncContext async_runtime, - SignalConstPointerAuthenticatedChatConnection chat, - const SignalType_FixedArray329_uint8_t* presentation, - SignalBadgeVisibilityFfiArg visibility -); SignalFfiError* signal_authenticated_chat_connection_remove_device( SignalCPromisebool* promise, SignalConstPointerTokioAsyncContext async_runtime, @@ -5022,7 +4859,6 @@ SignalFfiError* signal_expiring_profile_key_credential_get_expiration_time( SignalFfiError* signal_expiring_profile_key_credential_response_check_valid_contents( SignalBorrowedBuffer buffer ); -void signal_ffi_version_0_106_1(void); SignalFfiError* signal_fingerprint_clone( SignalMutPointerFingerprint* new_obj, SignalConstPointerFingerprint obj diff --git a/pkg/libsignalgo/prekeystore.go b/pkg/libsignalgo/prekeystore.go index 00ee50f..8c3c36f 100644 --- a/pkg/libsignalgo/prekeystore.go +++ b/pkg/libsignalgo/prekeystore.go @@ -52,8 +52,12 @@ func signal_load_pre_key_callback(storeCtx unsafe.Pointer, keyp *C.SignalMutPoin //export signal_store_pre_key_callback func signal_store_pre_key_callback(storeCtx unsafe.Pointer, id C.uint32_t, preKeyRecord C.SignalMutPointerPreKeyRecord) C.int { return wrapStoreCallback(storeCtx, func(store PreKeyStore, ctx context.Context) error { - record := wrapPreKeyRecord(preKeyRecord.raw) - return store.StorePreKey(ctx, uint32(id), record) + record := PreKeyRecord{ptr: preKeyRecord.raw} + cloned, err := record.Clone() + if err != nil { + return err + } + return store.StorePreKey(ctx, uint32(id), cloned) }) } diff --git a/pkg/libsignalgo/profilekey.go b/pkg/libsignalgo/profilekey.go index a39eaa5..0c21aff 100644 --- a/pkg/libsignalgo/profilekey.go +++ b/pkg/libsignalgo/profilekey.go @@ -46,24 +46,24 @@ func (pk *ProfileKey) cFixedArray() *C.SignalType_FixedArray32_uint8_t { return (*C.SignalType_FixedArray32_uint8_t)(unsafe.Pointer(pk)) } -func (pk *ProfileKey) cConstFixedArray() C.SignalType_ConstPointer_FixedArray32_uint8_t { - return (C.SignalType_ConstPointer_FixedArray32_uint8_t)(unsafe.Pointer(pk)) +func (pk *ProfileKey) cConstFixedArray() cFixedArray32Compat { + return cFixedArray32Compat(pk.cFixedArray()) } func (pkv *ProfileKeyVersion) cFixedArray() *C.SignalType_FixedArray64_uint8_t { return (*C.SignalType_FixedArray64_uint8_t)(unsafe.Pointer(pkv)) } -func (pkv *ProfileKeyVersion) cConstFixedArray() C.SignalType_ConstPointer_FixedArray64_uint8_t { - return (C.SignalType_ConstPointer_FixedArray64_uint8_t)(unsafe.Pointer(pkv)) +func (pkv *ProfileKeyVersion) cConstFixedArray() cFixedArray64Compat { + return cFixedArray64Compat(pkv.cFixedArray()) } func (ak *AccessKey) cFixedArray() *C.SignalType_FixedArray16_uint8_t { return (*C.SignalType_FixedArray16_uint8_t)(unsafe.Pointer(ak)) } -func (ak *AccessKey) cConstFixedArray() C.SignalType_ConstPointer_FixedArray16_uint8_t { - return (C.SignalType_ConstPointer_FixedArray16_uint8_t)(unsafe.Pointer(ak)) +func (ak *AccessKey) cConstFixedArray() cFixedArray16Compat { + return cFixedArray16Compat(ak.cFixedArray()) } func DeserializeProfileKey(bytes []byte) (*ProfileKey, error) { @@ -177,8 +177,8 @@ func (p *ProfileKeyCredentialRequestContext) cFixedArray() *C.SignalType_FixedAr return (*C.SignalType_FixedArray473_uint8_t)(unsafe.Pointer(p)) } -func (p *ProfileKeyCredentialRequestContext) cConstFixedArray() C.SignalType_ConstPointer_FixedArray473_uint8_t { - return (C.SignalType_ConstPointer_FixedArray473_uint8_t)(unsafe.Pointer(p)) +func (p *ProfileKeyCredentialRequestContext) cConstFixedArray() cFixedArray473Compat { + return cFixedArray473Compat(p.cFixedArray()) } func CreateProfileKeyCredentialRequestContext(serverPublicParams *ServerPublicParams, u uuid.UUID, profileKey ProfileKey) (*ProfileKeyCredentialRequestContext, error) { diff --git a/pkg/libsignalgo/senderkeystore.go b/pkg/libsignalgo/senderkeystore.go index fc51571..1649216 100644 --- a/pkg/libsignalgo/senderkeystore.go +++ b/pkg/libsignalgo/senderkeystore.go @@ -40,8 +40,7 @@ type SenderKeyStore interface { //export signal_load_sender_key_callback func signal_load_sender_key_callback(storeCtx unsafe.Pointer, recordp *C.SignalMutPointerSenderKeyRecord, address C.SignalMutPointerProtocolAddress, distributionID C.SignalUuid) C.int { return wrapStoreCallback(storeCtx, func(store SenderKeyStore, ctx context.Context) error { - addr := wrapAddress(address.raw) - record, err := store.LoadSenderKey(ctx, addr, *(*uuid.UUID)(unsafe.Pointer(&distributionID))) + record, err := store.LoadSenderKey(ctx, &Address{ptr: address.raw}, *(*uuid.UUID)(unsafe.Pointer(&distributionID))) if err == nil && record != nil { record.CancelFinalizer() recordp.raw = record.ptr @@ -53,9 +52,13 @@ func signal_load_sender_key_callback(storeCtx unsafe.Pointer, recordp *C.SignalM //export signal_store_sender_key_callback func signal_store_sender_key_callback(storeCtx unsafe.Pointer, address C.SignalMutPointerProtocolAddress, distributionID C.SignalUuid, senderKeyRecord C.SignalMutPointerSenderKeyRecord) C.int { return wrapStoreCallback(storeCtx, func(store SenderKeyStore, ctx context.Context) error { - addr := wrapAddress(address.raw) - record := wrapSenderKeyRecord(senderKeyRecord.raw) - return store.StoreSenderKey(ctx, addr, *(*uuid.UUID)(unsafe.Pointer(&distributionID)), record) + record := SenderKeyRecord{ptr: senderKeyRecord.raw} + cloned, err := record.Clone() + if err != nil { + return err + } + + return store.StoreSenderKey(ctx, &Address{ptr: address.raw}, *(*uuid.UUID)(unsafe.Pointer(&distributionID)), cloned) }) } diff --git a/pkg/libsignalgo/serviceid.go b/pkg/libsignalgo/serviceid.go index a320319..38631ee 100644 --- a/pkg/libsignalgo/serviceid.go +++ b/pkg/libsignalgo/serviceid.go @@ -188,6 +188,6 @@ func (s ServiceID) cFixedArray() *C.SignalType_FixedArray17_uint8_t { return s.FixedBytes().cFixedArray() } -func (s ServiceID) cConstFixedArray() C.SignalType_ConstPointer_FixedArray17_uint8_t { - return s.FixedBytes().cConstFixedArray() +func (s ServiceID) cConstFixedArray() cFixedArray17Compat { + return cFixedArray17Compat(s.cFixedArray()) } diff --git a/pkg/libsignalgo/sessionstore.go b/pkg/libsignalgo/sessionstore.go index c33a899..99000e5 100644 --- a/pkg/libsignalgo/sessionstore.go +++ b/pkg/libsignalgo/sessionstore.go @@ -38,8 +38,7 @@ type SessionStore interface { //export signal_load_session_callback func signal_load_session_callback(storeCtx unsafe.Pointer, recordp *C.SignalMutPointerSessionRecord, address C.SignalMutPointerProtocolAddress) C.int { return wrapStoreCallback(storeCtx, func(store SessionStore, ctx context.Context) error { - addr := wrapAddress(address.raw) - record, err := store.LoadSession(ctx, addr) + record, err := store.LoadSession(ctx, &Address{ptr: address.raw}) if err == nil && record != nil { record.CancelFinalizer() recordp.raw = record.ptr @@ -51,9 +50,12 @@ func signal_load_session_callback(storeCtx unsafe.Pointer, recordp *C.SignalMutP //export signal_store_session_callback func signal_store_session_callback(storeCtx unsafe.Pointer, address C.SignalMutPointerProtocolAddress, sessionRecord C.SignalMutPointerSessionRecord) C.int { return wrapStoreCallback(storeCtx, func(store SessionStore, ctx context.Context) error { - addr := wrapAddress(address.raw) - record := wrapSessionRecord(sessionRecord.raw) - return store.StoreSession(ctx, addr, record) + record := SessionRecord{ptr: sessionRecord.raw} + cloned, err := record.Clone() + if err != nil { + return err + } + return store.StoreSession(ctx, &Address{ptr: address.raw}, cloned) }) } diff --git a/pkg/libsignalgo/signalversion/version.go b/pkg/libsignalgo/signalversion/version.go index 367a3d5..b935b64 100644 --- a/pkg/libsignalgo/signalversion/version.go +++ b/pkg/libsignalgo/signalversion/version.go @@ -2,4 +2,4 @@ package signalversion -const Version = "v0.106.1" +const Version = "v0.105.0" diff --git a/pkg/libsignalgo/signedprekeystore.go b/pkg/libsignalgo/signedprekeystore.go index a66b9f4..b1306e2 100644 --- a/pkg/libsignalgo/signedprekeystore.go +++ b/pkg/libsignalgo/signedprekeystore.go @@ -50,8 +50,12 @@ func signal_load_signed_pre_key_callback(storeCtx unsafe.Pointer, keyp *C.Signal //export signal_store_signed_pre_key_callback func signal_store_signed_pre_key_callback(storeCtx unsafe.Pointer, id C.uint32_t, preKeyRecord C.SignalMutPointerSignedPreKeyRecord) C.int { return wrapStoreCallback(storeCtx, func(store SignedPreKeyStore, ctx context.Context) error { - record := wrapSignedPreKeyRecord(preKeyRecord.raw) - return store.StoreSignedPreKey(ctx, uint32(id), record) + record := SignedPreKeyRecord{ptr: preKeyRecord.raw} + cloned, err := record.Clone() + if err != nil { + return err + } + return store.StoreSignedPreKey(ctx, uint32(id), cloned) }) } diff --git a/pkg/libsignalgo/update-ffi-docker.sh b/pkg/libsignalgo/update-ffi-docker.sh index e15e7f6..73f8947 100755 --- a/pkg/libsignalgo/update-ffi-docker.sh +++ b/pkg/libsignalgo/update-ffi-docker.sh @@ -1,3 +1,9 @@ #!/bin/bash docker run --rm -itv $(pwd):/data rust:1-alpine /data/update-ffi-docker-inner.sh -./update-ffi-version.sh + +echo "// Generated by update-ffi.sh; DO NOT EDIT." > signalversion/version.go +echo >> signalversion/version.go +echo "package signalversion" >> signalversion/version.go +echo >> signalversion/version.go +cd libsignal +echo "const Version = \"$(git describe --tags --always)\"" >> ../signalversion/version.go diff --git a/pkg/libsignalgo/update-ffi-version.sh b/pkg/libsignalgo/update-ffi-version.sh deleted file mode 100755 index 23ae12c..0000000 --- a/pkg/libsignalgo/update-ffi-version.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -set -e - -pushd libsignal - -LIBSIGNAL_TAG=$(git describe --tags --always) -LIBSIGNAL_VERSION_FUNC=$(echo $LIBSIGNAL_TAG | tr -d 'v' | sed 's/\./_/g') - -popd - -cat > signalversion/version.go < version.go < signalversion/version.go +echo >> signalversion/version.go +echo "package signalversion" >> signalversion/version.go +echo >> signalversion/version.go + # Store the current working directory ORIGINAL_DIR="$(pwd)" # Navigate to libsignal directory cd "$LIBSIGNAL_DIRECTORY" +echo "const Version = \"$(git describe --tags --always)\"" >> ../signalversion/version.go + # Build libsignal cargo build -p libsignal-ffi --release @@ -28,6 +35,4 @@ cd "$ORIGINAL_DIR" cp "${LIBSIGNAL_DIRECTORY}/target/release/libsignal_ffi.a" . cp "${LIBSIGNAL_DIRECTORY}/swift/Sources/SignalFfi/signal_ffi.h" libsignal-ffi.h -./update-ffi-version.sh - echo "Files copied successfully." diff --git a/pkg/libsignalgo/version.go b/pkg/libsignalgo/version.go deleted file mode 100644 index b5732cb..0000000 --- a/pkg/libsignalgo/version.go +++ /dev/null @@ -1,13 +0,0 @@ -// Generated by update-ffi.sh; DO NOT EDIT. - -package libsignalgo - -/* -#include "./libsignal-ffi.h" -*/ -import "C" - -func init() { - // Compile time check to ensure the libsignal version matches - C.signal_ffi_version_0_106_1() -} diff --git a/pkg/msgconv/from-matrix.go b/pkg/msgconv/from-matrix.go index f755bbb..c54654c 100644 --- a/pkg/msgconv/from-matrix.go +++ b/pkg/msgconv/from-matrix.go @@ -21,7 +21,6 @@ import ( "fmt" "net/http" "strings" - "unicode/utf8" "github.com/rs/zerolog" "go.mau.fi/util/exmime" @@ -153,33 +152,9 @@ func (mc *MessageConverter) ToSignal( dm.BodyRanges = nil dm.Preview = nil } - if err := mc.uploadLongText(ctx, dm); err != nil { - return nil, err - } return dm, nil } -const maxInlineBodySize = 2 * 1024 - -func (mc *MessageConverter) uploadLongText(ctx context.Context, dm *signalpb.DataMessage) error { - body := dm.GetBody() - if len(body) <= maxInlineBodySize { - return nil - } - att, err := getClient(ctx).UploadAttachment(ctx, []byte(body)) - if err != nil { - return fmt.Errorf("%w (long text): %w", bridgev2.ErrMediaReuploadFailed, err) - } - att.ContentType = proto.String(LongTextContentType) - dm.Attachments = append([]*signalpb.AttachmentPointer{att}, dm.Attachments...) - cut := maxInlineBodySize - for !utf8.RuneStart(body[cut]) { - cut-- - } - dm.Body = proto.String(body[:cut]) - return nil -} - func maybeInt[T constraints.Integer](v T) *T { if v == 0 { return nil @@ -204,7 +179,7 @@ func (mc *MessageConverter) convertFileToSignal(ctx context.Context, evt *event. if content.MSC3245Voice != nil && mime != "audio/aac" && ffmpeg.Supported() { data, err = ffmpeg.ConvertBytes(ctx, data, ".aac", []string{}, []string{"-c:a", "aac"}, mime) if err != nil { - return nil, fmt.Errorf("%w (voice to aac): %w", bridgev2.ErrMediaConvertFailed, err) + return nil, err } mime = "audio/aac" fileName += ".aac" @@ -214,7 +189,7 @@ func (mc *MessageConverter) convertFileToSignal(ctx context.Context, evt *event. // allowed case "image/gif": if !ffmpeg.Supported() { - return nil, fmt.Errorf("%w: converting gif stickers is not supported", bridgev2.ErrUnsupportedMediaType) + return nil, fmt.Errorf("converting gif stickers is not supported") } data, err = ffmpeg.ConvertBytes(ctx, data, ".apng", []string{}, []string{}, mime) if err != nil { @@ -223,7 +198,7 @@ func (mc *MessageConverter) convertFileToSignal(ctx context.Context, evt *event. fileName += ".apng" mime = "image/apng" default: - return nil, fmt.Errorf("%w for sticker: %s", bridgev2.ErrUnsupportedMediaType, mime) + return nil, fmt.Errorf("unsupported content type for sticker %s", mime) } } att, err := getClient(ctx).UploadAttachment(ctx, data) diff --git a/pkg/msgconv/from-signal.go b/pkg/msgconv/from-signal.go index 2140db9..68f5558 100644 --- a/pkg/msgconv/from-signal.go +++ b/pkg/msgconv/from-signal.go @@ -99,9 +99,7 @@ func (mc *MessageConverter) ToMatrix( } if dm.GetIsViewOnce() { var unavailableReason string - if sender == client.Store.ACI { - unavailableReason = "Sent a view-once message." - } else if len(dm.GetAttachments()) == 0 || + if len(dm.GetAttachments()) == 0 || sender == client.Store.ACI || !time.UnixMilli(int64(dm.GetTimestamp())).Add(45*24*time.Hour).After(time.Now()) { unavailableReason = "View-once media is no longer available." } else if len(dm.GetAttachments()) != 1 || dm.Sticker != nil || dm.PollVote != nil || dm.PollCreate != nil || dm.PollTerminate != nil || @@ -153,12 +151,6 @@ func (mc *MessageConverter) ToMatrix( cm.Parts = append(cm.Parts, mc.convertPollTerminateToMatrix(ctx, sender, dm.PollTerminate)) return cm } - if dm.PinMessage != nil || dm.UnpinMessage != nil { - var part *bridgev2.ConvertedMessagePart - part, cm.ReplyTo = mc.convertPinToMatrix(dm) - cm.Parts = append(cm.Parts, part) - return cm - } if dm.GetIsViewOnce() { if !mc.Bridge.Config.ViewLimitedMedia && (cm.Disappear.Timer == 0 || cm.Disappear.Timer > 5*time.Minute) { cm.Disappear = database.DisappearingSetting{ @@ -181,7 +173,7 @@ func (mc *MessageConverter) ToMatrix( } } for i, att := range dm.GetAttachments() { - if att.GetContentType() != LongTextContentType || att.GetSize() > matrixTextMaxLength { + if att.GetContentType() != "text/x-signal-plain" || att.GetSize() > matrixTextMaxLength { part := mc.convertAttachmentToMatrix(ctx, i, att, attMap) if dm.GetIsViewOnce() { if part.Content.MsgType == event.MsgImage || part.Content.MsgType == event.MsgVideo { @@ -225,7 +217,7 @@ func (mc *MessageConverter) ToMatrix( for i, part := range cm.Parts { part.ID = signalid.MakeMessagePartID(i) meta := &signalid.MessageMetadata{ - ContainsAttachments: ContainsAttachments(dm), + ContainsAttachments: len(dm.GetAttachments()) > 0, ViewOnce: dm.GetIsViewOnce(), } if meta.ViewOnce { @@ -508,11 +500,6 @@ func (mc *MessageConverter) convertAttachmentToMatrix(ctx context.Context, index func (mc *MessageConverter) convertStickerToMatrix(ctx context.Context, sticker *signalpb.DataMessage_Sticker, attMap AttachmentMap) *bridgev2.ConvertedMessagePart { converted, err := mc.reuploadAttachment(ctx, sticker.GetData(), attMap) - hasPack := len(sticker.GetPackId()) == PackIDLength && len(sticker.GetPackKey()) == PackKeyLength && !bytes.Equal(sticker.GetPackId(), zeroPackID) - if err != nil && hasPack && (errors.Is(err, signalmeow.ErrAttachmentNotFound) || errors.Is(err, ErrAttachmentNotInBackup) || errors.Is(err, ErrBackupNotSupported)) { - zerolog.Ctx(ctx).Debug().Err(err).Msg("Sticker attachment is unavailable, downloading it from the sticker pack") - converted, err = mc.reuploadStickerFromPack(ctx, sticker) - } if err != nil { zerolog.Ctx(ctx).Err(err).Msg("Failed to handle sticker") return &bridgev2.ConvertedMessagePart{ @@ -530,7 +517,7 @@ func (mc *MessageConverter) convertStickerToMatrix(ctx context.Context, sticker converted.Content.Info.Height = 200 } converted.Content.Body = sticker.GetEmoji() - if hasPack { + if len(sticker.GetPackId()) == PackIDLength && len(sticker.GetPackKey()) == PackKeyLength && !bytes.Equal(sticker.GetPackId(), zeroPackID) { converted.Content.Info.BridgedSticker = &event.BridgedSticker{ Network: StickerSourceID, ID: strconv.FormatUint(uint64(sticker.GetStickerId()), 10), @@ -543,49 +530,6 @@ func (mc *MessageConverter) convertStickerToMatrix(ctx context.Context, sticker return converted } -func (mc *MessageConverter) reuploadStickerFromPack(ctx context.Context, sticker *signalpb.DataMessage_Sticker) (*bridgev2.ConvertedMessagePart, error) { - att := sticker.GetData() - content := &event.MessageEventContent{ - Body: att.GetFileName(), - Info: &event.FileInfo{ - MimeType: att.GetContentType(), - Width: int(att.GetWidth()), - Height: int(att.GetHeight()), - }, - } - if mc.DirectMedia { - mediaID, err := signalid.DirectMediaSticker{ - PackID: sticker.GetPackId(), - PackKey: sticker.GetPackKey(), - StickerID: sticker.GetStickerId(), - }.AsMediaID() - if err != nil { - return nil, err - } - content.URL, content.File, err = mc.Bridge.GenerateContentURI(ctx, getPortal(ctx).MXID, mediaID) - if err != nil { - return nil, err - } - } else { - data, err := signalmeow.DownloadStickerPackItem(ctx, sticker.GetPackId(), sticker.GetPackKey(), sticker.GetStickerId()) - if err != nil { - return nil, fmt.Errorf("failed to download sticker from pack: %w", err) - } - if content.Info.MimeType == "" { - content.Info.MimeType = http.DetectContentType(data) - } - content.Info.Size = len(data) - content.URL, content.File, err = getIntent(ctx).UploadMedia(ctx, getPortal(ctx).MXID, data, content.Body, content.Info.MimeType) - if err != nil { - return nil, fmt.Errorf("failed to upload sticker from pack: %w", err) - } - } - return &bridgev2.ConvertedMessagePart{ - Type: event.EventMessage, - Content: content, - }, nil -} - func (mc *MessageConverter) downloadSignalLongText(ctx context.Context, att *signalpb.AttachmentPointer, attMap AttachmentMap) (*string, error) { data, err := mc.downloadAttachment(ctx, att, attMap, nil) if err != nil { @@ -663,7 +607,7 @@ func (mc *MessageConverter) reuploadAttachment(ctx context.Context, att *signalp if err != nil { return nil, err } - content.URL, content.File, err = mc.Bridge.GenerateContentURI(ctx, getPortal(ctx).MXID, mediaID) + content.URL, err = mc.Bridge.Matrix.GenerateContentURI(ctx, mediaID) } else { err = mc.actuallyReuploadAttachment(ctx, content, att, attMap) if err != nil { @@ -847,28 +791,6 @@ func (mc *MessageConverter) convertPollTerminateToMatrix(ctx context.Context, se } } -func (mc *MessageConverter) convertPinToMatrix(dm *signalpb.DataMessage) (*bridgev2.ConvertedMessagePart, *networkid.MessageOptionalPartID) { - body := "Pinned a message" - targetAuthor, targetTS := dm.GetPinMessage().GetTargetAuthorAciBinary(), dm.GetPinMessage().GetTargetSentTimestamp() - if dm.PinMessage == nil { - body = "Unpinned a message" - targetAuthor, targetTS = dm.GetUnpinMessage().GetTargetAuthorAciBinary(), dm.GetUnpinMessage().GetTargetSentTimestamp() - } - var replyTo *networkid.MessageOptionalPartID - if len(targetAuthor) == 16 { - replyTo = &networkid.MessageOptionalPartID{ - MessageID: signalid.MakeMessageID(uuid.UUID(targetAuthor), targetTS), - } - } - return &bridgev2.ConvertedMessagePart{ - Type: event.EventMessage, - Content: &event.MessageEventContent{ - MsgType: event.MsgNotice, - Body: body, - }, - }, replyTo -} - var invalidPollVote = &bridgev2.ConvertedMessagePart{ Type: event.EventUnstablePollResponse, Content: &event.MessageEventContent{}, diff --git a/pkg/msgconv/matrixfmt/html.go b/pkg/msgconv/matrixfmt/html.go index 9a54274..b60c0ad 100644 --- a/pkg/msgconv/matrixfmt/html.go +++ b/pkg/msgconv/matrixfmt/html.go @@ -134,14 +134,12 @@ func JoinEntityString(with string, strings ...*EntityString) *EntityString { continue } DebugLog(" + %q %+v\n", s.String, s.Entities) - if len(str) > 0 { - str = append(str, withUTF16...) - } for _, entity := range s.Entities { entity.Start += len(str) entities = append(entities, entity) } str = append(str, s.String...) + str = append(str, withUTF16...) } DebugLog(" -> %q %+v\n", str, entities) return &EntityString{ diff --git a/pkg/msgconv/msgconv.go b/pkg/msgconv/msgconv.go index 2ec6ad0..03bf621 100644 --- a/pkg/msgconv/msgconv.go +++ b/pkg/msgconv/msgconv.go @@ -18,7 +18,6 @@ package msgconv import ( "context" - "slices" "github.com/google/uuid" "maunium.net/go/mautrix/bridgev2" @@ -29,7 +28,6 @@ import ( "go.mau.fi/mautrix-signal/pkg/msgconv/signalfmt" "go.mau.fi/mautrix-signal/pkg/signalid" "go.mau.fi/mautrix-signal/pkg/signalmeow" - "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" ) type contextKey int @@ -98,14 +96,6 @@ func NewMessageConverter(br *bridgev2.Bridge) *MessageConverter { } } -const LongTextContentType = "text/x-signal-plain" - -func ContainsAttachments(dm *signalpb.DataMessage) bool { - return slices.ContainsFunc(dm.GetAttachments(), func(att *signalpb.AttachmentPointer) bool { - return att.GetContentType() != LongTextContentType - }) -} - func getClient(ctx context.Context) *signalmeow.Client { return ctx.Value(contextKeyClient).(*signalmeow.Client) } diff --git a/pkg/signalid/dbmeta.go b/pkg/signalid/dbmeta.go index 0c35c0d..916aec5 100644 --- a/pkg/signalid/dbmeta.go +++ b/pkg/signalid/dbmeta.go @@ -24,8 +24,7 @@ type PortalMetadata struct { Revision uint32 `json:"revision,omitempty"` ExpirationTimerVersion uint32 `json:"expiration_timer_version,omitempty"` // Lazy resync tracking - LastSync jsontime.Unix `json:"last_sync,omitempty"` - InfoVersion uint32 `json:"info_version,omitempty"` + LastSync jsontime.Unix `json:"last_sync,omitempty"` } type MessageMetadata struct { diff --git a/pkg/signalmeow/attachments.go b/pkg/signalmeow/attachments.go index c4317ad..0b5f7e2 100644 --- a/pkg/signalmeow/attachments.go +++ b/pkg/signalmeow/attachments.go @@ -277,7 +277,7 @@ func (cli *Client) UploadAttachment(ctx context.Context, body []byte) (*signalpb } // Get upload attributes from Signal server - attributesPath := fmt.Sprintf("/v4/attachments/form/upload?uploadLength=%d", len(encryptedWithMAC)) + attributesPath := "/v4/attachments/form/upload" resp, err := cli.AuthedWS.SendRequest(ctx, http.MethodGet, attributesPath, nil, nil) if err != nil { log.Err(err).Msg("Failed to request upload attributes") @@ -344,7 +344,6 @@ func (cli *Client) uploadAttachmentLegacy( ContentType: web.ContentTypeOctetStream, Username: &username, Password: &password, - Upload: true, }) web.CloseBody(resp) if err != nil { @@ -369,7 +368,6 @@ func (cli *Client) uploadAttachmentTUS( Body: encryptedWithMAC, ContentType: web.ContentTypeOffsetOctetStream, Headers: uploadAttributes.Headers, - Upload: true, }) web.CloseBody(resp) // TODO actually support resuming on error diff --git a/pkg/signalmeow/backup.go b/pkg/signalmeow/backup.go index 911a888..f003e19 100644 --- a/pkg/signalmeow/backup.go +++ b/pkg/signalmeow/backup.go @@ -42,7 +42,6 @@ const transferArchiveFetchTimeout = 1 * time.Hour var ( ErrNoEphemeralBackupKey = errors.New("no ephemeral backup key") - ErrTransferTimedOut = errors.New("timed out waiting for transfer archive") ) const ( @@ -272,8 +271,8 @@ func (cli *Client) WaitForTransfer(ctx context.Context) (*TransferArchiveMetadat for { remainingTime := time.Until(timeout) - if remainingTime < time.Second { - return nil, ErrTransferTimedOut + if remainingTime < 0 { + return nil, fmt.Errorf("timed out") } reqStart := time.Now() reqTimeout := min(remainingTime, 5*time.Minute) diff --git a/pkg/signalmeow/client.go b/pkg/signalmeow/client.go index fad3f8e..c8898e3 100644 --- a/pkg/signalmeow/client.go +++ b/pkg/signalmeow/client.go @@ -72,8 +72,6 @@ type Client struct { storageSyncLock sync.Mutex storageSyncQueued atomic.Bool - storageVersion uint64 - storageRecords []string writeCallbackCounter chan time.Time } diff --git a/pkg/signalmeow/groupcache.go b/pkg/signalmeow/groupcache.go index 3a676fb..ee8259f 100644 --- a/pkg/signalmeow/groupcache.go +++ b/pkg/signalmeow/groupcache.go @@ -203,7 +203,6 @@ func (gc *GroupCache) ApplyUpdate(change *GroupChange, endorsementResponse libsi delete(gc.data, id) return nil } - prevMembers := cached.getMemberServiceIDs() // Pending member adds, promotes and removes cached.PendingMembers = append(cached.PendingMembers, change.AddPendingMembers...) @@ -305,9 +304,6 @@ func (gc *GroupCache) ApplyUpdate(change *GroupChange, endorsementResponse libsi if change.ModifyAnnouncementsOnly != nil { cached.AnnouncementsOnly = *change.ModifyAnnouncementsOnly } - if change.TerminateGroup { - cached.Terminated = true - } if change.ModifyDisappearingMessagesDuration != nil { cached.DisappearingMessagesDuration = *change.ModifyDisappearingMessagesDuration } @@ -323,12 +319,6 @@ func (gc *GroupCache) ApplyUpdate(change *GroupChange, endorsementResponse libsi cached.UpdatedAt = time.Now() cached.Revision = change.Revision - if len(endorsementResponse) == 0 { - if !slices.Equal(prevMembers, cached.getMemberServiceIDs()) { - delete(gc.data, id) - } - return nil - } endorsement, memberEndorsements, err := endorsementResponse.ReceiveWithServiceIDs( cached.getMemberServiceIDs(), gc.serviceID, diff --git a/pkg/signalmeow/groups.go b/pkg/signalmeow/groups.go index 882e048..493ecbb 100644 --- a/pkg/signalmeow/groups.go +++ b/pkg/signalmeow/groups.go @@ -81,7 +81,6 @@ type Group struct { Members []*GroupMember Description string AnnouncementsOnly bool - Terminated bool Revision uint32 DisappearingMessagesDuration uint32 AccessControl *GroupAccessControl @@ -207,7 +206,6 @@ type GroupChange struct { DeleteBannedMembers []*libsignalgo.ServiceID PromotePendingPniAciMembers []*PromotePendingPniAciMember ModifyInviteLinkPassword *types.SerializedInviteLinkPassword - TerminateGroup bool } func (groupChange *GroupChange) isEmpty() bool { @@ -228,8 +226,7 @@ func (groupChange *GroupChange) isEmpty() bool { len(groupChange.PromoteRequestingMembers) == 0 && groupChange.ModifyDescription == nil && groupChange.ModifyAnnouncementsOnly == nil && - len(groupChange.AddBannedMembers) == 0 && - !groupChange.TerminateGroup + len(groupChange.AddBannedMembers) == 0 } func (groupChange *GroupChange) resolveConflict(group *Group) { @@ -500,8 +497,6 @@ func decryptGroup(ctx context.Context, encryptedGroup *signalpb.Group, groupMast // These aren't encrypted decryptedGroup.AvatarPath = encryptedGroup.AvatarUrl decryptedGroup.Revision = encryptedGroup.Version - decryptedGroup.Terminated = encryptedGroup.Terminated - decryptedGroup.AnnouncementsOnly = encryptedGroup.AnnouncementsOnly // Decrypt members for _, member := range encryptedGroup.Members { @@ -712,7 +707,7 @@ func (cli *Client) DownloadGroupAvatar(ctx context.Context, avatarPath string, g return nil, fmt.Errorf("failed to send request: %w", err) } if resp.StatusCode < 200 || resp.StatusCode >= 300 { - return nil, fmt.Errorf("unexpected response status: %w", web.HTTPStatusError{StatusCode: resp.StatusCode, Status: resp.Status}) + return nil, fmt.Errorf("unexpected response status %d", resp.StatusCode) } encryptedAvatar, err := io.ReadAll(resp.Body) if err != nil { @@ -1081,7 +1076,6 @@ func (cli *Client) decryptGroupChange(ctx context.Context, encryptedGroupChange if encryptedActions.ModifyAnnouncementsOnly != nil { decryptedGroupChange.ModifyAnnouncementsOnly = &encryptedActions.ModifyAnnouncementsOnly.AnnouncementsOnly } - decryptedGroupChange.TerminateGroup = encryptedActions.TerminateGroup != nil if encryptedActions.ModifyDisappearingMessageTimer != nil && len(encryptedActions.ModifyDisappearingMessageTimer.Timer) > 0 { timerBlob, err := decryptGroupPropertyIntoBlob(groupSecretParams, encryptedActions.ModifyDisappearingMessageTimer.Timer) if err != nil { @@ -1807,7 +1801,8 @@ func (cli *Client) GetGroupHistoryPage(ctx context.Context, gid types.GroupIdent "Cached-Send-Endorsements": "0", }, } - path := fmt.Sprintf("/v2/groups/logs/%d?maxSupportedChangeEpoch=%d&includeFirstState=%t&includeLastState=false", fromRevision, 7, includeFirstState) + // highest known epoch seems to always be 5, but that may change in the future. includeLastState is always false + path := fmt.Sprintf("/v2/groups/logs/%d?maxSupportedChangeEpoch=%d&includeFirstState=%t&includeLastState=false", fromRevision, 5, includeFirstState) response, err := web.SendHTTPRequest(ctx, web.StorageHostname, http.MethodGet, path, opts) defer web.CloseBody(response) if err != nil { diff --git a/pkg/signalmeow/keys.go b/pkg/signalmeow/keys.go index 1b2370a..84a242a 100644 --- a/pkg/signalmeow/keys.go +++ b/pkg/signalmeow/keys.go @@ -64,13 +64,6 @@ func (cli *Client) RegisterAllPreKeys(ctx context.Context, pks store.PreKeyStore return fmt.Errorf("failed to get all kyber prekeys: %w", err) } - if len(preKeys) > PREKEY_BATCH_SIZE { - preKeys = preKeys[len(preKeys)-PREKEY_BATCH_SIZE:] - } - if len(kyberPreKeys) > PREKEY_BATCH_SIZE { - kyberPreKeys = kyberPreKeys[len(kyberPreKeys)-PREKEY_BATCH_SIZE:] - } - // We need to have some keys to upload if len(preKeys) == 0 && len(kyberPreKeys) == 0 { return fmt.Errorf("no prekeys to upload") @@ -415,7 +408,6 @@ func addBase64PaddingAndDecode(data string) ([]byte, error) { var ( ErrUnregisteredUser = errors.New("user is unregistered") ErrDevicesChanged = errors.New("device list changed while sending skdm") - ErrRateLimited = errors.New("rate limited by server") ) func (cli *Client) FetchAndProcessPreKey(ctx context.Context, theirServiceID libsignalgo.ServiceID, specificDeviceID int) error { diff --git a/pkg/signalmeow/profile.go b/pkg/signalmeow/profile.go index a0878b6..a515f5a 100644 --- a/pkg/signalmeow/profile.go +++ b/pkg/signalmeow/profile.go @@ -289,7 +289,7 @@ func (cli *Client) DownloadUserAvatar(ctx context.Context, avatarPath string, pr } defer web.CloseBody(resp) if resp.StatusCode < 200 || resp.StatusCode >= 300 { - return nil, fmt.Errorf("unexpected response status: %w", web.HTTPStatusError{StatusCode: resp.StatusCode, Status: resp.Status}) + return nil, fmt.Errorf("unexpected response status %d", resp.StatusCode) } encryptedAvatar, err := io.ReadAll(resp.Body) if err != nil { diff --git a/pkg/signalmeow/receiving.go b/pkg/signalmeow/receiving.go index dcc748a..f0d69df 100644 --- a/pkg/signalmeow/receiving.go +++ b/pkg/signalmeow/receiving.go @@ -154,10 +154,7 @@ func (cli *Client) StartReceiveLoops(ctx context.Context) (chan SignalConnection case <-loopCtx.Done(): log.Info().Msg("Context done, exiting websocket status loop") return - case status, ok := <-authChan: - if !ok { - return - } + case status := <-authChan: lastAuthStatus = status currentStatus = status @@ -185,11 +182,7 @@ func (cli *Client) StartReceiveLoops(ctx context.Context) (chan SignalConnection default: } } - case status, ok := <-unauthChan: - if !ok { - unauthChan = nil - continue - } + case status := <-unauthChan: lastUnauthStatus = status currentStatus = status @@ -378,19 +371,6 @@ func (cli *Client) incomingAPIMessageHandler(ctx context.Context, req *signalpb. Str("envelope_type", signalpb.Envelope_Type_name[int32(envelope.GetType())]). Msg("Received envelope") - if envelope.GetType() == signalpb.Envelope_SERVER_DELIVERY_RECEIPT { - if sourceServiceID.Type == libsignalgo.ServiceIDTypeACI && sourceServiceID.UUID != uuid.Nil && !cli.handleEvent(&events.Receipt{ - Sender: sourceServiceID.UUID, - Content: &signalpb.ReceiptMessage{ - Type: signalpb.ReceiptMessage_DELIVERY.Enum(), - Timestamp: []uint64{envelope.GetClientTimestamp()}, - }, - }) { - return nil, ErrHandlerFailed - } - return &web.SimpleResponse{Status: 200}, nil - } - result := cli.decryptEnvelope(ctx, envelope, sourceServiceID, destinationServiceID) err = cli.handleDecryptedResult(ctx, result, envelope, destinationServiceID) @@ -664,7 +644,7 @@ func (cli *Client) handleDecryptedResult( }) } case *signalpb.Content_CallMessage: - if !isBlocked && (content.CallMessage.Offer != nil || (content.CallMessage.Hangup != nil && content.CallMessage.Hangup.GetType() != signalpb.CallMessage_Hangup_HANGUP_ACCEPTED)) { + if !isBlocked && (content.CallMessage.Offer != nil || content.CallMessage.Hangup != nil) { handlerSuccess = cli.handleEvent(&events.Call{ Info: events.MessageInfo{ Sender: theirServiceID.UUID, @@ -922,7 +902,7 @@ func (cli *Client) handlePNISignatureMessage(ctx context.Context, sender libsign 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, 1) + 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 { diff --git a/pkg/signalmeow/receiving_decrypt.go b/pkg/signalmeow/receiving_decrypt.go index 8ad3e15..f92647c 100644 --- a/pkg/signalmeow/receiving_decrypt.go +++ b/pkg/signalmeow/receiving_decrypt.go @@ -71,15 +71,12 @@ func (cli *Client) decryptEnvelope( } var result *DecryptionResult var bundleType string - var ciphertextType libsignalgo.CiphertextMessageType if *envelope.Type == signalpb.Envelope_PREKEY_MESSAGE { result, err = cli.prekeyDecrypt(ctx, destinationServiceID, sender, envelope.Content, envelope.GetServerTimestamp()) bundleType = "prekey bundle" - ciphertextType = libsignalgo.CiphertextMessageTypePreKey } else { result, err = cli.decryptCiphertextEnvelope(ctx, destinationServiceID, sender, envelope.Content, envelope.GetServerTimestamp()) bundleType = "ciphertext" - ciphertextType = libsignalgo.CiphertextMessageTypeWhisper } if err != nil { return DecryptionResult{ @@ -87,7 +84,7 @@ func (cli *Client) decryptEnvelope( Err: fmt.Errorf("failed to decrypt %s envelope: %w", bundleType, err), Retriable: true, // TODO should these ever be not retriable? Ciphertext: envelope.Content, - CiphertextType: ciphertextType, + CiphertextType: libsignalgo.CiphertextMessageType(envelope.GetType()), } } return *result @@ -107,6 +104,9 @@ func (cli *Client) decryptEnvelope( Unencrypted: true, } + case signalpb.Envelope_SERVER_DELIVERY_RECEIPT: + return DecryptionResult{Err: fmt.Errorf("server delivery receipt envelopes are not yet supported")} + case signalpb.Envelope_UNKNOWN: return DecryptionResult{Err: fmt.Errorf("unknown envelope type")} diff --git a/pkg/signalmeow/senderkey.go b/pkg/signalmeow/senderkey.go index 322fcc8..344bacf 100644 --- a/pkg/signalmeow/senderkey.go +++ b/pkg/signalmeow/senderkey.go @@ -221,8 +221,8 @@ func (cli *Client) sendToGroupWithSenderKey( header.Set("Unidentified-Access-Key", xak.String()) } path := fmt.Sprintf( - "/v1/messages/multi_recipient?ts=%d&urgent=%t&online=%t", - messageTimestamp, isUrgent(content), content.GetTypingMessage() != nil, + "/v1/messages/multi_recipient?ts=%d&urgent=%t&online=false", + messageTimestamp, isUrgent(content), ) log.Debug(). Any("recipients", ski.SharedWith). @@ -249,7 +249,7 @@ func (cli *Client) sendToGroupWithSenderKey( cli.Store.RecipientStore.MarkUnregistered(ctx, serviceID, true) result.FailedToSendTo = append(result.FailedToSendTo, FailedSendResult{ Recipient: serviceID, - Error: fmt.Errorf("%w (multi-recipient send returned 404)", ErrUnregisteredUser), + Error: fmt.Errorf("multi-recipient send 404"), }) } else { result.SuccessfullySentTo = append(result.SuccessfullySentTo, SuccessfulSendResult{ @@ -277,13 +277,6 @@ func (cli *Client) sendToGroupWithSenderKey( doUnlock() // Retry recursively after fixing device lists return cli.sendToGroupWithSenderKey(ctx, groupID, allRecipients, sec, content, messageTimestamp, retries+1) - case 413, 428, 429: - return nil, fmt.Errorf("%w (multi-recipient send returned %d)", ErrRateLimited, resp.GetStatus()) - case 500, 503: - log.Warn().Uint32("status_code", resp.GetStatus()). - Msg("Multi-recipient send failed with server error, retrying") - doUnlock() - return cli.sendToGroupWithSenderKey(ctx, groupID, allRecipients, sec, content, messageTimestamp, retries+1) default: return nil, fmt.Errorf("unexpected status code %d in multi-recipient send", resp.GetStatus()) } diff --git a/pkg/signalmeow/sending.go b/pkg/signalmeow/sending.go index dce4eea..f48a252 100644 --- a/pkg/signalmeow/sending.go +++ b/pkg/signalmeow/sending.go @@ -297,20 +297,6 @@ type GroupMessageSendResult struct { FailedToSendTo []FailedSendResult } -// FailureError returns an error from FailedToSendTo, preferring errors other than ErrUnregisteredUser, -// so that a permanent failure is only reported if every recipient failed permanently. -func (r *GroupMessageSendResult) FailureError() error { - if len(r.FailedToSendTo) == 0 { - return nil - } - for _, failure := range r.FailedToSendTo { - if !errors.Is(failure.Error, ErrUnregisteredUser) { - return failure.Error - } - } - return r.FailedToSendTo[len(r.FailedToSendTo)-1].Error -} - type SendResult interface { isSendResult() } @@ -672,7 +658,8 @@ func (cli *Client) sendToGroup( return result, nil // I only sent to myself } if len(result.SuccessfullySentTo) == 0 { - return nil, fmt.Errorf("failed to send to any group members: %w", result.FailureError()) + lastError := result.FailedToSendTo[len(result.FailedToSendTo)-1].Error + return nil, fmt.Errorf("failed to send to any group members: %w", lastError) } return result, nil @@ -964,7 +951,7 @@ func (cli *Client) sendContent( outgoingMessages := MyMessages{ Timestamp: messageTimestamp, - Online: content.GetTypingMessage() != nil, + Online: false, Urgent: isUrgent(content), Messages: messages, } @@ -1050,8 +1037,6 @@ func (cli *Client) sendContent( } cli.Store.RecipientStore.MarkUnregistered(ctx, recipient, true) return sentUnidentified, fmt.Errorf("%w (send returned 404)", ErrUnregisteredUser) - } else if *response.Status == 413 || *response.Status == 429 { - return sentUnidentified, fmt.Errorf("%w (send returned %d)", ErrRateLimited, *response.Status) } else if *response.Status != 200 { return sentUnidentified, fmt.Errorf("unexpected status code while sending: %d", *response.Status) } @@ -1204,7 +1189,7 @@ func (cli *Client) handle428(ctx context.Context, recipient libsignalgo.ServiceI // } // } //} - return fmt.Errorf("%w (send returned 428, retry after %ds)", ErrRateLimited, retryAfterSeconds) + return fmt.Errorf("got 428 error") } func (cli *Client) ReportSpam(ctx context.Context, aci uuid.UUID, serverGUID string, token []byte) error { diff --git a/pkg/signalmeow/sticker.go b/pkg/signalmeow/sticker.go index f50fafc..e1364b1 100644 --- a/pkg/signalmeow/sticker.go +++ b/pkg/signalmeow/sticker.go @@ -72,7 +72,7 @@ func downloadStickerData(ctx context.Context, path string, packKey []byte) ([]by if err != nil { return nil, fmt.Errorf("failed to make request: %w", err) } else if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("unexpected status code: %w", web.HTTPStatusError{StatusCode: resp.StatusCode, Status: resp.Status}) + return nil, fmt.Errorf("unexpected status code %d", resp.StatusCode) } else if body, err = io.ReadAll(resp.Body); err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } else if decrypted, err = decryptSticker(packKey, body); err != nil { diff --git a/pkg/signalmeow/storageservice.go b/pkg/signalmeow/storageservice.go index f3b9aac..cfc2951 100644 --- a/pkg/signalmeow/storageservice.go +++ b/pkg/signalmeow/storageservice.go @@ -55,30 +55,18 @@ func (cli *Client) StorageSync(ctx context.Context) { cli.storageSyncQueued.Store(false) log := cli.Log.With().Str("action", "sync storage").Logger() ctx = log.WithContext(ctx) - update, err := cli.FetchStorage(ctx, cli.Store.MasterKey, cli.storageVersion, cli.storageRecords) + // TODO only fetch changed entries + update, err := cli.FetchStorage(ctx, cli.Store.MasterKey, 0, nil) if err != nil { log.Err(err).Msg("Failed to fetch storage") return - } else if update == nil { - log.Debug().Uint64("version", cli.storageVersion).Msg("Storage manifest is already up to date") - return } err = cli.Store.DoContactTxn(ctx, func(ctx context.Context) error { return cli.processStorageInTxn(ctx, update) }) if err != nil { log.Err(err).Msg("Failed to process storage update") - return } - log.Debug(). - Uint64("previous_version", cli.storageVersion). - Uint64("version", update.Version). - Int("new_records", len(update.NewRecords)). - Int("removed_records", len(update.RemovedRecords)). - Int("missing_records", len(update.MissingRecords)). - Msg("Processed storage update") - cli.storageVersion = update.Version - cli.storageRecords = update.KnownRecords } func (cli *Client) processStorageInTxn(ctx context.Context, update *StorageUpdate) error { @@ -113,9 +101,8 @@ func (cli *Client) processStorageInTxn(ctx context.Context, update *StorageUpdat recipient.Profile.Name = strings.TrimSpace(fmt.Sprintf("%s %s", contact.GivenName, contact.FamilyName)) } if contact.SystemGivenName != "" || contact.SystemFamilyName != "" { - newContactName := strings.TrimSpace(fmt.Sprintf("%s %s", contact.SystemGivenName, contact.SystemFamilyName)) - changed = changed || recipient.ContactName != newContactName - recipient.ContactName = newContactName + changed = true + recipient.ContactName = strings.TrimSpace(fmt.Sprintf("%s %s", contact.SystemGivenName, contact.SystemFamilyName)) } newNickname := "" if contact.Nickname != nil { @@ -133,7 +120,7 @@ func (cli *Client) processStorageInTxn(ctx context.Context, update *StorageUpdat changed = true recipient.Blocked = contact.Blocked } - if !ptr.Val(recipient.Whitelisted) && (recipient.Whitelisted == nil || contact.Whitelisted) { + if !ptr.Val(recipient.Whitelisted) { changed = true recipient.Whitelisted = &contact.Whitelisted } @@ -171,9 +158,7 @@ func (cli *Client) processStorageInTxn(ctx context.Context, update *StorageUpdat return fmt.Errorf("failed to save device after receiving account record: %w", err) } log.Debug().Msg("Saved device after receiving account record") - case *signalpb.StorageRecord_GroupV1, *signalpb.StorageRecord_StoryDistributionList, *signalpb.StorageRecord_StickerPack, - *signalpb.StorageRecord_CallLink, *signalpb.StorageRecord_ChatFolder, *signalpb.StorageRecord_NotificationProfile, - *signalpb.StorageRecord_FavoriteSticker: + case *signalpb.StorageRecord_GroupV1, *signalpb.StorageRecord_StoryDistributionList: // irrelevant data default: log.Warn().Type("type", data).Str("item_id", record.StorageID).Msg("Unknown storage record type") @@ -193,7 +178,6 @@ type StorageUpdate struct { NewRecords []*DecryptedStorageRecord RemovedRecords []string MissingRecords []string - KnownRecords []string } func (cli *Client) FetchStorage(ctx context.Context, masterKey []byte, currentVersion uint64, existingKeys []string) (*StorageUpdate, error) { @@ -205,7 +189,6 @@ func (cli *Client) FetchStorage(ctx context.Context, masterKey []byte, currentVe return nil, nil } removedKeys := make([]string, 0) - knownKeys := make([]string, 0, len(manifest.GetIdentifiers())) newKeys := manifestRecordToMap(manifest.GetIdentifiers()) slices.Sort(existingKeys) existingKeys = slices.Compact(existingKeys) @@ -213,7 +196,6 @@ func (cli *Client) FetchStorage(ctx context.Context, masterKey []byte, currentVe _, isStillThere := newKeys[key] if isStillThere { delete(newKeys, key) - knownKeys = append(knownKeys, key) } else { removedKeys = append(removedKeys, key) } @@ -223,15 +205,11 @@ func (cli *Client) FetchStorage(ctx context.Context, masterKey []byte, currentVe if err != nil { return nil, err } - for _, record := range newRecords { - knownKeys = append(knownKeys, record.StorageID) - } return &StorageUpdate{ Version: manifest.GetVersion(), NewRecords: newRecords, RemovedRecords: removedKeys, MissingRecords: missingKeys, - KnownRecords: knownKeys, }, nil } diff --git a/pkg/signalmeow/store/prekey_store.go b/pkg/signalmeow/store/prekey_store.go index 2cf62a5..37bca7e 100644 --- a/pkg/signalmeow/store/prekey_store.go +++ b/pkg/signalmeow/store/prekey_store.go @@ -51,13 +51,13 @@ type PreKeyStore interface { } const ( - getAllPreKeysQuery = `SELECT key_pair FROM signalmeow_pre_keys WHERE account_id=$1 AND service_id=$2 AND is_signed=$3 ORDER BY key_id` + getAllPreKeysQuery = `SELECT key_pair FROM signalmeow_pre_keys WHERE account_id=$1 AND service_id=$2 AND is_signed=$3` getPreKeyQuery = `SELECT key_pair FROM signalmeow_pre_keys WHERE account_id=$1 AND service_id=$2 AND key_id=$3 AND is_signed=$4` insertPreKeyQuery = `INSERT INTO signalmeow_pre_keys (account_id, service_id, key_id, is_signed, key_pair) VALUES ($1, $2, $3, $4, $5)` deletePreKeyQuery = `DELETE FROM signalmeow_pre_keys WHERE account_id=$1 AND service_id=$2 AND key_id=$3 AND is_signed=$4` getLastPreKeyIDQuery = `SELECT COUNT(*), COALESCE(MAX(key_id), 0) FROM signalmeow_pre_keys WHERE account_id=$1 AND service_id=$2 AND is_signed=$3` - getAllKyberPreKeysQuery = `SELECT key_pair FROM signalmeow_kyber_pre_keys WHERE account_id=$1 AND service_id=$2 AND is_last_resort=false ORDER BY key_id` + getAllKyberPreKeysQuery = `SELECT key_pair FROM signalmeow_kyber_pre_keys WHERE account_id=$1 AND service_id=$2 AND is_last_resort=false` getKyberPreKeyQuery = `SELECT key_pair FROM signalmeow_kyber_pre_keys WHERE account_id=$1 AND service_id=$2 AND key_id=$3` insertKyberPreKeyQuery = `INSERT INTO signalmeow_kyber_pre_keys (account_id, service_id, key_id, key_pair, is_last_resort) VALUES ($1, $2, $3, $4, $5)` deleteKyberPreKeyQuery = `DELETE FROM signalmeow_kyber_pre_keys WHERE account_id=$1 AND service_id=$2 AND key_id=$3` diff --git a/pkg/signalmeow/web/signalwebsocket.go b/pkg/signalmeow/web/signalwebsocket.go index 2ee9ac8..3231322 100644 --- a/pkg/signalmeow/web/signalwebsocket.go +++ b/pkg/signalmeow/web/signalwebsocket.go @@ -22,7 +22,6 @@ import ( "encoding/json" "errors" "fmt" - "net" "net/http" "net/url" "strings" @@ -117,9 +116,6 @@ func (s *SignalWebsocket) Close() (err error) { s.closeCalled.Store(true) if ws := s.ws.Swap(nil); ws != nil { err = ws.Close(websocket.StatusNormalClosure, "") - if errors.Is(err, net.ErrClosed) { - err = nil - } } if cancelLoop := s.cancel.Swap(nil); cancelLoop != nil { (*cancelLoop)() @@ -226,12 +222,10 @@ func (s *SignalWebsocket) connectLoop( return } if request == nil { - log.Error().Msg("Received nil request") - continue + log.Fatal().Msg("Received nil request") } if requestHandler == nil { - log.Error().Uint64("request_id", request.GetId()).Msg("Received request but no handler") - continue + log.Fatal().Msg("Received request but no handler") } // Handle the request with the request handler function @@ -260,7 +254,6 @@ func (s *SignalWebsocket) connectLoop( retrying := false errorCount := 0 isFirstConnect := true - var connectedAt time.Time wsURL := (&url.URL{ Scheme: "wss", Host: APIHostname, @@ -278,7 +271,7 @@ func (s *SignalWebsocket) connectLoop( case <-ctx.Done(): } backoff += backoffIncrement - } else if !isFirstConnect && (s.basicAuth != nil || time.Since(connectedAt) < time.Minute) { + } else if !isFirstConnect && s.basicAuth != nil { select { case <-time.After(initialBackoff): case <-ctx.Done(): @@ -294,17 +287,20 @@ func (s *SignalWebsocket) connectLoop( if resp != nil { if resp.StatusCode != 101 { // Server didn't want to open websocket - statusErr := HTTPStatusError{StatusCode: resp.StatusCode, Status: resp.Status} - if resp.StatusCode >= 500 || resp.StatusCode == http.StatusTooManyRequests { - // We can try again if it's a 5xx or a rate limit - s.pushStatus(ctx, SignalWebsocketConnectionEventDisconnected, fmt.Errorf("error opening websocket: %w", statusErr)) - } else if resp.StatusCode == http.StatusForbidden || (resp.StatusCode == http.StatusUnauthorized && s.basicAuth != nil) { + if resp.StatusCode >= 500 { + // We can try again if it's a 5xx + s.pushStatus(ctx, SignalWebsocketConnectionEventDisconnected, fmt.Errorf("5xx opening websocket: %v", resp.Status)) + } else if resp.StatusCode == 403 { // We are logged out, so we should stop trying to reconnect - s.pushStatus(ctx, SignalWebsocketConnectionEventLoggedOut, fmt.Errorf("%w opening websocket, we are logged out", statusErr)) + s.pushStatus(ctx, SignalWebsocketConnectionEventLoggedOut, fmt.Errorf("403 opening websocket, we are logged out")) + return // NOT RETRYING, KILLING THE CONNECTION LOOP + } else if resp.StatusCode > 0 && resp.StatusCode < 500 { + // Unexpected status code + s.pushStatus(ctx, SignalWebsocketConnectionEventFatalError, fmt.Errorf("unexpected status opening websocket: %v", resp.Status)) return // NOT RETRYING, KILLING THE CONNECTION LOOP } else { - // Unexpected status code - s.pushStatus(ctx, SignalWebsocketConnectionEventError, fmt.Errorf("unexpected status opening websocket: %w", statusErr)) + // Something is very wrong + s.pushStatus(ctx, SignalWebsocketConnectionEventError, fmt.Errorf("unexpected error opening websocket: %v", resp.Status)) } // Retry the connection retrying = true @@ -323,16 +319,10 @@ func (s *SignalWebsocket) connectLoop( } // Succssfully connected - if resp != nil { - if alerts := resp.Header.Values("X-Signal-Alert"); len(alerts) > 0 { - log.Warn().Strs("alerts", alerts).Msg("Signal server sent alerts when opening websocket") - } - } s.pushStatus(ctx, SignalWebsocketConnectionEventConnected, nil) s.ws.Store(ws) retrying = false backoff = initialBackoff - connectedAt = time.Now() responseChannels := exsync.NewMap[uint64, chan *signalpb.WebSocketResponseMessage]() loopCtx, loopCancel := context.WithCancelCause(ctx) @@ -412,16 +402,12 @@ func (s *SignalWebsocket) connectLoop( if ctxCauseErr == nil || errors.Is(ctxCauseErr, context.Canceled) { s.pushStatus(ctx, SignalWebsocketConnectionEventCleanShutdown, nil) } else { - if time.Since(connectedAt) > time.Minute { - errorCount = 0 - } + errorCount++ s.pushStatus(ctx, SignalWebsocketConnectionEventDisconnected, ctxCauseErr) if errors.Is(ctxCauseErr, ErrForcedReconnect) { // Skip the delay for forced reconnects // TODO should the delay be lowered globally? isFirstConnect = true - } else { - errorCount++ } } @@ -434,12 +420,10 @@ func (s *SignalWebsocket) connectLoop( wg.Wait() log.Debug().Msg("Finished websocket cleanup") if errorCount > 500 { - // Something is really wrong, stop reconnecting. + // Something is really wrong, we better panic. // This is a last defense against a runaway error loop, // like the WS continually closing and reconnecting - log.Error().Int("error_count", errorCount).Msg("Too many errors, stopping connection loop") - s.pushStatus(ctx, SignalWebsocketConnectionEventFatalError, fmt.Errorf("too many websocket errors (%d)", errorCount)) - return + log.Fatal().Int("error_count", errorCount).Msg("Too many errors, panicking") } } } @@ -480,17 +464,13 @@ func readLoop( Str("request_verb", *msg.Request.Verb). Str("request_path", *msg.Request.Path). Msg("Received WS request") - select { - case incomingRequestChan <- msg.Request: - case <-ctx.Done(): - return ctx.Err() - } + incomingRequestChan <- msg.Request } else if *msg.Type == signalpb.WebSocketMessage_RESPONSE { if msg.Response == nil { - return errors.New("received response message with no response") + log.Fatal().Msg("Received response with no response") } if msg.Response.Id == nil { - return errors.New("received response message with no id") + log.Fatal().Msg("Received response with no id") } responseChannel, ok := responseChannels.Pop(*msg.Response.Id) if !ok { @@ -502,9 +482,9 @@ func readLoop( logEvt := log.Debug(). Uint64("response_id", msg.Response.GetId()). Uint32("response_status", msg.Response.GetStatus()). - Str("response_message", msg.Response.GetMessage()). - Strs("response_headers", msg.Response.Headers) - if log.GetLevel() == zerolog.TraceLevel || msg.Response.GetStatus() >= 400 { + Str("response_message", msg.Response.GetMessage()) + if log.GetLevel() == zerolog.TraceLevel || len(msg.Response.Body) < 256 { + logEvt.Strs("response_headers", msg.Response.Headers) if json.Valid(msg.Response.Body) { logEvt.RawJSON("response_body", msg.Response.Body) } else { @@ -698,7 +678,6 @@ func OpenWebsocket(ctx context.Context, url string) (*websocket.Conn, *http.Resp func CreateWSResponse(ctx context.Context, id uint64, status int) *signalpb.WebSocketMessage { if status != 200 && status != 400 { // TODO support more responses to Signal? Are there more? - //bridgelint:ignore GR004 statuses come from the bridge's own handlers, so anything else is a bridge bug zerolog.Ctx(ctx).Fatal().Int("status", status).Msg("Error creating response. Non 200/400 not supported yet.") return nil } diff --git a/pkg/signalmeow/web/web.go b/pkg/signalmeow/web/web.go index fc86457..d30e367 100644 --- a/pkg/signalmeow/web/web.go +++ b/pkg/signalmeow/web/web.go @@ -28,7 +28,6 @@ import ( "net/http" "runtime" "strings" - "sync/atomic" "time" "github.com/rs/zerolog" @@ -75,9 +74,6 @@ var signalTransport = &http.Transport{ var SignalHTTPClient = &http.Client{ Transport: signalTransport, } -var SignalUploadHTTPClient = &http.Client{ - Transport: signalTransport, -} func init() { SignalCertPool.AddCert(exerrors.Must(x509.ParseCertificate(signalRootCertBytes))) @@ -101,10 +97,9 @@ type HTTPReqOpt struct { ContentType ContentType Headers map[string]string OverrideURL string // Override the full URL, if set ignores path and Host - Upload bool } -var httpReqCounter atomic.Int64 +var httpReqCounter = 0 func SendHTTPRequest(ctx context.Context, host, method, path string, opt *HTTPReqOpt) (*http.Response, error) { // Set defaults @@ -148,14 +143,11 @@ func SendHTTPRequest(ctx context.Context, host, method, path string, opt *HTTPRe req.SetBasicAuth(*opt.Username, *opt.Password) } - log = log.With().Int64("request_number", httpReqCounter.Add(1)).Logger() + httpReqCounter++ + log = log.With().Int("request_number", httpReqCounter).Logger() log.Trace().Msg("Sending HTTP request") - client := SignalHTTPClient - if opt.Upload { - client = SignalUploadHTTPClient - } start := time.Now() - resp, err := client.Do(req) + resp, err := SignalHTTPClient.Do(req) dur := time.Since(start) if err != nil { log.Err(err).Dur("duration", dur).Msg("Error sending request") @@ -165,16 +157,6 @@ func SendHTTPRequest(ctx context.Context, host, method, path string, opt *HTTPRe return resp, nil } -// HTTPStatusError is returned when a Signal server responds with an unexpected HTTP status. -type HTTPStatusError struct { - StatusCode int - Status string -} - -func (e HTTPStatusError) Error() string { - return e.Status -} - func CloseBody(resp *http.Response) { if resp != nil && resp.Body != nil { _ = resp.Body.Close() @@ -260,8 +242,9 @@ func GetAttachment(ctx context.Context, path string, cdnNumber uint32) (*http.Re req.Header.Add("Content-Type", "application/octet-stream") req.Header.Set("User-Agent", UserAgent) + httpReqCounter++ log = log.With(). - Int64("request_number", httpReqCounter.Add(1)). + Int("request_number", httpReqCounter). Str("url", urlStr). Logger()