Skip to content

Commit

Permalink
Prevent downgrading ghost user info by default (#464)
Browse files Browse the repository at this point in the history
Fixes #396
Closes #449
  • Loading branch information
tulir committed Feb 28, 2024
1 parent 2717c53 commit a78a88c
Show file tree
Hide file tree
Showing 17 changed files with 185 additions and 129 deletions.
5 changes: 3 additions & 2 deletions config/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type BridgeConfig struct {
DisplaynameTemplate string `yaml:"displayname_template"`
PrivateChatPortalMeta string `yaml:"private_chat_portal_meta"`
UseContactAvatars bool `yaml:"use_contact_avatars"`
UseOutdatedProfiles bool `yaml:"use_outdated_profiles"`
NumberInTopic bool `yaml:"number_in_topic"`

NoteToSelfAvatar id.ContentURIString `yaml:"note_to_self_avatar"`
Expand Down Expand Up @@ -169,12 +170,12 @@ type DisplaynameParams struct {
func (bc BridgeConfig) FormatDisplayname(contact *types.Contact) string {
var buffer strings.Builder
_ = bc.displaynameTemplate.Execute(&buffer, DisplaynameParams{
ProfileName: contact.ProfileName,
ProfileName: contact.Profile.Name,
ContactName: contact.ContactName,
//Username: contact.Username,
PhoneNumber: contact.E164,
UUID: contact.UUID.String(),
AboutEmoji: contact.ProfileAboutEmoji,
AboutEmoji: contact.Profile.AboutEmoji,
})
return buffer.String()
}
Expand Down
1 change: 1 addition & 0 deletions config/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func DoUpgrade(helper *up.Helper) {
}
helper.Copy(up.Str, "bridge", "private_chat_portal_meta")
helper.Copy(up.Bool, "bridge", "use_contact_avatars")
helper.Copy(up.Bool, "bridge", "use_outdated_profiles")
helper.Copy(up.Bool, "bridge", "number_in_topic")
helper.Copy(up.Str, "bridge", "note_to_self_avatar")
helper.Copy(up.Int, "bridge", "portal_message_buffer")
Expand Down
28 changes: 18 additions & 10 deletions database/puppet.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package database
import (
"context"
"database/sql"
"time"

"github.com/google/uuid"
"go.mau.fi/util/dbutil"
Expand All @@ -28,7 +29,7 @@ import (
const (
puppetBaseSelect = `
SELECT uuid, number, name, name_quality, avatar_path, avatar_hash, avatar_url, name_set, avatar_set,
contact_info_set, is_registered, custom_mxid, access_token
contact_info_set, is_registered, profile_fetched_at, custom_mxid, access_token
FROM puppet
`
getPuppetBySignalIDQuery = puppetBaseSelect + `WHERE uuid=$1`
Expand All @@ -38,18 +39,18 @@ const (
updatePuppetQuery = `
UPDATE puppet SET
number=$2, name=$3, name_quality=$4, avatar_path=$5, avatar_hash=$6, avatar_url=$7,
name_set=$8, avatar_set=$9, contact_info_set=$10, is_registered=$11,
custom_mxid=$12, access_token=$13
name_set=$8, avatar_set=$9, contact_info_set=$10, is_registered=$11, profile_fetched_at=$12,
custom_mxid=$13, access_token=$14
WHERE uuid=$1
`
insertPuppetQuery = `
INSERT INTO puppet (
uuid, number, name, name_quality, avatar_path, avatar_hash, avatar_url,
name_set, avatar_set, contact_info_set, is_registered,
name_set, avatar_set, contact_info_set, is_registered, profile_fetched_at,
custom_mxid, access_token
)
VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14
)
`
)
Expand All @@ -71,11 +72,12 @@ type Puppet struct {
NameSet bool
AvatarSet bool

IsRegistered bool
IsRegistered bool
ContactInfoSet bool
ProfileFetchedAt time.Time

CustomMXID id.UserID
AccessToken string
ContactInfoSet bool
CustomMXID id.UserID
AccessToken string
}

func newPuppet(qh *dbutil.QueryHelper[*Puppet]) *Puppet {
Expand All @@ -100,6 +102,7 @@ func (pq *PuppetQuery) GetAllWithCustomMXID(ctx context.Context) ([]*Puppet, err

func (p *Puppet) Scan(row dbutil.Scannable) (*Puppet, error) {
var number, customMXID sql.NullString
var profileFetchedAt sql.NullInt64
err := row.Scan(
&p.SignalID,
&number,
Expand All @@ -112,14 +115,18 @@ func (p *Puppet) Scan(row dbutil.Scannable) (*Puppet, error) {
&p.AvatarSet,
&p.ContactInfoSet,
&p.IsRegistered,
&profileFetchedAt,
&customMXID,
&p.AccessToken,
)
if err != nil {
return nil, nil
return nil, err
}
p.Number = number.String
p.CustomMXID = id.UserID(customMXID.String)
if profileFetchedAt.Valid {
p.ProfileFetchedAt = time.UnixMilli(profileFetchedAt.Int64)
}
return p, nil
}

Expand All @@ -136,6 +143,7 @@ func (p *Puppet) sqlVariables() []any {
p.AvatarSet,
p.ContactInfoSet,
p.IsRegistered,
dbutil.UnixMilliPtr(p.ProfileFetchedAt),
dbutil.StrPtr(p.CustomMXID),
p.AccessToken,
}
Expand Down
7 changes: 4 additions & 3 deletions database/upgrades/00-latest.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
-- v0 -> v19 (compatible with v17+): Latest revision
-- v0 -> v20 (compatible with v17+): Latest revision

CREATE TABLE portal (
chat_id TEXT NOT NULL,
Expand Down Expand Up @@ -33,8 +33,9 @@ CREATE TABLE puppet (
name_set BOOLEAN NOT NULL DEFAULT false,
avatar_set BOOLEAN NOT NULL DEFAULT false,

is_registered BOOLEAN NOT NULL DEFAULT false,
contact_info_set BOOLEAN NOT NULL DEFAULT false,
is_registered BOOLEAN NOT NULL DEFAULT false,
contact_info_set BOOLEAN NOT NULL DEFAULT false,
profile_fetched_at BIGINT,

custom_mxid TEXT,
access_token TEXT NOT NULL,
Expand Down
2 changes: 2 additions & 0 deletions database/upgrades/20-puppet-profile-fetch-ts.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- v20 (compatible with v17+): Add profile fetch timestamp for puppets
ALTER TABLE puppet ADD profile_fetched_at BIGINT;
2 changes: 2 additions & 0 deletions example-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ bridge:
private_chat_portal_meta: default
# Should avatars from the user's contact list be used? This is not safe on multi-user instances.
use_contact_avatars: false
# Should the bridge sync ghost user info even if profile fetching fails? This is not safe on multi-user instances.
use_outdated_profiles: false
# Should the Signal user's phone number be included in the room topic in private chat portal rooms?
number_in_topic: true
# Avatar image for the Note to Self room.
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ require (
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/stretchr/testify v1.8.4
github.com/tidwall/gjson v1.17.1
go.mau.fi/util v0.4.0
go.mau.fi/util v0.4.1-0.20240222202553-953608f657a3
golang.org/x/crypto v0.19.0
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a
golang.org/x/net v0.21.0
google.golang.org/protobuf v1.32.0
maunium.net/go/mautrix v0.18.0-beta.1
maunium.net/go/mautrix v0.18.0-beta.1.0.20240223191208-581aa8015501
nhooyr.io/websocket v1.8.10
)

Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,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.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA=
github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
go.mau.fi/util v0.4.0 h1:S2X3qU4pUcb/vxBRfAuZjbrR9xVMAXSjQojNBLPBbhs=
go.mau.fi/util v0.4.0/go.mod h1:leeiHtgVBuN+W9aDii3deAXnfC563iN3WK6BF8/AjNw=
go.mau.fi/util v0.4.1-0.20240222202553-953608f657a3 h1:NcRrdzORHKab5bP1Z8BpH0nxsxsvH0iPPZLpOUN+UIc=
go.mau.fi/util v0.4.1-0.20240222202553-953608f657a3/go.mod h1:leeiHtgVBuN+W9aDii3deAXnfC563iN3WK6BF8/AjNw=
go.mau.fi/zeroconfig v0.1.2 h1:DKOydWnhPMn65GbXZOafgkPm11BvFashZWLct0dGFto=
go.mau.fi/zeroconfig v0.1.2/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
Expand All @@ -99,7 +99,7 @@ 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/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8=
maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho=
maunium.net/go/mautrix v0.18.0-beta.1 h1:YAr4PxmcrJzUHR56p/MLDQ0qS7PsvaqXERAtC4aSkYA=
maunium.net/go/mautrix v0.18.0-beta.1/go.mod h1:1Q8P5C/uNmSBmull6DSqcawpg/E7hcGLQCD+JoU+vUo=
maunium.net/go/mautrix v0.18.0-beta.1.0.20240223191208-581aa8015501 h1:3STixn49dd7VXL+p4hW0AEWy5/BeZlgA3i3BVsIgtqM=
maunium.net/go/mautrix v0.18.0-beta.1.0.20240223191208-581aa8015501/go.mod h1:1Q8P5C/uNmSBmull6DSqcawpg/E7hcGLQCD+JoU+vUo=
nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q=
nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
12 changes: 9 additions & 3 deletions pkg/libsignalgo/profilekey.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,25 @@ type ProfileKeyCommitment [C.SignalPROFILE_KEY_COMMITMENT_LEN]byte
type ProfileKeyVersion [C.SignalPROFILE_KEY_VERSION_ENCODED_LEN]byte
type AccessKey [C.SignalACCESS_KEY_LEN]byte

var blankProfileKey ProfileKey

func (pk *ProfileKey) IsEmpty() bool {
return pk == nil || *pk == blankProfileKey
}

func (ak *AccessKey) String() string {
return string((*ak)[:])
return string(ak[:])
}

func (pv *ProfileKeyVersion) String() string {
return string((*pv)[:])
return string(pv[:])
}

func (pk *ProfileKey) Slice() []byte {
if pk == nil {
return nil
}
return (*pk)[:]
return pk[:]
}

func (pk *ProfileKey) GetCommitment(u uuid.UUID) (*ProfileKeyCommitment, error) {
Expand Down
57 changes: 19 additions & 38 deletions pkg/signalmeow/contact.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"net/http"
"strings"
Expand All @@ -46,27 +47,23 @@ func (cli *Client) StoreContactDetailsAsContact(ctx context.Context, contactDeta
Logger()
existingContact, err := cli.Store.ContactStore.LoadContact(ctx, parsedUUID)
if err != nil {
log.Err(err).Msg("error loading contact")
log.Err(err).Msg("Failed to load contact from database")
return nil, err
}
if existingContact == nil {
log.Debug().Msg("creating new contact")
existingContact = &types.Contact{
UUID: parsedUUID,
}
} else {
log.Debug().Msg("updating existing contact")
}

existingContact.E164 = contactDetails.GetNumber()
existingContact.ContactName = contactDetails.GetName()
if profileKeyString := contactDetails.GetProfileKey(); profileKeyString != nil {
profileKey := libsignalgo.ProfileKey(profileKeyString)
existingContact.ProfileKey = &profileKey
existingContact.Profile.Key = profileKey
err = cli.Store.ProfileKeyStore.StoreProfileKey(ctx, existingContact.UUID, profileKey)
if err != nil {
log.Err(err).Msg("storing profile key")
//return *existingContact, nil, err
log.Err(err).Msg("Failed to store profile key from contact")
}
}

Expand All @@ -86,10 +83,9 @@ func (cli *Client) StoreContactDetailsAsContact(ctx context.Context, contactDeta
}
}

log.Debug().Msg("storing contact")
storeErr := cli.Store.ContactStore.StoreContact(ctx, *existingContact)
if storeErr != nil {
log.Err(storeErr).Msg("error storing contact")
log.Err(storeErr).Msg("Failed to save contact")
return existingContact, storeErr
}
return existingContact, nil
Expand All @@ -104,7 +100,7 @@ func (cli *Client) fetchContactThenTryAndUpdateWithProfile(ctx context.Context,

existingContact, err := cli.Store.ContactStore.LoadContact(ctx, profileUUID)
if err != nil {
log.Err(err).Msg("error loading contact")
log.Err(err).Msg("Failed to load contact from database")
return nil, err
}
if existingContact == nil {
Expand All @@ -118,38 +114,26 @@ func (cli *Client) fetchContactThenTryAndUpdateWithProfile(ctx context.Context,
}
profile, err := cli.RetrieveProfileByID(ctx, profileUUID)
if err != nil {
log.Err(err).Msg("error retrieving profile")
//return nil, nil, err
// Don't return here, we still want to return what we have
logLevel := zerolog.ErrorLevel
if errors.Is(err, errProfileKeyNotFound) {
logLevel = zerolog.DebugLevel
}
log.WithLevel(logLevel).Err(err).Msg("Failed to fetch profile")
// Continue to return contact without profile
}

if profile != nil {
if existingContact.ProfileName != profile.Name {
existingContact.ProfileName = profile.Name
contactChanged = true
}
if existingContact.ProfileAbout != profile.About {
existingContact.ProfileAbout = profile.About
contactChanged = true
}
if existingContact.ProfileAboutEmoji != profile.AboutEmoji {
existingContact.ProfileAboutEmoji = profile.AboutEmoji
contactChanged = true
}
if existingContact.ProfileAvatarPath != profile.AvatarPath {
existingContact.ProfileAvatarPath = profile.AvatarPath
contactChanged = true
}
if existingContact.ProfileKey == nil || *existingContact.ProfileKey != profile.Key {
existingContact.ProfileKey = &profile.Key
// Don't bother saving every fetched timestamp to the database, but save if anything else changed
if !existingContact.Profile.Equals(profile) || existingContact.Profile.FetchedAt.IsZero() {
contactChanged = true
}
existingContact.Profile = *profile
}

if contactChanged {
err := cli.Store.ContactStore.StoreContact(ctx, *existingContact)
err = cli.Store.ContactStore.StoreContact(ctx, *existingContact)
if err != nil {
log.Err(err).Msg("error storing contact")
log.Err(err).Msg("Failed to save contact")
return nil, err
}
}
Expand All @@ -164,21 +148,18 @@ func (cli *Client) UpdateContactE164(ctx context.Context, uuid uuid.UUID, e164 s
Logger()
existingContact, err := cli.Store.ContactStore.LoadContact(ctx, uuid)
if err != nil {
log.Err(err).Msg("error loading contact")
log.Err(err).Msg("Failed to load contact from database")
return err
}
if existingContact == nil {
log.Debug().Msg("creating new contact")
existingContact = &types.Contact{
UUID: uuid,
}
} else {
log.Debug().Msg("found existing contact")
}
if existingContact.E164 == e164 {
return nil
}
log.Debug().Msg("e164 changed for contact")
log.Debug().Msg("Contact phone number changed")
existingContact.E164 = e164
return cli.Store.ContactStore.StoreContact(ctx, *existingContact)
}
Expand Down
Loading

0 comments on commit a78a88c

Please sign in to comment.