Files
status-go/internal/protocol/messenger_backup_handler.go
Igor Sirotin f9cc782a6f refactor: move services to pkg/services
Part of the Go project layout migration, item 31.

Pure move plus import-path rewrite across 687 files. No API or behaviour
change.

The services keep their grouping under pkg/services/<name> rather than
being promoted to pkg/<name>: 27 top-level directories in pkg/ would read
worse than what we have, and the grouping is what makes "an RPC service"
identifiable at a glance.

Paths that follow the move: the logosstorage test target and generate
step, the two wallet token-list tools, the migration-order check (and the
pre-rebase hook symlinked to it), and the storage env helper.

refs #7067
2026-08-21 10:11:05 +02:00

288 lines
8.4 KiB
Go

package protocol
import (
"context"
"database/sql"
"go.uber.org/zap"
"github.com/golang/protobuf/proto"
"github.com/status-im/status-go/internal/db/multiaccounts/errors"
"github.com/status-im/status-go/internal/db/multiaccounts/settings"
"github.com/status-im/status-go/internal/images"
"github.com/status-im/status-go/internal/logutils"
"github.com/status-im/status-go/internal/protocol/backupsync"
"github.com/status-im/status-go/internal/protocol/common"
"github.com/status-im/status-go/internal/protocol/communities"
"github.com/status-im/status-go/internal/protocol/protobuf"
"github.com/status-im/status-go/internal/signal"
ensservice "github.com/status-im/status-go/pkg/services/ens"
)
const (
SyncWakuSectionKeyProfile = "profile"
SyncWakuSectionKeyContacts = "contacts"
SyncWakuSectionKeyCommunities = "communities"
SyncWakuSectionKeySettings = "settings"
SyncWakuSectionKeyKeypairs = "keypairs"
SyncWakuSectionKeyWatchOnlyAccounts = "watchOnlyAccounts"
)
func (m *Messenger) handleLocalBackup(ctx context.Context, state *ReceivedMessageState, backup *protobuf.MessengerLocalBackup) []error {
var errors []error
err := m.handleBackedUpProfile(backup.Profile, backup.Clock)
if err != nil {
errors = append(errors, err)
}
if len(backup.Messages) > 0 {
err := m.persistence.SaveBackedUpMessages(backup.Messages)
if err != nil {
errors = append(errors, err)
}
signal.LocalMessageBackupDone()
}
for _, contact := range backup.Contacts {
err = m.HandleSyncInstallationContactV2(ctx, state, contact, nil)
if err != nil {
errors = append(errors, err)
}
}
err = m.handleSyncChats(state, backup.Chats)
if err != nil {
errors = append(errors, err)
}
communityErrors := m.handleLocalBackupCommunities(state, backup.Communities)
if len(communityErrors) > 0 {
errors = append(errors, communityErrors...)
}
return errors
}
func (m *Messenger) handleBackedUpProfile(message *protobuf.BackedUpProfile, backupTime uint64) error {
if message == nil {
return nil
}
response := backupsync.BackedUpDataResponse{
Profile: &backupsync.BackedUpProfile{},
}
err := common.ValidateDisplayName(&message.DisplayName)
if err != nil {
// Print a warning and set the display name to the account name, but don't stop the recovery
m.logger.Warn("invalid display name found", zap.Error(err))
response.SetDisplayName(m.account.Name)
} else {
err = m.SaveSyncDisplayName(message.DisplayName, message.DisplayNameClock)
if err != nil && err != errors.ErrNewClockOlderThanCurrent {
return err
}
response.SetDisplayName(message.DisplayName)
// if we already have a newer clock, then we don't need to update the display name
if err == errors.ErrNewClockOlderThanCurrent {
response.SetDisplayName(m.account.Name)
}
}
syncWithBackedUpImages := false
dbImages, err := m.multiAccounts.GetIdentityImages(message.KeyUid)
if err != nil {
if err != sql.ErrNoRows {
return err
}
// if images are deleted and no images were backed up, then we need to delete them on other devices,
// that's why we don't return in case of `sql.ErrNoRows`
syncWithBackedUpImages = true
}
if len(dbImages) == 0 {
if len(message.Pictures) > 0 {
syncWithBackedUpImages = true
}
} else {
// since both images (large and thumbnail) are always stored in the same time, we're free to use either of those two clocks for comparison
lastImageStoredClock := dbImages[0].Clock
syncWithBackedUpImages = lastImageStoredClock < backupTime
}
if syncWithBackedUpImages {
if len(message.Pictures) == 0 {
err = m.multiAccounts.DeleteIdentityImage(message.KeyUid)
if err != nil {
return err
}
response.SetImages(nil)
} else {
idImages := make([]images.IdentityImage, len(message.Pictures))
for i, pic := range message.Pictures {
img := images.IdentityImage{
Name: pic.Name,
Payload: pic.Payload,
Width: int(pic.Width),
Height: int(pic.Height),
FileSize: int(pic.FileSize),
ResizeTarget: int(pic.ResizeTarget),
Clock: pic.Clock,
}
idImages[i] = img
}
err = m.multiAccounts.StoreIdentityImages(message.KeyUid, idImages, false)
if err != nil {
return err
}
if m.httpServer != nil {
for j, image := range idImages {
idImages[j].LocalURL = m.httpServer.MakeAccountImageURL(message.KeyUid, image.Name, image.Clock)
}
}
response.SetImages(idImages)
}
}
profileShowcasePreferences, err := m.saveProfileShowcasePreferencesProto(message.ProfileShowcasePreferences, false)
if err != nil {
return err
}
if profileShowcasePreferences != nil {
response.SetProfileShowcasePreferences(profileShowcasePreferences)
}
var ensUsernameDetails []*ensservice.UsernameDetail
for _, d := range message.EnsUsernameDetails {
dd, err := m.saveEnsUsernameDetailProto(d)
if err != nil {
return err
}
ensUsernameDetails = append(ensUsernameDetails, dd)
}
response.SetEnsUsernameDetails(ensUsernameDetails)
if m.config.messengerSignalsHandler != nil {
m.config.messengerSignalsHandler.SendBackedUpProfile(&response)
}
return err
}
func syncInstallationCommunitiesSet(communities []*protobuf.SyncInstallationCommunity) map[string]*protobuf.SyncInstallationCommunity {
ret := map[string]*protobuf.SyncInstallationCommunity{}
for _, c := range communities {
id := string(c.GetId())
prevC, ok := ret[id]
if !ok || prevC.Clock < c.Clock {
ret[id] = c
}
}
return ret
}
func (m *Messenger) handleLocalBackupCommunities(state *ReceivedMessageState, communities []*protobuf.SyncInstallationCommunity) []error {
var errors []error
for _, syncCommunity := range syncInstallationCommunitiesSet(communities) {
err := m.handleSyncInstallationCommunity(context.Background(), state, syncCommunity)
if err != nil {
errors = append(errors, err)
continue
}
err = m.requestCommunityKeysAndSharedAddresses(syncCommunity)
if err != nil {
errors = append(errors, err)
}
}
return errors
}
func (m *Messenger) PublishSettingEvent(settingField *settings.SyncSettingField) {
if m.config.messengerSignalsHandler != nil {
response := backupsync.BackedUpDataResponse{
Setting: settingField,
}
m.config.messengerSignalsHandler.SendBackedUpSettings(&response)
}
}
func (m *Messenger) requestCommunityKeysAndSharedAddresses(syncCommunity *protobuf.SyncInstallationCommunity) error {
ctx, span := m.tracer.Start(context.Background(), "Messenger.requestCommunityKeysAndSharedAddresses")
defer span.End()
if !syncCommunity.Joined {
return nil
}
community, err := m.GetCommunityByID(syncCommunity.Id)
if err != nil {
return err
}
if community == nil {
return communities.ErrOrgNotFound
}
// Send a request to get back our previous shared addresses
request := &protobuf.CommunitySharedAddressesRequest{
CommunityId: syncCommunity.Id,
}
payload, err := proto.Marshal(request)
if err != nil {
return err
}
rawMessage := &common.RawMessage{
Payload: payload,
Sender: m.identity,
CommunityID: community.ID(),
SkipEncryptionLayer: true,
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_SHARED_ADDRESSES_REQUEST,
}
_, err = m.SendMessageToControlNode(ctx, community, rawMessage)
if err != nil {
m.logger.Error("failed to request shared addresses", zap.String("communityId", logutils.TruncateWithDot(community.IDString())), zap.Error(err))
return err
}
// If the community is encrypted or one channel is, ask for the encryption keys back
isEncrypted := syncCommunity.Encrypted || len(syncCommunity.EncryptionKeysV2) > 0
if !isEncrypted {
// check if we have encrypted channels
myPk := m.IdentityPublicKeyString()
for channelID, channel := range community.Chats() {
_, exists := channel.GetMembers()[myPk]
if exists && community.ChannelEncrypted(channelID) {
isEncrypted = true
break
}
}
}
if isEncrypted {
err = m.requestCommunityEncryptionKeys(community, nil)
if err != nil {
m.logger.Error("failed to request community encryption keys", zap.String("communityId", logutils.TruncateWithDot(community.IDString())), zap.Error(err))
return err
}
}
return nil
}
func (m *Messenger) HandleBackedUpMessageBatch(ctx context.Context, state *ReceivedMessageState, messageBatch *protobuf.BackedUpMessageBatch, msg *common.StatusMessage) error {
// BackedUpMessages can only be sent in the context of a local backup
return nil
}