mirror of
https://github.com/mautrix/signal.git
synced 2026-05-15 13:46:55 -04:00
Compare commits
No commits in common. "main" and "v0.2604.0" have entirely different histories.
40 changed files with 239 additions and 767 deletions
3
.github/ISSUE_TEMPLATE/bug.md
vendored
3
.github/ISSUE_TEMPLATE/bug.md
vendored
|
|
@ -11,8 +11,7 @@ type: Bug
|
|||
|
||||
### Checklist
|
||||
|
||||
<!-- All items below are mandatory. Issues not following the rules may be closed without comment. -->
|
||||
<!-- Both items below are mandatory. Issues not following the rules may be closed without comment. -->
|
||||
|
||||
* [ ] This is an actual bug, not just a setup issue (see the [troubleshooting docs](https://docs.mau.fi/bridges/general/troubleshooting.html) or ask in the Matrix room for setup help).
|
||||
* [ ] I am certain that sufficient information is included. Ask in the Matrix room first if not.
|
||||
* [ ] The bug is still present on the main branch. The `!signal version` command output is: ``
|
||||
|
|
|
|||
10
go.mod
10
go.mod
|
|
@ -11,17 +11,16 @@ require (
|
|||
github.com/emersion/go-vcard v0.0.0-20241024213814-c9703dde27ff
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/mattn/go-pointer v0.0.1
|
||||
github.com/rs/zerolog v1.35.1
|
||||
github.com/rs/zerolog v1.35.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
go.mau.fi/util v0.9.9-0.20260511124621-9241e81bdf25
|
||||
go.mau.fi/util v0.9.8
|
||||
golang.org/x/crypto v0.50.0
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f
|
||||
golang.org/x/net v0.53.0
|
||||
golang.org/x/sync v0.20.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
maunium.net/go/mautrix v0.27.1-0.20260513120123-5fba7e3afae4
|
||||
maunium.net/go/mautrix v0.27.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
@ -32,7 +31,7 @@ require (
|
|||
github.com/lib/pq v1.12.3 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.44 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.42 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20260330135022-df67b199bc81 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
||||
|
|
@ -44,6 +43,7 @@ require (
|
|||
github.com/yuin/goldmark v1.8.2 // indirect
|
||||
go.mau.fi/zeroconfig v0.2.0 // indirect
|
||||
golang.org/x/mod v0.35.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.43.0 // indirect
|
||||
golang.org/x/text v0.36.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
|
|
|
|||
16
go.sum
16
go.sum
|
|
@ -30,8 +30,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
|||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0=
|
||||
github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
|
||||
github.com/mattn/go-sqlite3 v1.14.44 h1:3VSe+xafpbzsLbdr2AWlAZk9yRHiBhTBakioXaCKTF8=
|
||||
github.com/mattn/go-sqlite3 v1.14.44/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ=
|
||||
github.com/mattn/go-sqlite3 v1.14.42 h1:MigqEP4ZmHw3aIdIT7T+9TLa90Z6smwcthx+Azv4Cgo=
|
||||
github.com/mattn/go-sqlite3 v1.14.42/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ=
|
||||
github.com/petermattis/goid v0.0.0-20260330135022-df67b199bc81 h1:WDsQxOJDy0N1VRAjXLpi8sCEZRSGarLWQevDxpTBRrM=
|
||||
github.com/petermattis/goid v0.0.0-20260330135022-df67b199bc81/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
|
|
@ -42,8 +42,8 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR
|
|||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI=
|
||||
github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw=
|
||||
github.com/rs/zerolog v1.35.0 h1:VD0ykx7HMiMJytqINBsKcbLS+BJ4WYjz+05us+LRTdI=
|
||||
github.com/rs/zerolog v1.35.0/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
|
|
@ -61,8 +61,8 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
|||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE=
|
||||
github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
go.mau.fi/util v0.9.9-0.20260511124621-9241e81bdf25 h1:YPEmc+li7TF6C9AdRTcSLMb6yCHdF27/wNT7kFLIVNg=
|
||||
go.mau.fi/util v0.9.9-0.20260511124621-9241e81bdf25/go.mod h1:jE9FfhbgEgAwxei6lomO9v8zdCIATcquONUu4vjRwSs=
|
||||
go.mau.fi/util v0.9.8 h1:+/jf8eM2dAT2wx9UidmaneH28r/CSCKCniCyby1qWz8=
|
||||
go.mau.fi/util v0.9.8/go.mod h1:up/5mbzH2M1pSBNXqRxODn8dg/hEKbLJu92W4/SNAX0=
|
||||
go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU=
|
||||
go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w=
|
||||
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||
|
|
@ -91,5 +91,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.27.1-0.20260513120123-5fba7e3afae4 h1:zNC9eVAhw8FhKpM3AxNAh/iy75UEYX91uJUvqqAYlvo=
|
||||
maunium.net/go/mautrix v0.27.1-0.20260513120123-5fba7e3afae4/go.mod h1:3sOGhXi3P1V6/NruTA0gujkvTypXVUraWktCuTGyDuM=
|
||||
maunium.net/go/mautrix v0.27.0 h1:yfEYwoIluVWkofUgbZl9gP4i5nQTF+QNsxtb+r5bKlM=
|
||||
maunium.net/go/mautrix v0.27.0/go.mod h1:7QpEQiTy6p4LHkXXaZI+N46tGYy8HMhD0JjzZAFoFWs=
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ func supportedIfFFmpeg() event.CapabilitySupportLevel {
|
|||
}
|
||||
|
||||
func capID() string {
|
||||
base := "fi.mau.signal.capabilities.2026_05_12"
|
||||
base := "fi.mau.signal.capabilities.2025_12_09"
|
||||
if ffmpeg.Supported() {
|
||||
return base + "+ffmpeg"
|
||||
}
|
||||
|
|
@ -111,8 +111,7 @@ var signalCaps = &event.RoomFeatures{
|
|||
},
|
||||
event.CapMsgSticker: {
|
||||
MimeTypes: map[string]event.CapabilitySupportLevel{
|
||||
// Signal clients will only render static webp, so apng is preferred
|
||||
"image/webp": event.CapLevelPartialSupport,
|
||||
"image/webp": event.CapLevelFullySupported,
|
||||
"image/png": event.CapLevelFullySupported,
|
||||
"image/apng": event.CapLevelFullySupported,
|
||||
"image/gif": supportedIfFFmpeg(),
|
||||
|
|
@ -212,7 +211,6 @@ var signalGeneralCaps = &bridgev2.NetworkGeneralCapabilities{
|
|||
AggressiveUpdateInfo: true,
|
||||
ImplicitReadReceipts: true,
|
||||
Provisioning: bridgev2.ProvisioningCapabilities{
|
||||
ImagePackImport: true,
|
||||
ResolveIdentifier: bridgev2.ResolveIdentifierCapabilities{
|
||||
CreateDM: true,
|
||||
LookupPhone: true,
|
||||
|
|
@ -237,5 +235,5 @@ func (s *SignalConnector) GetCapabilities() *bridgev2.NetworkGeneralCapabilities
|
|||
}
|
||||
|
||||
func (s *SignalConnector) GetBridgeInfoVersion() (info, capabilities int) {
|
||||
return 1, 8
|
||||
return 1, 7
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import (
|
|||
"maunium.net/go/mautrix/bridgev2"
|
||||
"maunium.net/go/mautrix/bridgev2/networkid"
|
||||
"maunium.net/go/mautrix/bridgev2/status"
|
||||
"maunium.net/go/mautrix/event"
|
||||
|
||||
"go.mau.fi/mautrix-signal/pkg/signalid"
|
||||
"go.mau.fi/mautrix-signal/pkg/signalmeow"
|
||||
|
|
@ -47,7 +46,6 @@ type SignalClient struct {
|
|||
var (
|
||||
_ bridgev2.NetworkAPI = (*SignalClient)(nil)
|
||||
_ bridgev2.BackgroundSyncingNetworkAPI = (*SignalClient)(nil)
|
||||
_ bridgev2.StickerImportingNetworkAPI = (*SignalClient)(nil)
|
||||
)
|
||||
|
||||
var pushCfg = &bridgev2.PushConfig{
|
||||
|
|
@ -78,14 +76,6 @@ func (s *SignalClient) RegisterPushNotifications(ctx context.Context, pushType b
|
|||
}
|
||||
}
|
||||
|
||||
func (s *SignalClient) DownloadImagePack(ctx context.Context, url string) (*bridgev2.ImportedImagePack, error) {
|
||||
return s.Main.MsgConv.DownloadImagePack(ctx, url)
|
||||
}
|
||||
|
||||
func (s *SignalClient) ListImagePacks(ctx context.Context) ([]*event.ImagePackMetadata, error) {
|
||||
return []*event.ImagePackMetadata{}, nil
|
||||
}
|
||||
|
||||
func (s *SignalClient) LogoutRemote(ctx context.Context) {
|
||||
if s.Client == nil {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"maunium.net/go/mautrix/bridgev2"
|
||||
|
|
@ -29,7 +30,6 @@ func (s *SignalConnector) Download(ctx context.Context, mediaID networkid.MediaI
|
|||
return nil, fmt.Errorf("failed to parse direct media id: %w", err)
|
||||
}
|
||||
|
||||
var rawDataResp []byte
|
||||
switch info := info.(type) {
|
||||
case *signalid.DirectMediaAttachment:
|
||||
log.Info().
|
||||
|
|
@ -76,11 +76,18 @@ func (s *SignalConnector) Download(ctx context.Context, mediaID networkid.MediaI
|
|||
return nil, fmt.Errorf("failed to to get group master key: %w", err)
|
||||
}
|
||||
|
||||
rawDataResp, err = client.Client.DownloadGroupAvatar(ctx, info.GroupAvatarPath, groupMasterKey)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Direct download failed")
|
||||
return nil, err
|
||||
}
|
||||
return &mediaproxy.GetMediaResponseCallback{
|
||||
Callback: func(w io.Writer) (int64, error) {
|
||||
data, err := client.Client.DownloadGroupAvatar(ctx, info.GroupAvatarPath, groupMasterKey)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Direct download failed")
|
||||
return 0, err
|
||||
}
|
||||
|
||||
_, err = w.Write(data)
|
||||
return int64(len(data)), err
|
||||
},
|
||||
}, nil
|
||||
case *signalid.DirectMediaProfileAvatar:
|
||||
log.Info().
|
||||
Stringer("user_id", info.UserID).
|
||||
|
|
@ -104,27 +111,19 @@ func (s *SignalConnector) Download(ctx context.Context, mediaID networkid.MediaI
|
|||
return nil, fmt.Errorf("profile key not found")
|
||||
}
|
||||
|
||||
rawDataResp, err = client.Client.DownloadUserAvatar(ctx, info.ProfileAvatarPath, *profileKey)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Direct download failed")
|
||||
return nil, err
|
||||
}
|
||||
case *signalid.DirectMediaSticker:
|
||||
log.Info().
|
||||
Hex("pack_id", info.PackID).
|
||||
Uint32("sticker_id", info.StickerID).
|
||||
Msg("Direct downloading sticker")
|
||||
return &mediaproxy.GetMediaResponseCallback{
|
||||
Callback: func(w io.Writer) (int64, error) {
|
||||
data, err := client.Client.DownloadUserAvatar(ctx, info.ProfileAvatarPath, *profileKey)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Direct download failed")
|
||||
return 0, err
|
||||
}
|
||||
|
||||
rawDataResp, err = signalmeow.DownloadStickerPackItem(ctx, info.PackID, info.PackKey, info.StickerID)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Direct download failed")
|
||||
return nil, err
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
return int64(len(data)), err
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("no downloader for direct media type: %T", info)
|
||||
}
|
||||
if rawDataResp == nil {
|
||||
return nil, fmt.Errorf("unexpected fallthrough with no data")
|
||||
}
|
||||
return mediaproxy.GetMediaResponseRawData(rawDataResp), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,11 +159,11 @@ func signal_destroy_identity_key_store_callback(storeCtx unsafe.Pointer) {
|
|||
func (ctx *CallbackContext) wrapIdentityKeyStore(store IdentityKeyStore) C.SignalConstPointerFfiIdentityKeyStoreStruct {
|
||||
return C.SignalConstPointerFfiIdentityKeyStoreStruct{&C.SignalIdentityKeyStore{
|
||||
ctx: wrapStore(ctx, store),
|
||||
get_local_identity_key_pair: C.SignalFfiIdentityKeyStoreGetLocalIdentityKeyPair(C.signal_get_identity_key_pair_callback),
|
||||
get_local_registration_id: C.SignalFfiIdentityKeyStoreGetLocalRegistrationId(C.signal_get_local_registration_id_callback),
|
||||
get_identity_key: C.SignalFfiIdentityKeyStoreGetIdentityKey(C.signal_get_identity_key_callback),
|
||||
save_identity_key: C.SignalFfiIdentityKeyStoreSaveIdentityKey(C.signal_save_identity_key_callback),
|
||||
is_trusted_identity: C.SignalFfiIdentityKeyStoreIsTrustedIdentity(C.signal_is_trusted_identity_callback),
|
||||
destroy: C.SignalFfiIdentityKeyStoreDestroy(C.signal_destroy_identity_key_store_callback),
|
||||
get_local_identity_key_pair: C.SignalFfiBridgeIdentityKeyStoreGetLocalIdentityKeyPair(C.signal_get_identity_key_pair_callback),
|
||||
get_local_registration_id: C.SignalFfiBridgeIdentityKeyStoreGetLocalRegistrationId(C.signal_get_local_registration_id_callback),
|
||||
get_identity_key: C.SignalFfiBridgeIdentityKeyStoreGetIdentityKey(C.signal_get_identity_key_callback),
|
||||
save_identity_key: C.SignalFfiBridgeIdentityKeyStoreSaveIdentityKey(C.signal_save_identity_key_callback),
|
||||
is_trusted_identity: C.SignalFfiBridgeIdentityKeyStoreIsTrustedIdentity(C.signal_is_trusted_identity_callback),
|
||||
destroy: C.SignalFfiBridgeIdentityKeyStoreDestroy(C.signal_destroy_identity_key_store_callback),
|
||||
}}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,9 +77,9 @@ func signal_destroy_kyber_pre_key_store_callback(storeCtx unsafe.Pointer) {
|
|||
func (ctx *CallbackContext) wrapKyberPreKeyStore(store KyberPreKeyStore) C.SignalConstPointerFfiKyberPreKeyStoreStruct {
|
||||
return C.SignalConstPointerFfiKyberPreKeyStoreStruct{&C.SignalKyberPreKeyStore{
|
||||
ctx: wrapStore(ctx, store),
|
||||
load_kyber_pre_key: C.SignalFfiKyberPreKeyStoreLoadKyberPreKey(C.signal_load_kyber_pre_key_callback),
|
||||
store_kyber_pre_key: C.SignalFfiKyberPreKeyStoreStoreKyberPreKey(C.signal_store_kyber_pre_key_callback),
|
||||
mark_kyber_pre_key_used: C.SignalFfiKyberPreKeyStoreMarkKyberPreKeyUsed(C.signal_mark_kyber_pre_key_used_callback),
|
||||
destroy: C.SignalFfiKyberPreKeyStoreDestroy(C.signal_destroy_kyber_pre_key_store_callback),
|
||||
load_kyber_pre_key: C.SignalFfiBridgeKyberPreKeyStoreLoadKyberPreKey(C.signal_load_kyber_pre_key_callback),
|
||||
store_kyber_pre_key: C.SignalFfiBridgeKyberPreKeyStoreStoreKyberPreKey(C.signal_store_kyber_pre_key_callback),
|
||||
mark_kyber_pre_key_used: C.SignalFfiBridgeKyberPreKeyStoreMarkKyberPreKeyUsed(C.signal_mark_kyber_pre_key_used_callback),
|
||||
destroy: C.SignalFfiBridgeKyberPreKeyStoreDestroy(C.signal_destroy_kyber_pre_key_store_callback),
|
||||
}}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit bbc16886cae2feab1cd1fe271ccc651e8860ce96
|
||||
Subproject commit b58bd7d5dfa0a391486df4210fd83bab96b9b479
|
||||
|
|
@ -629,27 +629,6 @@ typedef struct {
|
|||
const SignalHttpRequest *raw;
|
||||
} SignalConstPointerHttpRequest;
|
||||
|
||||
/**
|
||||
* The fixed-width binary representation of a ServiceId.
|
||||
*
|
||||
* Rarely used. The variable-width format that privileges ACIs is preferred.
|
||||
*/
|
||||
typedef uint8_t SignalServiceIdFixedWidthBinaryBytes[17];
|
||||
|
||||
typedef struct {
|
||||
const uint32_t *base;
|
||||
size_t length;
|
||||
} SignalBorrowedSliceOfu32;
|
||||
|
||||
typedef struct {
|
||||
const SignalCiphertextMessage *raw;
|
||||
} SignalConstPointerCiphertextMessage;
|
||||
|
||||
typedef struct {
|
||||
const SignalConstPointerCiphertextMessage *base;
|
||||
size_t length;
|
||||
} SignalBorrowedSliceOfConstPointerCiphertextMessage;
|
||||
|
||||
/**
|
||||
* A wrapper type for raw UUIDs, because C treats arrays specially in argument position.
|
||||
*/
|
||||
|
|
@ -657,6 +636,13 @@ typedef struct {
|
|||
uint8_t bytes[16];
|
||||
} SignalUuid;
|
||||
|
||||
/**
|
||||
* The fixed-width binary representation of a ServiceId.
|
||||
*
|
||||
* Rarely used. The variable-width format that privileges ACIs is preferred.
|
||||
*/
|
||||
typedef uint8_t SignalServiceIdFixedWidthBinaryBytes[17];
|
||||
|
||||
typedef struct {
|
||||
SignalPrivateKey *raw;
|
||||
} SignalMutPointerPrivateKey;
|
||||
|
|
@ -768,6 +754,10 @@ typedef struct {
|
|||
const SignalPlaintextContent *raw;
|
||||
} SignalConstPointerPlaintextContent;
|
||||
|
||||
typedef struct {
|
||||
const SignalCiphertextMessage *raw;
|
||||
} SignalConstPointerCiphertextMessage;
|
||||
|
||||
typedef struct {
|
||||
SignalConnectionInfo *raw;
|
||||
} SignalMutPointerConnectionInfo;
|
||||
|
|
@ -792,18 +782,20 @@ typedef struct {
|
|||
SignalSessionRecord *raw;
|
||||
} SignalMutPointerSessionRecord;
|
||||
|
||||
typedef int (*SignalFfiSessionStoreLoadSession)(void *ctx, SignalMutPointerSessionRecord *out, SignalMutPointerProtocolAddress address);
|
||||
typedef int (*SignalFfiBridgeSessionStoreLoadSession)(void *ctx, SignalMutPointerSessionRecord *out, SignalMutPointerProtocolAddress address);
|
||||
|
||||
typedef int (*SignalFfiSessionStoreStoreSession)(void *ctx, SignalMutPointerProtocolAddress address, SignalMutPointerSessionRecord record);
|
||||
typedef int (*SignalFfiBridgeSessionStoreStoreSession)(void *ctx, SignalMutPointerProtocolAddress address, SignalMutPointerSessionRecord record);
|
||||
|
||||
typedef void (*SignalFfiSessionStoreDestroy)(void *ctx);
|
||||
typedef void (*SignalFfiBridgeSessionStoreDestroy)(void *ctx);
|
||||
|
||||
typedef struct {
|
||||
void *ctx;
|
||||
SignalFfiSessionStoreLoadSession load_session;
|
||||
SignalFfiSessionStoreStoreSession store_session;
|
||||
SignalFfiSessionStoreDestroy destroy;
|
||||
} SignalSessionStore;
|
||||
SignalFfiBridgeSessionStoreLoadSession load_session;
|
||||
SignalFfiBridgeSessionStoreStoreSession store_session;
|
||||
SignalFfiBridgeSessionStoreDestroy destroy;
|
||||
} SignalFfiBridgeSessionStoreStruct;
|
||||
|
||||
typedef SignalFfiBridgeSessionStoreStruct SignalSessionStore;
|
||||
|
||||
typedef struct {
|
||||
const SignalSessionStore *raw;
|
||||
|
|
@ -818,27 +810,29 @@ typedef struct {
|
|||
SignalMutPointerPublicKey second;
|
||||
} SignalPairOfMutPointerPrivateKeyMutPointerPublicKey;
|
||||
|
||||
typedef int (*SignalFfiIdentityKeyStoreGetLocalIdentityKeyPair)(void *ctx, SignalPairOfMutPointerPrivateKeyMutPointerPublicKey *out);
|
||||
typedef int (*SignalFfiBridgeIdentityKeyStoreGetLocalIdentityKeyPair)(void *ctx, SignalPairOfMutPointerPrivateKeyMutPointerPublicKey *out);
|
||||
|
||||
typedef int (*SignalFfiIdentityKeyStoreGetLocalRegistrationId)(void *ctx, uint32_t *out);
|
||||
typedef int (*SignalFfiBridgeIdentityKeyStoreGetLocalRegistrationId)(void *ctx, uint32_t *out);
|
||||
|
||||
typedef int (*SignalFfiIdentityKeyStoreGetIdentityKey)(void *ctx, SignalMutPointerPublicKey *out, SignalMutPointerProtocolAddress address);
|
||||
typedef int (*SignalFfiBridgeIdentityKeyStoreGetIdentityKey)(void *ctx, SignalMutPointerPublicKey *out, SignalMutPointerProtocolAddress address);
|
||||
|
||||
typedef int (*SignalFfiIdentityKeyStoreSaveIdentityKey)(void *ctx, uint8_t *out, SignalMutPointerProtocolAddress address, SignalMutPointerPublicKey public_key);
|
||||
typedef int (*SignalFfiBridgeIdentityKeyStoreSaveIdentityKey)(void *ctx, uint8_t *out, SignalMutPointerProtocolAddress address, SignalMutPointerPublicKey public_key);
|
||||
|
||||
typedef int (*SignalFfiIdentityKeyStoreIsTrustedIdentity)(void *ctx, bool *out, SignalMutPointerProtocolAddress address, SignalMutPointerPublicKey public_key, uint32_t direction);
|
||||
typedef int (*SignalFfiBridgeIdentityKeyStoreIsTrustedIdentity)(void *ctx, bool *out, SignalMutPointerProtocolAddress address, SignalMutPointerPublicKey public_key, uint32_t direction);
|
||||
|
||||
typedef void (*SignalFfiIdentityKeyStoreDestroy)(void *ctx);
|
||||
typedef void (*SignalFfiBridgeIdentityKeyStoreDestroy)(void *ctx);
|
||||
|
||||
typedef struct {
|
||||
void *ctx;
|
||||
SignalFfiIdentityKeyStoreGetLocalIdentityKeyPair get_local_identity_key_pair;
|
||||
SignalFfiIdentityKeyStoreGetLocalRegistrationId get_local_registration_id;
|
||||
SignalFfiIdentityKeyStoreGetIdentityKey get_identity_key;
|
||||
SignalFfiIdentityKeyStoreSaveIdentityKey save_identity_key;
|
||||
SignalFfiIdentityKeyStoreIsTrustedIdentity is_trusted_identity;
|
||||
SignalFfiIdentityKeyStoreDestroy destroy;
|
||||
} SignalIdentityKeyStore;
|
||||
SignalFfiBridgeIdentityKeyStoreGetLocalIdentityKeyPair get_local_identity_key_pair;
|
||||
SignalFfiBridgeIdentityKeyStoreGetLocalRegistrationId get_local_registration_id;
|
||||
SignalFfiBridgeIdentityKeyStoreGetIdentityKey get_identity_key;
|
||||
SignalFfiBridgeIdentityKeyStoreSaveIdentityKey save_identity_key;
|
||||
SignalFfiBridgeIdentityKeyStoreIsTrustedIdentity is_trusted_identity;
|
||||
SignalFfiBridgeIdentityKeyStoreDestroy destroy;
|
||||
} SignalFfiBridgeIdentityKeyStoreStruct;
|
||||
|
||||
typedef SignalFfiBridgeIdentityKeyStoreStruct SignalIdentityKeyStore;
|
||||
|
||||
typedef struct {
|
||||
const SignalIdentityKeyStore *raw;
|
||||
|
|
@ -852,21 +846,23 @@ typedef struct {
|
|||
SignalPreKeyRecord *raw;
|
||||
} SignalMutPointerPreKeyRecord;
|
||||
|
||||
typedef int (*SignalFfiPreKeyStoreLoadPreKey)(void *ctx, SignalMutPointerPreKeyRecord *out, uint32_t id);
|
||||
typedef int (*SignalFfiBridgePreKeyStoreLoadPreKey)(void *ctx, SignalMutPointerPreKeyRecord *out, uint32_t id);
|
||||
|
||||
typedef int (*SignalFfiPreKeyStoreStorePreKey)(void *ctx, uint32_t id, SignalMutPointerPreKeyRecord record);
|
||||
typedef int (*SignalFfiBridgePreKeyStoreStorePreKey)(void *ctx, uint32_t id, SignalMutPointerPreKeyRecord record);
|
||||
|
||||
typedef int (*SignalFfiPreKeyStoreRemovePreKey)(void *ctx, uint32_t id);
|
||||
typedef int (*SignalFfiBridgePreKeyStoreRemovePreKey)(void *ctx, uint32_t id);
|
||||
|
||||
typedef void (*SignalFfiPreKeyStoreDestroy)(void *ctx);
|
||||
typedef void (*SignalFfiBridgePreKeyStoreDestroy)(void *ctx);
|
||||
|
||||
typedef struct {
|
||||
void *ctx;
|
||||
SignalFfiPreKeyStoreLoadPreKey load_pre_key;
|
||||
SignalFfiPreKeyStoreStorePreKey store_pre_key;
|
||||
SignalFfiPreKeyStoreRemovePreKey remove_pre_key;
|
||||
SignalFfiPreKeyStoreDestroy destroy;
|
||||
} SignalPreKeyStore;
|
||||
SignalFfiBridgePreKeyStoreLoadPreKey load_pre_key;
|
||||
SignalFfiBridgePreKeyStoreStorePreKey store_pre_key;
|
||||
SignalFfiBridgePreKeyStoreRemovePreKey remove_pre_key;
|
||||
SignalFfiBridgePreKeyStoreDestroy destroy;
|
||||
} SignalFfiBridgePreKeyStoreStruct;
|
||||
|
||||
typedef SignalFfiBridgePreKeyStoreStruct SignalPreKeyStore;
|
||||
|
||||
typedef struct {
|
||||
const SignalPreKeyStore *raw;
|
||||
|
|
@ -876,18 +872,20 @@ typedef struct {
|
|||
SignalSignedPreKeyRecord *raw;
|
||||
} SignalMutPointerSignedPreKeyRecord;
|
||||
|
||||
typedef int (*SignalFfiSignedPreKeyStoreLoadSignedPreKey)(void *ctx, SignalMutPointerSignedPreKeyRecord *out, uint32_t id);
|
||||
typedef int (*SignalFfiBridgeSignedPreKeyStoreLoadSignedPreKey)(void *ctx, SignalMutPointerSignedPreKeyRecord *out, uint32_t id);
|
||||
|
||||
typedef int (*SignalFfiSignedPreKeyStoreStoreSignedPreKey)(void *ctx, uint32_t id, SignalMutPointerSignedPreKeyRecord record);
|
||||
typedef int (*SignalFfiBridgeSignedPreKeyStoreStoreSignedPreKey)(void *ctx, uint32_t id, SignalMutPointerSignedPreKeyRecord record);
|
||||
|
||||
typedef void (*SignalFfiSignedPreKeyStoreDestroy)(void *ctx);
|
||||
typedef void (*SignalFfiBridgeSignedPreKeyStoreDestroy)(void *ctx);
|
||||
|
||||
typedef struct {
|
||||
void *ctx;
|
||||
SignalFfiSignedPreKeyStoreLoadSignedPreKey load_signed_pre_key;
|
||||
SignalFfiSignedPreKeyStoreStoreSignedPreKey store_signed_pre_key;
|
||||
SignalFfiSignedPreKeyStoreDestroy destroy;
|
||||
} SignalSignedPreKeyStore;
|
||||
SignalFfiBridgeSignedPreKeyStoreLoadSignedPreKey load_signed_pre_key;
|
||||
SignalFfiBridgeSignedPreKeyStoreStoreSignedPreKey store_signed_pre_key;
|
||||
SignalFfiBridgeSignedPreKeyStoreDestroy destroy;
|
||||
} SignalFfiBridgeSignedPreKeyStoreStruct;
|
||||
|
||||
typedef SignalFfiBridgeSignedPreKeyStoreStruct SignalSignedPreKeyStore;
|
||||
|
||||
typedef struct {
|
||||
const SignalSignedPreKeyStore *raw;
|
||||
|
|
@ -897,21 +895,23 @@ typedef struct {
|
|||
SignalKyberPreKeyRecord *raw;
|
||||
} SignalMutPointerKyberPreKeyRecord;
|
||||
|
||||
typedef int (*SignalFfiKyberPreKeyStoreLoadKyberPreKey)(void *ctx, SignalMutPointerKyberPreKeyRecord *out, uint32_t id);
|
||||
typedef int (*SignalFfiBridgeKyberPreKeyStoreLoadKyberPreKey)(void *ctx, SignalMutPointerKyberPreKeyRecord *out, uint32_t id);
|
||||
|
||||
typedef int (*SignalFfiKyberPreKeyStoreStoreKyberPreKey)(void *ctx, uint32_t id, SignalMutPointerKyberPreKeyRecord record);
|
||||
typedef int (*SignalFfiBridgeKyberPreKeyStoreStoreKyberPreKey)(void *ctx, uint32_t id, SignalMutPointerKyberPreKeyRecord record);
|
||||
|
||||
typedef int (*SignalFfiKyberPreKeyStoreMarkKyberPreKeyUsed)(void *ctx, uint32_t id, uint32_t ec_prekey_id, SignalMutPointerPublicKey base_key);
|
||||
typedef int (*SignalFfiBridgeKyberPreKeyStoreMarkKyberPreKeyUsed)(void *ctx, uint32_t id, uint32_t ec_prekey_id, SignalMutPointerPublicKey base_key);
|
||||
|
||||
typedef void (*SignalFfiKyberPreKeyStoreDestroy)(void *ctx);
|
||||
typedef void (*SignalFfiBridgeKyberPreKeyStoreDestroy)(void *ctx);
|
||||
|
||||
typedef struct {
|
||||
void *ctx;
|
||||
SignalFfiKyberPreKeyStoreLoadKyberPreKey load_kyber_pre_key;
|
||||
SignalFfiKyberPreKeyStoreStoreKyberPreKey store_kyber_pre_key;
|
||||
SignalFfiKyberPreKeyStoreMarkKyberPreKeyUsed mark_kyber_pre_key_used;
|
||||
SignalFfiKyberPreKeyStoreDestroy destroy;
|
||||
} SignalKyberPreKeyStore;
|
||||
SignalFfiBridgeKyberPreKeyStoreLoadKyberPreKey load_kyber_pre_key;
|
||||
SignalFfiBridgeKyberPreKeyStoreStoreKyberPreKey store_kyber_pre_key;
|
||||
SignalFfiBridgeKyberPreKeyStoreMarkKyberPreKeyUsed mark_kyber_pre_key_used;
|
||||
SignalFfiBridgeKyberPreKeyStoreDestroy destroy;
|
||||
} SignalFfiBridgeKyberPreKeyStoreStruct;
|
||||
|
||||
typedef SignalFfiBridgeKyberPreKeyStoreStruct SignalKyberPreKeyStore;
|
||||
|
||||
typedef struct {
|
||||
const SignalKyberPreKeyStore *raw;
|
||||
|
|
@ -1047,18 +1047,20 @@ typedef struct {
|
|||
SignalSenderKeyRecord *raw;
|
||||
} SignalMutPointerSenderKeyRecord;
|
||||
|
||||
typedef int (*SignalFfiSenderKeyStoreLoadSenderKey)(void *ctx, SignalMutPointerSenderKeyRecord *out, SignalMutPointerProtocolAddress sender, SignalUuid distribution_id);
|
||||
typedef int (*SignalFfiBridgeSenderKeyStoreLoadSenderKey)(void *ctx, SignalMutPointerSenderKeyRecord *out, SignalMutPointerProtocolAddress sender, SignalUuid distribution_id);
|
||||
|
||||
typedef int (*SignalFfiSenderKeyStoreStoreSenderKey)(void *ctx, SignalMutPointerProtocolAddress sender, SignalUuid distribution_id, SignalMutPointerSenderKeyRecord record);
|
||||
typedef int (*SignalFfiBridgeSenderKeyStoreStoreSenderKey)(void *ctx, SignalMutPointerProtocolAddress sender, SignalUuid distribution_id, SignalMutPointerSenderKeyRecord record);
|
||||
|
||||
typedef void (*SignalFfiSenderKeyStoreDestroy)(void *ctx);
|
||||
typedef void (*SignalFfiBridgeSenderKeyStoreDestroy)(void *ctx);
|
||||
|
||||
typedef struct {
|
||||
void *ctx;
|
||||
SignalFfiSenderKeyStoreLoadSenderKey load_sender_key;
|
||||
SignalFfiSenderKeyStoreStoreSenderKey store_sender_key;
|
||||
SignalFfiSenderKeyStoreDestroy destroy;
|
||||
} SignalSenderKeyStore;
|
||||
SignalFfiBridgeSenderKeyStoreLoadSenderKey load_sender_key;
|
||||
SignalFfiBridgeSenderKeyStoreStoreSenderKey store_sender_key;
|
||||
SignalFfiBridgeSenderKeyStoreDestroy destroy;
|
||||
} SignalFfiBridgeSenderKeyStoreStruct;
|
||||
|
||||
typedef SignalFfiBridgeSenderKeyStoreStruct SignalSenderKeyStore;
|
||||
|
||||
typedef struct {
|
||||
const SignalSenderKeyStore *raw;
|
||||
|
|
@ -1115,11 +1117,6 @@ typedef struct {
|
|||
SignalFfiLoggerDestroy destroy;
|
||||
} SignalFfiLoggerStruct;
|
||||
|
||||
typedef struct {
|
||||
SignalOwnedBuffer first;
|
||||
SignalOwnedBuffer second;
|
||||
} SignalPairOfOwnedBufferOfc_ucharOwnedBufferOfc_uchar;
|
||||
|
||||
/**
|
||||
* A C callback used to report the results of Rust futures.
|
||||
*
|
||||
|
|
@ -1130,10 +1127,10 @@ typedef struct {
|
|||
* completed once.
|
||||
*/
|
||||
typedef struct {
|
||||
void (*complete)(SignalFfiError *error, const SignalPairOfOwnedBufferOfc_ucharOwnedBufferOfc_uchar *result, const void *context);
|
||||
void (*complete)(SignalFfiError *error, const SignalOwnedBuffer *result, const void *context);
|
||||
const void *context;
|
||||
SignalCancellationId cancellation_id;
|
||||
} SignalCPromisePairOfOwnedBufferOfc_ucharOwnedBufferOfc_uchar;
|
||||
} SignalCPromiseOwnedBufferOfc_uchar;
|
||||
|
||||
typedef struct {
|
||||
const SignalUnauthenticatedChatConnection *raw;
|
||||
|
|
@ -1203,18 +1200,20 @@ typedef struct {
|
|||
const SignalMessageBackupValidationOutcome *raw;
|
||||
} SignalConstPointerMessageBackupValidationOutcome;
|
||||
|
||||
typedef int (*SignalFfiInputStreamRead)(void *ctx, size_t *out, SignalBorrowedMutableBuffer buf);
|
||||
typedef int (*SignalFfiBridgeInputStreamRead)(void *ctx, size_t *out, SignalBorrowedMutableBuffer buf);
|
||||
|
||||
typedef int (*SignalFfiInputStreamSkip)(void *ctx, uint64_t amount);
|
||||
typedef int (*SignalFfiBridgeInputStreamSkip)(void *ctx, uint64_t amount);
|
||||
|
||||
typedef void (*SignalFfiInputStreamDestroy)(void *ctx);
|
||||
typedef void (*SignalFfiBridgeInputStreamDestroy)(void *ctx);
|
||||
|
||||
typedef struct {
|
||||
void *ctx;
|
||||
SignalFfiInputStreamRead read;
|
||||
SignalFfiInputStreamSkip skip;
|
||||
SignalFfiInputStreamDestroy destroy;
|
||||
} SignalInputStream;
|
||||
SignalFfiBridgeInputStreamRead read;
|
||||
SignalFfiBridgeInputStreamSkip skip;
|
||||
SignalFfiBridgeInputStreamDestroy destroy;
|
||||
} SignalFfiBridgeInputStreamStruct;
|
||||
|
||||
typedef SignalFfiBridgeInputStreamStruct SignalInputStream;
|
||||
|
||||
typedef struct {
|
||||
const SignalInputStream *raw;
|
||||
|
|
@ -1648,7 +1647,9 @@ typedef struct {
|
|||
SignalValidatingMac *raw;
|
||||
} SignalMutPointerValidatingMac;
|
||||
|
||||
typedef SignalInputStream SignalSyncInputStream;
|
||||
typedef SignalFfiBridgeInputStreamStruct SignalFfiBridgeSyncInputStreamStruct;
|
||||
|
||||
typedef SignalFfiBridgeSyncInputStreamStruct SignalSyncInputStream;
|
||||
|
||||
typedef struct {
|
||||
const SignalSyncInputStream *raw;
|
||||
|
|
@ -1736,10 +1737,6 @@ SignalFfiError *signal_authenticated_chat_connection_preconnect(SignalCPromisebo
|
|||
|
||||
SignalFfiError *signal_authenticated_chat_connection_send(SignalCPromiseFfiChatResponse *promise, SignalConstPointerTokioAsyncContext async_runtime, SignalConstPointerAuthenticatedChatConnection chat, SignalConstPointerHttpRequest http_request, uint32_t timeout_millis);
|
||||
|
||||
SignalFfiError *signal_authenticated_chat_connection_send_message(SignalCPromisebool *promise, SignalConstPointerTokioAsyncContext async_runtime, SignalConstPointerAuthenticatedChatConnection chat, const SignalServiceIdFixedWidthBinaryBytes *destination, uint64_t timestamp, SignalBorrowedSliceOfu32 device_ids, SignalBorrowedSliceOfu32 registration_ids, SignalBorrowedSliceOfConstPointerCiphertextMessage contents, bool online_only, bool is_urgent);
|
||||
|
||||
SignalFfiError *signal_authenticated_chat_connection_send_sync_message(SignalCPromisebool *promise, SignalConstPointerTokioAsyncContext async_runtime, SignalConstPointerAuthenticatedChatConnection chat, uint64_t timestamp, SignalBorrowedSliceOfu32 device_ids, SignalBorrowedSliceOfu32 registration_ids, SignalBorrowedSliceOfConstPointerCiphertextMessage contents, bool is_urgent);
|
||||
|
||||
SignalFfiError *signal_backup_auth_credential_check_valid_contents(SignalBorrowedBuffer params_bytes);
|
||||
|
||||
SignalFfiError *signal_backup_auth_credential_get_backup_id(uint8_t (*out)[16], SignalBorrowedBuffer credential_bytes);
|
||||
|
|
@ -1900,7 +1897,7 @@ SignalFfiError *signal_create_call_link_credential_request_issue_deterministic(S
|
|||
|
||||
SignalFfiError *signal_create_call_link_credential_response_check_valid_contents(SignalBorrowedBuffer response_bytes);
|
||||
|
||||
SignalFfiError *signal_decrypt_message(SignalOwnedBuffer *out, SignalConstPointerSignalMessage message, SignalConstPointerProtocolAddress protocol_address, SignalConstPointerProtocolAddress local_address, SignalConstPointerFfiSessionStoreStruct session_store, SignalConstPointerFfiIdentityKeyStoreStruct identity_key_store);
|
||||
SignalFfiError *signal_decrypt_message(SignalOwnedBuffer *out, SignalConstPointerSignalMessage message, SignalConstPointerProtocolAddress protocol_address, SignalConstPointerFfiSessionStoreStruct session_store, SignalConstPointerFfiIdentityKeyStoreStruct identity_key_store);
|
||||
|
||||
SignalFfiError *signal_decrypt_pre_key_message(SignalOwnedBuffer *out, SignalConstPointerPreKeySignalMessage message, SignalConstPointerProtocolAddress protocol_address, SignalConstPointerProtocolAddress local_address, SignalConstPointerFfiSessionStoreStruct session_store, SignalConstPointerFfiIdentityKeyStoreStruct identity_key_store, SignalConstPointerFfiPreKeyStoreStruct prekey_store, SignalConstPointerFfiSignedPreKeyStoreStruct signed_prekey_store, SignalConstPointerFfiKyberPreKeyStoreStruct kyber_prekey_store);
|
||||
|
||||
|
|
@ -2121,7 +2118,9 @@ bool signal_init_logger(SignalLogLevel max_level, SignalFfiLoggerStruct logger);
|
|||
|
||||
SignalFfiError *signal_key_transparency_aci_search_key(SignalOwnedBuffer *out, const SignalServiceIdFixedWidthBinaryBytes *aci);
|
||||
|
||||
SignalFfiError *signal_key_transparency_check(SignalCPromisePairOfOwnedBufferOfc_ucharOwnedBufferOfc_uchar *promise, SignalConstPointerTokioAsyncContext async_runtime, uint8_t environment, SignalConstPointerUnauthenticatedChatConnection chat_connection, const SignalServiceIdFixedWidthBinaryBytes *aci, SignalConstPointerPublicKey aci_identity_key, const char *e164, SignalOptionalBorrowedSliceOfc_uchar unidentified_access_key, SignalOptionalBorrowedSliceOfc_uchar username_hash, SignalOptionalBorrowedSliceOfc_uchar account_data, SignalOptionalBorrowedSliceOfc_uchar last_distinguished_tree_head, bool is_self_check, bool is_e164_discoverable);
|
||||
SignalFfiError *signal_key_transparency_check(SignalCPromiseOwnedBufferOfc_uchar *promise, SignalConstPointerTokioAsyncContext async_runtime, uint8_t environment, SignalConstPointerUnauthenticatedChatConnection chat_connection, const SignalServiceIdFixedWidthBinaryBytes *aci, SignalConstPointerPublicKey aci_identity_key, const char *e164, SignalOptionalBorrowedSliceOfc_uchar unidentified_access_key, SignalOptionalBorrowedSliceOfc_uchar username_hash, SignalOptionalBorrowedSliceOfc_uchar account_data, SignalBorrowedBuffer last_distinguished_tree_head, bool is_self_check, bool is_e164_discoverable);
|
||||
|
||||
SignalFfiError *signal_key_transparency_distinguished(SignalCPromiseOwnedBufferOfc_uchar *promise, SignalConstPointerTokioAsyncContext async_runtime, uint8_t environment, SignalConstPointerUnauthenticatedChatConnection chat_connection, SignalOptionalBorrowedSliceOfc_uchar last_distinguished_tree_head);
|
||||
|
||||
SignalFfiError *signal_key_transparency_e164_search_key(SignalOwnedBuffer *out, const char *e164);
|
||||
|
||||
|
|
@ -2355,7 +2354,7 @@ SignalFfiError *signal_privatekey_serialize(SignalOwnedBuffer *out, SignalConstP
|
|||
|
||||
SignalFfiError *signal_privatekey_sign(SignalOwnedBuffer *out, SignalConstPointerPrivateKey key, SignalBorrowedBuffer message);
|
||||
|
||||
SignalFfiError *signal_process_prekey_bundle(SignalConstPointerPreKeyBundle bundle, SignalConstPointerProtocolAddress protocol_address, SignalConstPointerProtocolAddress local_address, SignalConstPointerFfiSessionStoreStruct session_store, SignalConstPointerFfiIdentityKeyStoreStruct identity_key_store, uint64_t now);
|
||||
SignalFfiError *signal_process_prekey_bundle(SignalConstPointerPreKeyBundle bundle, SignalConstPointerProtocolAddress protocol_address, SignalConstPointerFfiSessionStoreStruct session_store, SignalConstPointerFfiIdentityKeyStoreStruct identity_key_store, uint64_t now);
|
||||
|
||||
SignalFfiError *signal_process_sender_key_distribution_message(SignalConstPointerProtocolAddress sender, SignalConstPointerSenderKeyDistributionMessage sender_key_distribution_message, SignalConstPointerFfiSenderKeyStoreStruct store);
|
||||
|
||||
|
|
@ -2783,8 +2782,6 @@ SignalFfiError *signal_unauthenticated_chat_connection_look_up_username_link(Sig
|
|||
|
||||
SignalFfiError *signal_unauthenticated_chat_connection_send(SignalCPromiseFfiChatResponse *promise, SignalConstPointerTokioAsyncContext async_runtime, SignalConstPointerUnauthenticatedChatConnection chat, SignalConstPointerHttpRequest http_request, uint32_t timeout_millis);
|
||||
|
||||
SignalFfiError *signal_unauthenticated_chat_connection_send_message(SignalCPromisebool *promise, SignalConstPointerTokioAsyncContext async_runtime, SignalConstPointerUnauthenticatedChatConnection chat, const SignalServiceIdFixedWidthBinaryBytes *destination, uint64_t timestamp, SignalBorrowedSliceOfu32 device_ids, SignalBorrowedSliceOfu32 registration_ids, SignalBorrowedSliceOfBuffers contents, uint8_t auth_kind, SignalOptionalBorrowedSliceOfc_uchar auth_buffer, bool online_only, bool is_urgent);
|
||||
|
||||
SignalFfiError *signal_unauthenticated_chat_connection_send_multi_recipient_message(SignalCPromiseOwnedBufferOfServiceIdFixedWidthBinaryBytes *promise, SignalConstPointerTokioAsyncContext async_runtime, SignalConstPointerUnauthenticatedChatConnection chat, SignalBorrowedBuffer payload, uint64_t timestamp, SignalBorrowedBuffer auth, bool online_only, bool is_urgent);
|
||||
|
||||
SignalFfiError *signal_unidentified_sender_message_content_deserialize(SignalMutPointerUnidentifiedSenderMessageContent *out, SignalBorrowedBuffer data);
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ func Encrypt(ctx context.Context, plaintext []byte, forAddress, localAddress *Ad
|
|||
return wrapCiphertextMessage(ciphertextMessage.raw), nil
|
||||
}
|
||||
|
||||
func Decrypt(ctx context.Context, message *Message, fromAddress, localAddress *Address, sessionStore SessionStore, identityStore IdentityKeyStore) ([]byte, error) {
|
||||
func Decrypt(ctx context.Context, message *Message, fromAddress *Address, sessionStore SessionStore, identityStore IdentityKeyStore) ([]byte, error) {
|
||||
callbackCtx := NewCallbackContext(ctx)
|
||||
defer callbackCtx.Unref()
|
||||
var decrypted C.SignalOwnedBuffer = C.SignalOwnedBuffer{}
|
||||
|
|
@ -57,7 +57,6 @@ func Decrypt(ctx context.Context, message *Message, fromAddress, localAddress *A
|
|||
&decrypted,
|
||||
message.constPtr(),
|
||||
fromAddress.constPtr(),
|
||||
localAddress.constPtr(),
|
||||
callbackCtx.wrapSessionStore(sessionStore),
|
||||
callbackCtx.wrapIdentityKeyStore(identityStore),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -27,14 +27,13 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func ProcessPreKeyBundle(ctx context.Context, bundle *PreKeyBundle, forAddress, localAddress *Address, sessionStore SessionStore, identityStore IdentityKeyStore) error {
|
||||
func ProcessPreKeyBundle(ctx context.Context, bundle *PreKeyBundle, forAddress *Address, sessionStore SessionStore, identityStore IdentityKeyStore) error {
|
||||
callbackCtx := NewCallbackContext(ctx)
|
||||
defer callbackCtx.Unref()
|
||||
var now C.uint64_t = C.uint64_t(time.Now().Unix())
|
||||
signalFfiError := C.signal_process_prekey_bundle(
|
||||
bundle.constPtr(),
|
||||
forAddress.constPtr(),
|
||||
localAddress.constPtr(),
|
||||
callbackCtx.wrapSessionStore(sessionStore),
|
||||
callbackCtx.wrapIdentityKeyStore(identityStore),
|
||||
now,
|
||||
|
|
|
|||
|
|
@ -76,9 +76,9 @@ func signal_destroy_pre_key_store_callback(storeCtx unsafe.Pointer) {
|
|||
func (ctx *CallbackContext) wrapPreKeyStore(store PreKeyStore) C.SignalConstPointerFfiPreKeyStoreStruct {
|
||||
return C.SignalConstPointerFfiPreKeyStoreStruct{&C.SignalPreKeyStore{
|
||||
ctx: wrapStore(ctx, store),
|
||||
load_pre_key: C.SignalFfiPreKeyStoreLoadPreKey(C.signal_load_pre_key_callback),
|
||||
store_pre_key: C.SignalFfiPreKeyStoreStorePreKey(C.signal_store_pre_key_callback),
|
||||
remove_pre_key: C.SignalFfiPreKeyStoreRemovePreKey(C.signal_remove_pre_key_callback),
|
||||
destroy: C.SignalFfiPreKeyStoreDestroy(C.signal_destroy_pre_key_store_callback),
|
||||
load_pre_key: C.SignalFfiBridgePreKeyStoreLoadPreKey(C.signal_load_pre_key_callback),
|
||||
store_pre_key: C.SignalFfiBridgePreKeyStoreStorePreKey(C.signal_store_pre_key_callback),
|
||||
remove_pre_key: C.SignalFfiBridgePreKeyStoreRemovePreKey(C.signal_remove_pre_key_callback),
|
||||
destroy: C.SignalFfiBridgePreKeyStoreDestroy(C.signal_destroy_pre_key_store_callback),
|
||||
}}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ func signal_destroy_sender_key_store_callback(storeCtx unsafe.Pointer) {
|
|||
func (ctx *CallbackContext) wrapSenderKeyStore(store SenderKeyStore) C.SignalConstPointerFfiSenderKeyStoreStruct {
|
||||
return C.SignalConstPointerFfiSenderKeyStoreStruct{&C.SignalSenderKeyStore{
|
||||
ctx: wrapStore(ctx, store),
|
||||
load_sender_key: C.SignalFfiSenderKeyStoreLoadSenderKey(C.signal_load_sender_key_callback),
|
||||
store_sender_key: C.SignalFfiSenderKeyStoreStoreSenderKey(C.signal_store_sender_key_callback),
|
||||
destroy: C.SignalFfiSenderKeyStoreDestroy(C.signal_destroy_sender_key_store_callback),
|
||||
load_sender_key: C.SignalFfiBridgeSenderKeyStoreLoadSenderKey(C.signal_load_sender_key_callback),
|
||||
store_sender_key: C.SignalFfiBridgeSenderKeyStoreStoreSenderKey(C.signal_store_sender_key_callback),
|
||||
destroy: C.SignalFfiBridgeSenderKeyStoreDestroy(C.signal_destroy_sender_key_store_callback),
|
||||
}}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import (
|
|||
"go.mau.fi/mautrix-signal/pkg/libsignalgo"
|
||||
)
|
||||
|
||||
func initializeSessions(t *testing.T, aliceStore, bobStore *InMemorySignalProtocolStore, bobAddress, aliceAddress *libsignalgo.Address) {
|
||||
func initializeSessions(t *testing.T, aliceStore, bobStore *InMemorySignalProtocolStore, bobAddress *libsignalgo.Address) {
|
||||
ctx := context.TODO()
|
||||
|
||||
bobPreKey, err := libsignalgo.GeneratePrivateKey()
|
||||
|
|
@ -86,7 +86,7 @@ func initializeSessions(t *testing.T, aliceStore, bobStore *InMemorySignalProtoc
|
|||
assert.NoError(t, err)
|
||||
|
||||
// Alice processes the bundle
|
||||
err = libsignalgo.ProcessPreKeyBundle(ctx, bobBundle, bobAddress, aliceAddress, aliceStore, aliceStore)
|
||||
err = libsignalgo.ProcessPreKeyBundle(ctx, bobBundle, bobAddress, aliceStore, aliceStore)
|
||||
assert.NoError(t, err)
|
||||
|
||||
record, err := aliceStore.LoadSession(ctx, bobAddress)
|
||||
|
|
@ -132,7 +132,7 @@ func TestSessionCipher(t *testing.T) {
|
|||
aliceStore := NewInMemorySignalProtocolStore()
|
||||
bobStore := NewInMemorySignalProtocolStore()
|
||||
|
||||
initializeSessions(t, aliceStore, bobStore, bobAddress, aliceAddress)
|
||||
initializeSessions(t, aliceStore, bobStore, bobAddress)
|
||||
|
||||
alicePlaintext := []byte{8, 6, 7, 5, 3, 0, 9}
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ func TestSessionCipher(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
aliceCiphertext2, err := libsignalgo.DeserializeMessage(bobCiphertext2Serialized)
|
||||
assert.NoError(t, err)
|
||||
alicePlaintext2, err := libsignalgo.Decrypt(ctx, aliceCiphertext2, bobAddress, aliceAddress, aliceStore, aliceStore)
|
||||
alicePlaintext2, err := libsignalgo.Decrypt(ctx, aliceCiphertext2, bobAddress, aliceStore, aliceStore)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, bobPlaintext2, alicePlaintext2)
|
||||
}
|
||||
|
|
@ -183,7 +183,7 @@ func TestSessionCipherWithBadStore(t *testing.T) {
|
|||
aliceStore := NewInMemorySignalProtocolStore()
|
||||
bobStore := &BadInMemorySignalProtocolStore{NewInMemorySignalProtocolStore()}
|
||||
|
||||
initializeSessions(t, aliceStore, bobStore.InMemorySignalProtocolStore, bobAddress, aliceAddress)
|
||||
initializeSessions(t, aliceStore, bobStore.InMemorySignalProtocolStore, bobAddress)
|
||||
|
||||
alicePlaintext := []byte{8, 6, 7, 5, 3, 0, 9}
|
||||
|
||||
|
|
@ -216,7 +216,7 @@ func TestSealedSenderEncrypt_Repeated(t *testing.T) {
|
|||
aliceStore := NewInMemorySignalProtocolStore()
|
||||
bobStore := NewInMemorySignalProtocolStore()
|
||||
|
||||
initializeSessions(t, aliceStore, bobStore, bobAddress, aliceAddress)
|
||||
initializeSessions(t, aliceStore, bobStore, bobAddress)
|
||||
|
||||
trustRoot, err := libsignalgo.GenerateIdentityKeyPair()
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -252,18 +252,15 @@ func TestArchiveSession(t *testing.T) {
|
|||
ctx := context.TODO()
|
||||
setupLogging()
|
||||
|
||||
aliceACI := uuid.New()
|
||||
bobACI := uuid.New()
|
||||
|
||||
aliceAddress, err := libsignalgo.NewACIServiceID(aliceACI).Address(1)
|
||||
assert.NoError(t, err)
|
||||
bobAddress, err := libsignalgo.NewACIServiceID(bobACI).Address(1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
aliceStore := NewInMemorySignalProtocolStore()
|
||||
bobStore := NewInMemorySignalProtocolStore()
|
||||
|
||||
initializeSessions(t, aliceStore, bobStore, bobAddress, aliceAddress)
|
||||
initializeSessions(t, aliceStore, bobStore, bobAddress)
|
||||
|
||||
session, err := aliceStore.LoadSession(ctx, bobAddress)
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -318,7 +315,7 @@ func TestSealedSenderGroupCipher(t *testing.T) {
|
|||
|
||||
bobStore := NewInMemorySignalProtocolStore()
|
||||
|
||||
initializeSessions(t, aliceStore, bobStore, bobAddress, aliceAddress)
|
||||
initializeSessions(t, aliceStore, bobStore, bobAddress)
|
||||
|
||||
trustRoot, err := libsignalgo.GenerateIdentityKeyPair()
|
||||
assert.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -67,8 +67,8 @@ func signal_destroy_session_store_callback(storeCtx unsafe.Pointer) {
|
|||
func (ctx *CallbackContext) wrapSessionStore(store SessionStore) C.SignalConstPointerFfiSessionStoreStruct {
|
||||
return C.SignalConstPointerFfiSessionStoreStruct{&C.SignalSessionStore{
|
||||
ctx: wrapStore(ctx, store),
|
||||
load_session: C.SignalFfiSessionStoreLoadSession(C.signal_load_session_callback),
|
||||
store_session: C.SignalFfiSessionStoreStoreSession(C.signal_store_session_callback),
|
||||
destroy: C.SignalFfiSessionStoreDestroy(C.signal_destroy_session_store_callback),
|
||||
load_session: C.SignalFfiBridgeSessionStoreLoadSession(C.signal_load_session_callback),
|
||||
store_session: C.SignalFfiBridgeSessionStoreStoreSession(C.signal_store_session_callback),
|
||||
destroy: C.SignalFfiBridgeSessionStoreDestroy(C.signal_destroy_session_store_callback),
|
||||
}}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,8 +67,8 @@ func signal_destroy_signed_pre_key_store_callback(storeCtx unsafe.Pointer) {
|
|||
func (ctx *CallbackContext) wrapSignedPreKeyStore(store SignedPreKeyStore) C.SignalConstPointerFfiSignedPreKeyStoreStruct {
|
||||
return C.SignalConstPointerFfiSignedPreKeyStoreStruct{&C.SignalSignedPreKeyStore{
|
||||
ctx: wrapStore(ctx, store),
|
||||
load_signed_pre_key: C.SignalFfiSignedPreKeyStoreLoadSignedPreKey(C.signal_load_signed_pre_key_callback),
|
||||
store_signed_pre_key: C.SignalFfiSignedPreKeyStoreStoreSignedPreKey(C.signal_store_signed_pre_key_callback),
|
||||
destroy: C.SignalFfiSignedPreKeyStoreDestroy(C.signal_destroy_signed_pre_key_store_callback),
|
||||
load_signed_pre_key: C.SignalFfiBridgeSignedPreKeyStoreLoadSignedPreKey(C.signal_load_signed_pre_key_callback),
|
||||
store_signed_pre_key: C.SignalFfiBridgeSignedPreKeyStoreStoreSignedPreKey(C.signal_store_signed_pre_key_callback),
|
||||
destroy: C.SignalFfiBridgeSignedPreKeyStoreDestroy(C.signal_destroy_signed_pre_key_store_callback),
|
||||
}}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
package libsignalgo
|
||||
|
||||
const Version = "v0.93.2"
|
||||
const Version = "v0.92.1"
|
||||
|
|
|
|||
|
|
@ -110,24 +110,21 @@ func (mc *MessageConverter) ToSignal(
|
|||
return nil, fmt.Errorf("failed to convert sticker: %w", err)
|
||||
}
|
||||
att.Flags = proto.Uint32(uint32(signalpb.AttachmentPointer_BORDERLESS))
|
||||
|
||||
dm.Sticker = ParseStickerMeta(content.Info.BridgedSticker)
|
||||
if dm.Sticker == nil {
|
||||
var emoji *string
|
||||
// TODO check for single grapheme cluster?
|
||||
if len([]rune(content.Body)) == 1 {
|
||||
emoji = proto.String(variationselector.Remove(content.Body))
|
||||
}
|
||||
dm.Sticker = &signalpb.DataMessage_Sticker{
|
||||
// Signal iOS validates that pack id/key are of the correct length.
|
||||
// Android is fine with any non-nil values (like a zero-length byte string).
|
||||
PackId: make([]byte, 16),
|
||||
PackKey: make([]byte, 32),
|
||||
StickerId: proto.Uint32(0),
|
||||
Emoji: emoji,
|
||||
}
|
||||
var emoji *string
|
||||
// TODO check for single grapheme cluster?
|
||||
if len([]rune(content.Body)) == 1 {
|
||||
emoji = proto.String(variationselector.Remove(content.Body))
|
||||
}
|
||||
dm.Sticker = &signalpb.DataMessage_Sticker{
|
||||
// Signal iOS validates that pack id/key are of the correct length.
|
||||
// Android is fine with any non-nil values (like a zero-length byte string).
|
||||
PackId: make([]byte, 16),
|
||||
PackKey: make([]byte, 32),
|
||||
StickerId: proto.Uint32(0),
|
||||
|
||||
Data: att,
|
||||
Emoji: emoji,
|
||||
}
|
||||
dm.Sticker.Data = att
|
||||
case event.MsgLocation:
|
||||
lat, lon, err := parseGeoURI(content.GeoURI)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -468,16 +468,20 @@ func (mc *MessageConverter) convertStickerToMatrix(ctx context.Context, sticker
|
|||
converted.Content.Info.Height = 200
|
||||
}
|
||||
converted.Content.Body = sticker.GetEmoji()
|
||||
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),
|
||||
Emoji: sticker.GetEmoji(),
|
||||
PackURL: fmt.Sprintf(PackURLFormat, sticker.GetPackId(), sticker.GetPackKey()),
|
||||
}
|
||||
}
|
||||
converted.Type = event.EventSticker
|
||||
converted.Content.MsgType = ""
|
||||
if converted.Extra == nil {
|
||||
converted.Extra = map[string]any{}
|
||||
}
|
||||
// TODO fetch full pack metadata like the old bridge did?
|
||||
converted.Extra["fi.mau.signal.sticker"] = map[string]any{
|
||||
"id": sticker.GetStickerId(),
|
||||
"emoji": sticker.GetEmoji(),
|
||||
"pack": map[string]any{
|
||||
"id": sticker.GetPackId(),
|
||||
"key": sticker.GetPackKey(),
|
||||
},
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,199 +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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package msgconv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.mau.fi/util/emojishortcodes"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/bridgev2"
|
||||
"maunium.net/go/mautrix/bridgev2/database"
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
|
||||
"go.mau.fi/mautrix-signal/pkg/signalid"
|
||||
"go.mau.fi/mautrix-signal/pkg/signalmeow"
|
||||
signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
|
||||
)
|
||||
|
||||
const StickerSourceID = "signal"
|
||||
const PackURLFormat = "https://signal.art/addstickers/#pack_id=%x&pack_key=%x"
|
||||
|
||||
const PackIDLength = 16
|
||||
const PackKeyLength = 32
|
||||
const PackURLLength = len(PackURLFormat) - len("%x")*2 + PackIDLength*2 + PackKeyLength*2
|
||||
|
||||
var zeroPackID = make([]byte, PackIDLength)
|
||||
|
||||
func ParseStickerMeta(info *event.BridgedSticker) *signalpb.DataMessage_Sticker {
|
||||
if info == nil || info.Network != StickerSourceID || len(info.PackURL) != PackURLLength {
|
||||
return nil
|
||||
}
|
||||
stickerID, err := strconv.ParseUint(info.ID, 10, 32)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
packID, packKey, err := parsePackURL(info.PackURL)
|
||||
if err != nil || len(packID) != PackIDLength || len(packKey) != PackKeyLength || bytes.Equal(packID, zeroPackID) {
|
||||
return nil
|
||||
}
|
||||
return &signalpb.DataMessage_Sticker{
|
||||
PackId: packID,
|
||||
PackKey: packKey,
|
||||
StickerId: proto.Uint32(uint32(stickerID)),
|
||||
Emoji: &info.Emoji,
|
||||
}
|
||||
}
|
||||
|
||||
func parsePackURL(rawURL string) (packID, packKey []byte, err error) {
|
||||
parsed, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid URL: %w", err)
|
||||
} else if parsed.Host != "signal.art" || !strings.HasPrefix(parsed.Path, "/addstickers") {
|
||||
return nil, nil, fmt.Errorf("invalid host or path in URL")
|
||||
}
|
||||
q, err := url.ParseQuery(parsed.Fragment)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid URL fragment: %w", err)
|
||||
}
|
||||
packID, err = hex.DecodeString(q.Get("pack_id"))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid pack ID in URL: %w", err)
|
||||
}
|
||||
packKey, err = hex.DecodeString(q.Get("pack_key"))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid pack key in URL: %w", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (mc *MessageConverter) DownloadImagePack(ctx context.Context, url string) (*bridgev2.ImportedImagePack, error) {
|
||||
packID, packKey, err := parsePackURL(url)
|
||||
if err != nil {
|
||||
return nil, bridgev2.WrapRespErr(err, mautrix.MNotFound)
|
||||
}
|
||||
manifest, err := signalmeow.DownloadStickerPackManifest(ctx, packID, packKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to download sticker pack manifest: %w", err)
|
||||
}
|
||||
topLevelExtra := map[string]any{
|
||||
"fi.mau.signal.stickerpack": map[string]any{
|
||||
"pack_id": hex.EncodeToString(packID),
|
||||
"pack_key": hex.EncodeToString(packKey),
|
||||
},
|
||||
}
|
||||
content := &event.ImagePackEventContent{
|
||||
Images: make(map[string]*event.ImagePackImage, len(manifest.Stickers)),
|
||||
Metadata: event.ImagePackMetadata{
|
||||
DisplayName: manifest.GetTitle(),
|
||||
AvatarURL: "",
|
||||
Usage: []event.ImagePackUsage{event.ImagePackUsageSticker},
|
||||
Attribution: manifest.GetAuthor(),
|
||||
BridgedPack: &event.BridgedStickerPack{
|
||||
Network: StickerSourceID,
|
||||
URL: fmt.Sprintf(PackURLFormat, packID, packKey),
|
||||
},
|
||||
},
|
||||
}
|
||||
imagesByID := make(map[uint32]id.ContentURIString, len(manifest.Stickers))
|
||||
uploadImage := func(sticker *signalpb.Pack_Sticker) (id.ContentURIString, error) {
|
||||
stickerID := sticker.GetId()
|
||||
existing, ok := imagesByID[stickerID]
|
||||
if ok {
|
||||
return existing, nil
|
||||
}
|
||||
var mxc id.ContentURIString
|
||||
if mc.DirectMedia {
|
||||
mediaID, err := signalid.DirectMediaSticker{
|
||||
PackID: packID,
|
||||
PackKey: packKey,
|
||||
StickerID: stickerID,
|
||||
}.AsMediaID()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create media ID for sticker %d: %w", stickerID, err)
|
||||
}
|
||||
mxc, err = mc.Bridge.Matrix.GenerateContentURI(ctx, mediaID)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate content URI for sticker %d: %w", stickerID, err)
|
||||
}
|
||||
} else {
|
||||
dbKey := database.Key(fmt.Sprintf("stickercache:%x:%d", packID, stickerID))
|
||||
if cached := mc.Bridge.DB.KV.Get(ctx, dbKey); cached != "" {
|
||||
mxc = id.ContentURIString(cached)
|
||||
imagesByID[stickerID] = mxc
|
||||
return mxc, nil
|
||||
}
|
||||
data, err := signalmeow.DownloadStickerPackItem(ctx, packID, packKey, stickerID)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to download sticker %d: %w", stickerID, err)
|
||||
}
|
||||
mxc, _, err = mc.Bridge.Bot.UploadMedia(ctx, "", data, "", sticker.GetContentType())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to upload sticker %d: %w", stickerID, err)
|
||||
}
|
||||
mc.Bridge.DB.KV.Set(ctx, dbKey, string(mxc))
|
||||
}
|
||||
imagesByID[stickerID] = mxc
|
||||
return mxc, nil
|
||||
}
|
||||
for _, sticker := range manifest.Stickers {
|
||||
mxc, err := uploadImage(sticker)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
shortcode := emojishortcodes.Get(sticker.GetEmoji())
|
||||
realShortcode := shortcode
|
||||
i := 2
|
||||
for _, alreadyExists := content.Images[realShortcode]; alreadyExists; i++ {
|
||||
realShortcode = fmt.Sprintf("%s_%d", shortcode, i)
|
||||
}
|
||||
content.Images[realShortcode] = &event.ImagePackImage{
|
||||
URL: mxc,
|
||||
Body: sticker.GetEmoji(),
|
||||
Info: &event.FileInfo{
|
||||
MimeType: sticker.GetContentType(),
|
||||
Width: 200,
|
||||
Height: 200,
|
||||
BridgedSticker: &event.BridgedSticker{
|
||||
Network: StickerSourceID,
|
||||
ID: strconv.FormatUint(uint64(sticker.GetId()), 10),
|
||||
Emoji: sticker.GetEmoji(),
|
||||
PackURL: content.Metadata.BridgedPack.URL,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
if manifest.Cover != nil {
|
||||
content.Metadata.AvatarURL, err = uploadImage(manifest.Cover)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to upload sticker pack cover: %w", err)
|
||||
}
|
||||
}
|
||||
return &bridgev2.ImportedImagePack{
|
||||
Content: content,
|
||||
Extra: topLevelExtra,
|
||||
Shortcode: hex.EncodeToString(packID),
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -34,7 +34,6 @@ const (
|
|||
directMediaTypeGroupAvatar directMediaType = 1
|
||||
directMediaTypeProfileAvatar directMediaType = 2
|
||||
directMediaTypePlaintextDigestAttachment directMediaType = 3
|
||||
directMediaTypeSticker directMediaType = 4
|
||||
)
|
||||
|
||||
type DirectMediaInfo interface {
|
||||
|
|
@ -45,7 +44,6 @@ var (
|
|||
_ DirectMediaInfo = (*DirectMediaAttachment)(nil)
|
||||
_ DirectMediaInfo = (*DirectMediaGroupAvatar)(nil)
|
||||
_ DirectMediaInfo = (*DirectMediaProfileAvatar)(nil)
|
||||
_ DirectMediaInfo = (*DirectMediaSticker)(nil)
|
||||
)
|
||||
|
||||
type DirectMediaAttachment struct {
|
||||
|
|
@ -129,30 +127,6 @@ func (m DirectMediaProfileAvatar) AsMediaID() (mediaID networkid.MediaID, err er
|
|||
return networkid.MediaID(buf.Bytes()), nil
|
||||
}
|
||||
|
||||
type DirectMediaSticker struct {
|
||||
PackID []byte
|
||||
PackKey []byte
|
||||
StickerID uint32
|
||||
}
|
||||
|
||||
const packIDLen = 16
|
||||
const packKeyLen = 32
|
||||
const directMediaStickerLen = 1 + packIDLen + packKeyLen + 4
|
||||
|
||||
func (m DirectMediaSticker) AsMediaID() (mediaID networkid.MediaID, err error) {
|
||||
if len(m.PackID) != packIDLen {
|
||||
return nil, fmt.Errorf("invalid pack ID length: %d", len(m.PackID))
|
||||
} else if len(m.PackKey) != packKeyLen {
|
||||
return nil, fmt.Errorf("invalid pack key length: %d", len(m.PackKey))
|
||||
}
|
||||
mediaID = make(networkid.MediaID, directMediaStickerLen)
|
||||
mediaID[0] = byte(directMediaTypeSticker)
|
||||
copy(mediaID[1:], m.PackID)
|
||||
copy(mediaID[1+packIDLen:], m.PackKey)
|
||||
binary.BigEndian.PutUint32(mediaID[1+packIDLen+packKeyLen:], m.StickerID)
|
||||
return mediaID, nil
|
||||
}
|
||||
|
||||
func ParseDirectMediaInfo(mediaID networkid.MediaID) (_ DirectMediaInfo, err error) {
|
||||
mediaIDLen := len(mediaID)
|
||||
if mediaIDLen == 0 {
|
||||
|
|
@ -226,15 +200,6 @@ func ParseDirectMediaInfo(mediaID networkid.MediaID) (_ DirectMediaInfo, err err
|
|||
info.ProfileAvatarPath = string(profileAvatarPath)
|
||||
}
|
||||
return &info, nil
|
||||
case directMediaTypeSticker:
|
||||
var info DirectMediaSticker
|
||||
if len(mediaID) != directMediaStickerLen {
|
||||
return info, fmt.Errorf("invalid media ID length for sticker: %d", len(mediaID))
|
||||
}
|
||||
info.PackID = mediaID[1 : 1+packIDLen]
|
||||
info.PackKey = mediaID[1+packIDLen : 1+packIDLen+packKeyLen]
|
||||
info.StickerID = binary.BigEndian.Uint32(mediaID[1+packIDLen+packKeyLen:])
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid direct media type %d", mediaType)
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import (
|
|||
|
||||
"github.com/rs/zerolog"
|
||||
"go.mau.fi/util/fallocate"
|
||||
"go.mau.fi/util/pkcs7"
|
||||
"go.mau.fi/util/random"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
|
|
@ -137,15 +136,6 @@ func DownloadAttachment(
|
|||
const MACLength = 32
|
||||
const IVLength = 16
|
||||
|
||||
func macAndAESDecrypt(body, key []byte) ([]byte, error) {
|
||||
l := len(body) - MACLength
|
||||
if !verifyMAC(key[MACLength:], body[:l], body[l:]) {
|
||||
return nil, ErrInvalidMACForAttachment
|
||||
}
|
||||
|
||||
return aesDecrypt(key[:MACLength], body[:l])
|
||||
}
|
||||
|
||||
func decryptAttachment(body, key, digest []byte, plaintextDigest bool, size uint32) ([]byte, error) {
|
||||
if !plaintextDigest {
|
||||
hash := sha256.Sum256(body)
|
||||
|
|
@ -153,7 +143,12 @@ func decryptAttachment(body, key, digest []byte, plaintextDigest bool, size uint
|
|||
return nil, ErrInvalidDigestForAttachment
|
||||
}
|
||||
}
|
||||
decrypted, err := macAndAESDecrypt(body, key)
|
||||
l := len(body) - MACLength
|
||||
if !verifyMAC(key[MACLength:], body[:l], body[l:]) {
|
||||
return nil, ErrInvalidMACForAttachment
|
||||
}
|
||||
|
||||
decrypted, err := aesDecrypt(key[:MACLength], body[:l])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -245,14 +240,6 @@ func extend(data []byte, paddedLen int) []byte {
|
|||
}
|
||||
}
|
||||
|
||||
func macAndAESEncrypt(keys, plaintext []byte) ([]byte, error) {
|
||||
encrypted, err := aesEncrypt(keys[:32], plaintext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return appendMAC(keys[32:], encrypted), nil
|
||||
}
|
||||
|
||||
func (cli *Client) UploadAttachment(ctx context.Context, body []byte) (*signalpb.AttachmentPointer, error) {
|
||||
log := zerolog.Ctx(ctx).With().Str("func", "upload attachment").Logger()
|
||||
keys := random.Bytes(64) // combined AES and MAC keys
|
||||
|
|
@ -268,10 +255,11 @@ func (cli *Client) UploadAttachment(ctx context.Context, body []byte) (*signalpb
|
|||
}
|
||||
body = extend(body, paddedLen)
|
||||
|
||||
encryptedWithMAC, err := macAndAESEncrypt(keys, body)
|
||||
encrypted, err := aesEncrypt(keys[:32], body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encryptedWithMAC := appendMAC(keys[32:], encrypted)
|
||||
|
||||
// Get upload attributes from Signal server
|
||||
attributesPath := "/v4/attachments/form/upload"
|
||||
|
|
@ -479,10 +467,13 @@ func aesDecrypt(key, ciphertext []byte) ([]byte, error) {
|
|||
}
|
||||
|
||||
iv := ciphertext[:IVLength]
|
||||
ciphertext = ciphertext[IVLength:]
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(ciphertext, ciphertext)
|
||||
return pkcs7.Unpad(ciphertext)
|
||||
pad := ciphertext[len(ciphertext)-1]
|
||||
if pad > aes.BlockSize {
|
||||
return nil, fmt.Errorf("pad value (%d) larger than AES blocksize (%d)", pad, aes.BlockSize)
|
||||
}
|
||||
return ciphertext[aes.BlockSize : len(ciphertext)-int(pad)], nil
|
||||
}
|
||||
|
||||
func aesDecryptFile(key []byte, file *os.File, downloadedSize int64) (int64, error) {
|
||||
|
|
@ -542,11 +533,14 @@ func aesEncrypt(key, plaintext []byte) ([]byte, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
plaintext = pkcs7.Pad(plaintext, aes.BlockSize)
|
||||
pad := aes.BlockSize - len(plaintext)%aes.BlockSize
|
||||
plaintext = append(plaintext, bytes.Repeat([]byte{byte(pad)}, pad)...)
|
||||
|
||||
ciphertext := make([]byte, len(plaintext))
|
||||
iv := random.Bytes(16)
|
||||
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(plaintext, plaintext)
|
||||
mode.CryptBlocks(ciphertext, plaintext)
|
||||
|
||||
return append(iv, plaintext...), nil
|
||||
return append(iv, ciphertext...), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -413,10 +413,6 @@ func (cli *Client) FetchAndProcessPreKey(ctx context.Context, theirServiceID lib
|
|||
if cli.Store.RecipientStore.IsUnregistered(ctx, theirServiceID) {
|
||||
return fmt.Errorf("%w (cached)", ErrUnregisteredUser)
|
||||
}
|
||||
localAddress, err := cli.Store.ACIServiceID().Address(uint(cli.Store.DeviceID))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get own address: %w", err)
|
||||
}
|
||||
// Fetch prekey
|
||||
deviceIDPath := "/*"
|
||||
if specificDeviceID >= 0 {
|
||||
|
|
@ -522,7 +518,6 @@ func (cli *Client) FetchAndProcessPreKey(ctx context.Context, theirServiceID lib
|
|||
ctx,
|
||||
preKeyBundle,
|
||||
address,
|
||||
localAddress,
|
||||
cli.Store.ACISessionStore,
|
||||
cli.Store.ACIIdentityStore,
|
||||
)
|
||||
|
|
|
|||
2
pkg/signalmeow/protobuf/ContactDiscovery.pb.go
generated
2
pkg/signalmeow/protobuf/ContactDiscovery.pb.go
generated
|
|
@ -1,7 +1,7 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v7.34.1
|
||||
// protoc v6.33.5
|
||||
// source: ContactDiscovery.proto
|
||||
|
||||
// Copyright 2021 Signal Messenger, LLC
|
||||
|
|
|
|||
2
pkg/signalmeow/protobuf/DeviceName.pb.go
generated
2
pkg/signalmeow/protobuf/DeviceName.pb.go
generated
|
|
@ -1,7 +1,7 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v7.34.1
|
||||
// protoc v6.33.5
|
||||
// source: DeviceName.proto
|
||||
|
||||
// Copyright 2018 Signal Messenger, LLC
|
||||
|
|
|
|||
2
pkg/signalmeow/protobuf/Groups.pb.go
generated
2
pkg/signalmeow/protobuf/Groups.pb.go
generated
|
|
@ -5,7 +5,7 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v7.34.1
|
||||
// protoc v6.33.5
|
||||
// source: Groups.proto
|
||||
|
||||
package signalpb
|
||||
|
|
|
|||
2
pkg/signalmeow/protobuf/Provisioning.pb.go
generated
2
pkg/signalmeow/protobuf/Provisioning.pb.go
generated
|
|
@ -5,7 +5,7 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v7.34.1
|
||||
// protoc v6.33.5
|
||||
// source: Provisioning.proto
|
||||
|
||||
package signalpb
|
||||
|
|
|
|||
2
pkg/signalmeow/protobuf/SignalService.pb.go
generated
2
pkg/signalmeow/protobuf/SignalService.pb.go
generated
|
|
@ -5,7 +5,7 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v7.34.1
|
||||
// protoc v6.33.5
|
||||
// source: SignalService.proto
|
||||
|
||||
package signalpb
|
||||
|
|
|
|||
2
pkg/signalmeow/protobuf/StickerResources.pb.go
generated
2
pkg/signalmeow/protobuf/StickerResources.pb.go
generated
|
|
@ -6,7 +6,7 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v7.34.1
|
||||
// protoc v6.33.5
|
||||
// source: StickerResources.proto
|
||||
|
||||
package signalpb
|
||||
|
|
|
|||
15
pkg/signalmeow/protobuf/StorageService.pb.go
generated
15
pkg/signalmeow/protobuf/StorageService.pb.go
generated
|
|
@ -6,7 +6,7 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v7.34.1
|
||||
// protoc v6.33.5
|
||||
// source: StorageService.proto
|
||||
|
||||
package signalpb
|
||||
|
|
@ -1401,7 +1401,6 @@ type GroupV2Record struct {
|
|||
HideStory bool `protobuf:"varint,8,opt,name=hideStory,proto3" json:"hideStory,omitempty"`
|
||||
StorySendMode GroupV2Record_StorySendMode `protobuf:"varint,10,opt,name=storySendMode,proto3,enum=signalservice.GroupV2Record_StorySendMode" json:"storySendMode,omitempty"`
|
||||
AvatarColor *AvatarColor `protobuf:"varint,11,opt,name=avatarColor,proto3,enum=signalservice.AvatarColor,oneof" json:"avatarColor,omitempty"`
|
||||
VerifiedNameHash []byte `protobuf:"bytes,12,opt,name=verifiedNameHash,proto3" json:"verifiedNameHash,omitempty"` // SHA-256 of UTF-8 encoded decrypted group title that was last verified
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
|
@ -1506,13 +1505,6 @@ func (x *GroupV2Record) GetAvatarColor() AvatarColor {
|
|||
return AvatarColor_A100
|
||||
}
|
||||
|
||||
func (x *GroupV2Record) GetVerifiedNameHash() []byte {
|
||||
if x != nil {
|
||||
return x.VerifiedNameHash
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Payments struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
|
||||
|
|
@ -3203,7 +3195,7 @@ const file_StorageService_proto_rawDesc = "" +
|
|||
"\vwhitelisted\x18\x03 \x01(\bR\vwhitelisted\x12\x1a\n" +
|
||||
"\barchived\x18\x04 \x01(\bR\barchived\x12\"\n" +
|
||||
"\fmarkedUnread\x18\x05 \x01(\bR\fmarkedUnread\x120\n" +
|
||||
"\x13mutedUntilTimestamp\x18\x06 \x01(\x04R\x13mutedUntilTimestamp\"\xcd\x04\n" +
|
||||
"\x13mutedUntilTimestamp\x18\x06 \x01(\x04R\x13mutedUntilTimestamp\"\xa1\x04\n" +
|
||||
"\rGroupV2Record\x12\x1c\n" +
|
||||
"\tmasterKey\x18\x01 \x01(\fR\tmasterKey\x12\x18\n" +
|
||||
"\ablocked\x18\x02 \x01(\bR\ablocked\x12 \n" +
|
||||
|
|
@ -3215,8 +3207,7 @@ const file_StorageService_proto_rawDesc = "" +
|
|||
"\thideStory\x18\b \x01(\bR\thideStory\x12P\n" +
|
||||
"\rstorySendMode\x18\n" +
|
||||
" \x01(\x0e2*.signalservice.GroupV2Record.StorySendModeR\rstorySendMode\x12A\n" +
|
||||
"\vavatarColor\x18\v \x01(\x0e2\x1a.signalservice.AvatarColorH\x00R\vavatarColor\x88\x01\x01\x12*\n" +
|
||||
"\x10verifiedNameHash\x18\f \x01(\fR\x10verifiedNameHash\"7\n" +
|
||||
"\vavatarColor\x18\v \x01(\x0e2\x1a.signalservice.AvatarColorH\x00R\vavatarColor\x88\x01\x01\"7\n" +
|
||||
"\rStorySendMode\x12\v\n" +
|
||||
"\aDEFAULT\x10\x00\x12\f\n" +
|
||||
"\bDISABLED\x10\x01\x12\v\n" +
|
||||
|
|
|
|||
|
|
@ -172,7 +172,6 @@ message GroupV2Record {
|
|||
reserved /* storySendEnabled */ 9;
|
||||
StorySendMode storySendMode = 10;
|
||||
optional AvatarColor avatarColor = 11;
|
||||
bytes verifiedNameHash = 12; // SHA-256 of UTF-8 encoded decrypted group title that was last verified
|
||||
}
|
||||
|
||||
message Payments {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v7.34.1
|
||||
// protoc v6.33.5
|
||||
// source: UnidentifiedDelivery.proto
|
||||
|
||||
// Copyright 2018 Signal Messenger, LLC
|
||||
|
|
|
|||
2
pkg/signalmeow/protobuf/WebSocketResources.pb.go
generated
2
pkg/signalmeow/protobuf/WebSocketResources.pb.go
generated
|
|
@ -6,7 +6,7 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v7.34.1
|
||||
// protoc v6.33.5
|
||||
// source: WebSocketResources.proto
|
||||
|
||||
package signalpb
|
||||
|
|
|
|||
2
pkg/signalmeow/protobuf/backuppb/Backup.pb.go
generated
2
pkg/signalmeow/protobuf/backuppb/Backup.pb.go
generated
|
|
@ -1,7 +1,7 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v7.34.1
|
||||
// protoc v6.33.5
|
||||
// source: backuppb/Backup.proto
|
||||
|
||||
package backuppb
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
ANDROID_GIT_REVISION=${1:-439760e7732585bfd078d92d93732c04cc31e29e}
|
||||
DESKTOP_GIT_REVISION=${1:-1b2a3e7b283c32c5654a39da12fc04139fd26dbd}
|
||||
ANDROID_GIT_REVISION=${1:-dfd2f7baf96825834f784900ce644e9ead8a9a89}
|
||||
DESKTOP_GIT_REVISION=${1:-60a1e125452ee672d8747564d0055d5bfec9f679}
|
||||
|
||||
update_proto() {
|
||||
case "$1" in
|
||||
|
|
|
|||
|
|
@ -243,16 +243,11 @@ func (cli *Client) decryptCiphertextEnvelope(
|
|||
if identityStore == nil {
|
||||
return nil, fmt.Errorf("no identity store for destination service ID %s", destinationServiceID)
|
||||
}
|
||||
destinationAddress, err := destinationServiceID.Address(uint(cli.Store.DeviceID))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get own address: %w", err)
|
||||
}
|
||||
plaintext, ciphertextHash, err := cli.bufferedDecryptTxn(ctx, ciphertext, serverTimestamp, func(ctx context.Context) ([]byte, error) {
|
||||
return libsignalgo.Decrypt(
|
||||
ctx,
|
||||
message,
|
||||
senderAddress,
|
||||
destinationAddress,
|
||||
sessionStore,
|
||||
identityStore,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -384,7 +384,15 @@ func syncMessageFromSoloEditMessage(editMessage *signalpb.EditMessage, result Su
|
|||
}
|
||||
|
||||
func syncMessageFromReadReceiptMessage(ctx context.Context, receiptMessage *signalpb.ReceiptMessage, messageSender libsignalgo.ServiceID) *signalpb.Content {
|
||||
if *receiptMessage.Type != signalpb.ReceiptMessage_READ || messageSender.Type != libsignalgo.ServiceIDTypeACI {
|
||||
if *receiptMessage.Type != signalpb.ReceiptMessage_READ {
|
||||
zerolog.Ctx(ctx).Warn().
|
||||
Any("receipt_message_type", receiptMessage.Type).
|
||||
Msg("syncMessageFromReadReceiptMessage called with non-read receipt message")
|
||||
return nil
|
||||
} else if messageSender.Type != libsignalgo.ServiceIDTypeACI {
|
||||
zerolog.Ctx(ctx).Warn().
|
||||
Stringer("message_sender", messageSender).
|
||||
Msg("syncMessageFromReadReceiptMessage called with non-ACI message sender")
|
||||
return nil
|
||||
}
|
||||
read := []*signalpb.SyncMessage_Read{}
|
||||
|
|
|
|||
|
|
@ -1,251 +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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package signalmeow
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/hkdf"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"sync"
|
||||
|
||||
"go.mau.fi/util/exerrors"
|
||||
"go.mau.fi/util/random"
|
||||
"golang.org/x/sync/semaphore"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
|
||||
"go.mau.fi/mautrix-signal/pkg/signalmeow/web"
|
||||
)
|
||||
|
||||
func DownloadStickerPackManifest(ctx context.Context, packID, packKey []byte) (*signalpb.Pack, error) {
|
||||
if len(packID) != 16 {
|
||||
return nil, fmt.Errorf("invalid pack ID length: %d", len(packID))
|
||||
}
|
||||
resp, err := downloadStickerData(ctx, fmt.Sprintf("/stickers/%x/manifest.proto", packID), packKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pack signalpb.Pack
|
||||
err = proto.Unmarshal(resp, &pack)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal decrypted manifest: %w", err)
|
||||
}
|
||||
return &pack, nil
|
||||
}
|
||||
|
||||
func DownloadStickerPackItem(ctx context.Context, packID, packKey []byte, stickerID uint32) ([]byte, error) {
|
||||
if len(packID) != 16 {
|
||||
return nil, fmt.Errorf("invalid pack ID length: %d", len(packID))
|
||||
}
|
||||
return downloadStickerData(ctx, fmt.Sprintf("/stickers/%x/full/%d", packID, stickerID), packKey)
|
||||
}
|
||||
|
||||
func downloadStickerData(ctx context.Context, path string, packKey []byte) ([]byte, error) {
|
||||
if len(packKey) != 32 {
|
||||
return nil, fmt.Errorf("invalid pack key length: %d", len(packKey))
|
||||
}
|
||||
var body, decrypted []byte
|
||||
resp, err := web.SendHTTPRequest(ctx, web.CDN1Hostname, http.MethodGet, path, nil)
|
||||
defer web.CloseBody(resp)
|
||||
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 %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 {
|
||||
return nil, fmt.Errorf("failed to decrypt response: %w", err)
|
||||
} else {
|
||||
return decrypted, nil
|
||||
}
|
||||
}
|
||||
|
||||
type stickerUploadAttributes struct {
|
||||
ACL string `json:"acl"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Credential string `json:"credential"`
|
||||
Date string `json:"date"`
|
||||
ID int `json:"id"`
|
||||
Key string `json:"key"`
|
||||
Policy string `json:"policy"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
func (sua *stickerUploadAttributes) makeFormBody(encryptedData []byte) (*web.HTTPReqOpt, error) {
|
||||
var buf bytes.Buffer
|
||||
writer := multipart.NewWriter(&buf)
|
||||
var closed bool
|
||||
// This isn't necessary in practice, just do it to avoid linter warnings
|
||||
defer func() {
|
||||
if !closed {
|
||||
_ = writer.Close()
|
||||
}
|
||||
}()
|
||||
fields := map[string]string{
|
||||
"key": sua.Key,
|
||||
"acl": sua.ACL,
|
||||
"policy": sua.Policy,
|
||||
"x-amz-algorithm": sua.Algorithm,
|
||||
"x-amz-credential": sua.Credential,
|
||||
"x-amz-date": sua.Date,
|
||||
"Content-Type": "application/octet-stream",
|
||||
}
|
||||
for key, value := range fields {
|
||||
err := writer.WriteField(key, value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to write multipart field %s: %w", key, err)
|
||||
}
|
||||
}
|
||||
filePart, err := writer.CreatePart(textproto.MIMEHeader{
|
||||
"Content-Type": []string{"application/octet-stream"},
|
||||
"Content-Disposition": []string{`form-data; name="file"`},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create multipart file part: %w", err)
|
||||
}
|
||||
_, err = filePart.Write(encryptedData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to write file data to multipart body: %w", err)
|
||||
}
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to close multipart writer: %w", err)
|
||||
}
|
||||
closed = true
|
||||
return &web.HTTPReqOpt{
|
||||
Body: buf.Bytes(),
|
||||
ContentType: web.ContentType(writer.FormDataContentType()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (sua *stickerUploadAttributes) upload(ctx context.Context, packKey, fileData []byte) error {
|
||||
encryptedData, err := macAndAESEncrypt(fileData, deriveStickerPackKey(packKey))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encrypt sticker data: %w", err)
|
||||
}
|
||||
req, err := sua.makeFormBody(encryptedData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to prepare request: %w", err)
|
||||
}
|
||||
resp, err := web.SendHTTPRequest(ctx, web.CDN1Hostname, http.MethodPost, "/", req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = resp.Body.Close()
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("unexpected status code %d", resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sua *stickerUploadAttributes) uploadAsync(
|
||||
ctx context.Context,
|
||||
packKey []byte,
|
||||
getFileData func(context.Context) ([]byte, error),
|
||||
sema *semaphore.Weighted,
|
||||
done func(),
|
||||
onError func(error),
|
||||
) {
|
||||
defer done()
|
||||
err := sema.Acquire(ctx, 1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer sema.Release(1)
|
||||
fileData, err := getFileData(ctx)
|
||||
if err == nil {
|
||||
err = sua.upload(ctx, packKey, fileData)
|
||||
}
|
||||
if err != nil {
|
||||
onError(err)
|
||||
}
|
||||
}
|
||||
|
||||
type stickerPackUploadAttributes struct {
|
||||
PackID string `json:"packId"`
|
||||
Manifest *stickerUploadAttributes `json:"manifest"`
|
||||
Stickers []*stickerUploadAttributes `json:"stickers"`
|
||||
}
|
||||
|
||||
var StickerUploadParallelism = 4
|
||||
|
||||
func (cli *Client) UploadStickerPack(ctx context.Context, pack *signalpb.Pack, stickerData []func(context.Context) ([]byte, error)) (packID, packKey []byte, err error) {
|
||||
for i, sticker := range pack.Stickers {
|
||||
if sticker.GetId() >= uint32(len(stickerData)) {
|
||||
return nil, nil, fmt.Errorf("sticker ID %d at index %d is out of bounds, only %d sticker blobs provided", sticker.GetId(), i, len(stickerData))
|
||||
}
|
||||
}
|
||||
marshaledPack, err := proto.Marshal(pack)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to marshal pack: %w", err)
|
||||
}
|
||||
packKey = random.Bytes(32)
|
||||
resp, err := cli.AuthedWS.SendRequest(ctx, http.MethodGet, fmt.Sprintf("/v1/sticker/pack/form/%d", len(stickerData)), nil, nil)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to get upload form: %w", err)
|
||||
}
|
||||
var packAttributes stickerPackUploadAttributes
|
||||
err = web.DecodeWSResponseBody(ctx, &packAttributes, resp)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to decode pack attributes: %w", err)
|
||||
}
|
||||
if len(packAttributes.Stickers) != len(stickerData) {
|
||||
return nil, nil, fmt.Errorf("expected %d sticker upload attribute sets, got %d", len(stickerData), len(packAttributes.Stickers))
|
||||
}
|
||||
packID, err = hex.DecodeString(packAttributes.PackID)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid pack ID in response: %w", err)
|
||||
}
|
||||
err = packAttributes.Manifest.upload(ctx, packKey, marshaledPack)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to upload manifest: %w", err)
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(packAttributes.Stickers))
|
||||
sema := semaphore.NewWeighted(int64(StickerUploadParallelism))
|
||||
var errorList []error
|
||||
var errorLock sync.Mutex
|
||||
for i, attrs := range packAttributes.Stickers {
|
||||
go attrs.uploadAsync(ctx, packKey, stickerData[i], sema, wg.Done, func(err error) {
|
||||
errorLock.Lock()
|
||||
errorList = append(errorList, fmt.Errorf("failed to upload sticker #%d: %w", i+1, err))
|
||||
errorLock.Unlock()
|
||||
})
|
||||
}
|
||||
wg.Wait()
|
||||
err = ctx.Err()
|
||||
if err == nil {
|
||||
err = errors.Join(errorList...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func decryptSticker(packKey, ciphertext []byte) ([]byte, error) {
|
||||
return macAndAESDecrypt(ciphertext, deriveStickerPackKey(packKey))
|
||||
}
|
||||
|
||||
func deriveStickerPackKey(key []byte) []byte {
|
||||
return exerrors.Must(hkdf.Key(sha256.New, key, make([]byte, 32), "Sticker Pack", 2*32))
|
||||
}
|
||||
|
|
@ -28,7 +28,6 @@ import (
|
|||
"net/http"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
|
|
@ -130,7 +129,6 @@ func SendHTTPRequest(ctx context.Context, host, method, path string, opt *HTTPRe
|
|||
} else {
|
||||
req.Header.Set("Content-Type", string(ContentTypeJSON))
|
||||
}
|
||||
req.ContentLength = int64(len(opt.Body))
|
||||
req.Header.Set("Content-Length", fmt.Sprintf("%d", len(opt.Body)))
|
||||
req.Header.Set("User-Agent", UserAgent)
|
||||
req.Header.Set("X-Signal-Agent", SignalAgent)
|
||||
|
|
@ -141,14 +139,12 @@ func SendHTTPRequest(ctx context.Context, host, method, path string, opt *HTTPRe
|
|||
httpReqCounter++
|
||||
log = log.With().Int("request_number", httpReqCounter).Logger()
|
||||
log.Trace().Msg("Sending HTTP request")
|
||||
start := time.Now()
|
||||
resp, err := SignalHTTPClient.Do(req)
|
||||
dur := time.Since(start)
|
||||
if err != nil {
|
||||
log.Err(err).Dur("duration", dur).Msg("Error sending request")
|
||||
log.Err(err).Msg("Error sending request")
|
||||
return nil, err
|
||||
}
|
||||
log.Debug().Int("status_code", resp.StatusCode).Dur("duration", dur).Msg("Received HTTP response")
|
||||
log.Debug().Int("status_code", resp.StatusCode).Msg("received HTTP response")
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue