Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent downgrading ghost user info by default #464

Merged
merged 1 commit into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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