2021-10-11 15:39:52 +00:00
|
|
|
package protocol
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
|
|
|
|
"github.com/status-im/status-go/protocol/common"
|
|
|
|
"github.com/status-im/status-go/protocol/protobuf"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
BackupContactsPerBatch = 20
|
|
|
|
)
|
|
|
|
|
|
|
|
// backupTickerInterval is how often we should check for backups
|
|
|
|
var backupTickerInterval = 120 * time.Second
|
|
|
|
|
|
|
|
// backupIntervalSeconds is the amount of seconds we should allow between
|
|
|
|
// backups
|
|
|
|
var backupIntervalSeconds uint64 = 28800
|
|
|
|
|
|
|
|
func (m *Messenger) backupEnabled() (bool, error) {
|
|
|
|
return m.settings.BackupEnabled()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) lastBackup() (uint64, error) {
|
|
|
|
return m.settings.LastBackup()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) startBackupLoop() {
|
|
|
|
ticker := time.NewTicker(backupTickerInterval)
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ticker.C:
|
|
|
|
if !m.online() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
enabled, err := m.backupEnabled()
|
|
|
|
if err != nil {
|
|
|
|
m.logger.Error("failed to fetch backup enabled")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !enabled {
|
|
|
|
m.logger.Debug("backup not enabled, skipping")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
lastBackup, err := m.lastBackup()
|
|
|
|
if err != nil {
|
|
|
|
m.logger.Error("failed to fetch last backup time")
|
2021-10-27 10:59:43 +00:00
|
|
|
continue
|
2021-10-11 15:39:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
now := time.Now().Unix()
|
|
|
|
if uint64(now) <= backupIntervalSeconds+lastBackup {
|
|
|
|
m.logger.Debug("not backing up")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
m.logger.Debug("backing up data")
|
|
|
|
|
2021-10-27 10:59:43 +00:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
|
|
|
defer cancel()
|
|
|
|
_, err = m.BackupData(ctx)
|
2021-10-11 15:39:52 +00:00
|
|
|
if err != nil {
|
|
|
|
m.logger.Error("failed to backup data", zap.Error(err))
|
|
|
|
}
|
|
|
|
case <-m.quit:
|
2021-10-22 14:20:42 +00:00
|
|
|
ticker.Stop()
|
2021-10-11 15:39:52 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) BackupData(ctx context.Context) (uint64, error) {
|
|
|
|
var contacts []*protobuf.SyncInstallationContactV2
|
|
|
|
m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
|
|
|
|
syncContact := m.syncBackupContact(ctx, contact)
|
|
|
|
if syncContact != nil {
|
|
|
|
contacts = append(contacts, syncContact)
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
|
|
|
clock, chat := m.getLastClockWithRelatedChat()
|
|
|
|
|
|
|
|
for i := 0; i < len(contacts); i += BackupContactsPerBatch {
|
|
|
|
j := i + BackupContactsPerBatch
|
|
|
|
if j > len(contacts) {
|
|
|
|
j = len(contacts)
|
|
|
|
}
|
|
|
|
|
|
|
|
contactsToAdd := contacts[i:j]
|
|
|
|
|
|
|
|
backupMessage := &protobuf.Backup{
|
|
|
|
Contacts: contactsToAdd,
|
|
|
|
}
|
|
|
|
|
|
|
|
encodedMessage, err := proto.Marshal(backupMessage)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
2021-11-05 15:11:10 +00:00
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
SkipEncryption: true,
|
|
|
|
SendOnPersonalTopic: true,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_BACKUP,
|
2021-10-11 15:39:52 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2022-01-06 16:35:08 +00:00
|
|
|
|
2022-04-11 16:14:08 +00:00
|
|
|
joinedCs, err := m.communitiesManager.JoinedAndPendingCommunitiesWithRequests()
|
2022-01-06 16:35:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2022-04-11 16:14:08 +00:00
|
|
|
|
|
|
|
deletedCs, err := m.communitiesManager.DeletedCommunities()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
cs := append(joinedCs, deletedCs...)
|
2022-01-06 16:35:08 +00:00
|
|
|
for _, c := range cs {
|
2022-09-29 11:50:23 +00:00
|
|
|
_, beingImported := m.importingCommunities[c.IDString()]
|
|
|
|
if !beingImported {
|
|
|
|
settings, err := m.communitiesManager.GetCommunitySettingsByID(c.ID())
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2022-01-06 16:35:08 +00:00
|
|
|
|
2022-09-29 11:50:23 +00:00
|
|
|
syncMessage, err := c.ToSyncCommunityProtobuf(clock, settings)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2022-01-06 16:35:08 +00:00
|
|
|
|
2022-11-07 17:30:00 +00:00
|
|
|
encodedKeys, err := m.encryptor.GetAllHREncodedKeys(c.ID())
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
syncMessage.EncryptionKeys = encodedKeys
|
|
|
|
|
2022-09-29 11:50:23 +00:00
|
|
|
backupMessage := &protobuf.Backup{
|
|
|
|
Communities: []*protobuf.SyncCommunity{syncMessage},
|
|
|
|
}
|
2022-01-06 16:35:08 +00:00
|
|
|
|
2022-09-29 11:50:23 +00:00
|
|
|
encodedMessage, err := proto.Marshal(backupMessage)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2022-01-06 16:35:08 +00:00
|
|
|
|
2022-09-29 11:50:23 +00:00
|
|
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
SkipEncryption: true,
|
|
|
|
SendOnPersonalTopic: true,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_BACKUP,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2022-01-06 16:35:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-11 15:39:52 +00:00
|
|
|
chat.LastClockValue = clock
|
2022-01-06 16:35:08 +00:00
|
|
|
err = m.saveChat(chat)
|
2021-10-11 15:39:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2022-01-06 16:35:08 +00:00
|
|
|
clockInSeconds := clock / 1000
|
|
|
|
err = m.settings.SetLastBackup(clockInSeconds)
|
2021-10-11 15:39:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
if m.config.messengerSignalsHandler != nil {
|
2022-01-06 16:35:08 +00:00
|
|
|
m.config.messengerSignalsHandler.BackupPerformed(clockInSeconds)
|
2021-10-11 15:39:52 +00:00
|
|
|
}
|
|
|
|
|
2022-01-06 16:35:08 +00:00
|
|
|
return clockInSeconds, nil
|
2021-10-11 15:39:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// syncContact sync as contact with paired devices
|
|
|
|
func (m *Messenger) syncBackupContact(ctx context.Context, contact *Contact) *protobuf.SyncInstallationContactV2 {
|
|
|
|
if contact.IsSyncing {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var ensName string
|
|
|
|
if contact.ENSVerified {
|
2022-02-17 15:13:10 +00:00
|
|
|
ensName = contact.EnsName
|
2021-10-11 15:39:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
oneToOneChat, ok := m.allChats.Load(contact.ID)
|
|
|
|
muted := false
|
|
|
|
if ok {
|
|
|
|
muted = oneToOneChat.Muted
|
|
|
|
}
|
|
|
|
|
|
|
|
return &protobuf.SyncInstallationContactV2{
|
2021-10-22 14:20:42 +00:00
|
|
|
LastUpdatedLocally: contact.LastUpdatedLocally,
|
|
|
|
LastUpdated: contact.LastUpdated,
|
|
|
|
Id: contact.ID,
|
|
|
|
EnsName: ensName,
|
|
|
|
LocalNickname: contact.LocalNickname,
|
|
|
|
Added: contact.Added,
|
|
|
|
Blocked: contact.Blocked,
|
|
|
|
Muted: muted,
|
|
|
|
HasAddedUs: contact.HasAddedUs,
|
|
|
|
Removed: contact.Removed,
|
2022-07-05 19:49:44 +00:00
|
|
|
VerificationStatus: int64(contact.VerificationStatus),
|
|
|
|
TrustStatus: int64(contact.TrustStatus),
|
2021-10-11 15:39:52 +00:00
|
|
|
}
|
|
|
|
}
|