2019-11-21 16:19:22 +00:00
package protocol
2019-10-14 14:10:48 +00:00
import (
2021-07-27 12:43:46 +00:00
"context"
2020-01-10 18:59:01 +00:00
"crypto/ecdsa"
2023-01-10 17:59:32 +00:00
"database/sql"
2020-01-10 18:59:01 +00:00
"encoding/hex"
2020-02-10 11:22:37 +00:00
"fmt"
2022-12-12 09:22:37 +00:00
"sync"
2023-04-16 15:06:00 +00:00
"time"
2021-03-05 14:38:32 +00:00
2024-08-30 19:28:03 +00:00
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/services/accounts/accountsevent"
2023-08-18 11:39:59 +00:00
"github.com/status-im/status-go/services/browsers"
2023-04-06 14:06:24 +00:00
"github.com/status-im/status-go/signal"
2019-10-14 14:10:48 +00:00
"github.com/pkg/errors"
2021-05-14 21:22:50 +00:00
"go.uber.org/zap"
2023-06-08 12:00:19 +00:00
"github.com/google/uuid"
2024-03-28 15:57:59 +00:00
utils "github.com/status-im/status-go/common"
2020-01-10 18:59:01 +00:00
"github.com/status-im/status-go/eth-node/crypto"
2020-08-07 13:49:37 +00:00
"github.com/status-im/status-go/eth-node/types"
2020-11-25 00:34:32 +00:00
"github.com/status-im/status-go/images"
2022-05-18 10:42:51 +00:00
"github.com/status-im/status-go/multiaccounts/accounts"
2023-06-05 07:12:15 +00:00
multiaccountscommon "github.com/status-im/status-go/multiaccounts/common"
2022-03-23 18:47:00 +00:00
"github.com/status-im/status-go/multiaccounts/settings"
2023-12-04 10:18:05 +00:00
walletsettings "github.com/status-im/status-go/multiaccounts/settings_wallet"
2020-07-22 07:41:40 +00:00
"github.com/status-im/status-go/protocol/common"
2020-11-18 09:16:51 +00:00
"github.com/status-im/status-go/protocol/communities"
2020-01-10 18:59:01 +00:00
"github.com/status-im/status-go/protocol/encryption/multidevice"
2023-12-15 16:16:18 +00:00
"github.com/status-im/status-go/protocol/peersyncing"
2019-12-02 15:34:05 +00:00
"github.com/status-im/status-go/protocol/protobuf"
2022-07-01 13:54:02 +00:00
"github.com/status-im/status-go/protocol/requests"
2022-09-29 11:50:23 +00:00
"github.com/status-im/status-go/protocol/transport"
2021-05-14 21:22:50 +00:00
v1protocol "github.com/status-im/status-go/protocol/v1"
2022-07-05 19:49:44 +00:00
"github.com/status-im/status-go/protocol/verification"
2019-10-14 14:10:48 +00:00
)
2020-02-21 14:48:53 +00:00
const (
transactionRequestDeclinedMessage = "Transaction request declined"
requestAddressForTransactionAcceptedMessage = "Request address for transaction accepted"
requestAddressForTransactionDeclinedMessage = "Request address for transaction declined"
)
2023-04-19 14:44:57 +00:00
var (
2024-01-17 13:12:49 +00:00
ErrMessageNotAllowed = errors . New ( "message from a non-contact" )
ErrMessageForWrongChatType = errors . New ( "message for the wrong chat type" )
ErrNotWatchOnlyAccount = errors . New ( "an account is not a watch only account" )
ErrWalletAccountNotSupportedForMobileApp = errors . New ( "handling account is not supported for mobile app" )
ErrTryingToApplyOldWalletAccountsOrder = errors . New ( "trying to apply old wallet accounts order" )
ErrTryingToStoreOldWalletAccount = errors . New ( "trying to store an old wallet account" )
ErrTryingToStoreOldKeypair = errors . New ( "trying to store an old keypair" )
ErrSomeFieldsMissingForWalletAccount = errors . New ( "some fields are missing for wallet account" )
ErrUnknownKeypairForWalletAccount = errors . New ( "keypair is not known for the wallet account" )
ErrInvalidCommunityID = errors . New ( "invalid community id" )
ErrTryingToApplyOldTokenPreferences = errors . New ( "trying to apply old token preferences" )
ErrTryingToApplyOldCollectiblePreferences = errors . New ( "trying to apply old collectible preferences" )
2024-05-31 17:16:45 +00:00
ErrOutdatedCommunityRequestToJoin = errors . New ( "outdated community request to join response" )
2023-04-19 14:44:57 +00:00
)
2021-04-07 12:57:14 +00:00
2019-10-14 14:10:48 +00:00
// HandleMembershipUpdate updates a Chat instance according to the membership updates.
// It retrieves chat, if exists, and merges membership updates from the message.
// Finally, the Chat is updated with the new group events.
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleMembershipUpdateMessage ( messageState * ReceivedMessageState , rawMembershipUpdate * protobuf . MembershipUpdateMessage , statusMessage * v1protocol . StatusMessage ) error {
chat , _ := messageState . AllChats . Load ( rawMembershipUpdate . ChatId )
return m . HandleMembershipUpdate ( messageState , chat , rawMembershipUpdate , m . systemMessagesTranslations )
}
func ( m * Messenger ) HandleMembershipUpdate ( messageState * ReceivedMessageState , chat * Chat , rawMembershipUpdate * protobuf . MembershipUpdateMessage , translations * systemMessageTranslationsMap ) error {
2020-01-10 18:59:01 +00:00
var group * v1protocol . Group
var err error
2023-08-18 11:39:59 +00:00
if rawMembershipUpdate == nil {
return nil
}
2020-01-20 16:44:32 +00:00
logger := m . logger . With ( zap . String ( "site" , "HandleMembershipUpdate" ) )
2023-08-18 11:39:59 +00:00
message , err := v1protocol . MembershipUpdateMessageFromProtobuf ( rawMembershipUpdate )
2020-01-10 18:59:01 +00:00
if err != nil {
return err
}
2020-01-20 16:44:32 +00:00
if err := ValidateMembershipUpdateMessage ( message , messageState . Timesource . GetCurrentTime ( ) ) ; err != nil {
logger . Warn ( "failed to validate message" , zap . Error ( err ) )
return err
}
2021-11-25 15:21:42 +00:00
senderID := messageState . CurrentMessageState . Contact . ID
allowed , err := m . isMessageAllowedFrom ( senderID , chat )
2021-04-07 12:57:14 +00:00
if err != nil {
return err
}
if ! allowed {
return ErrMessageNotAllowed
}
2020-08-07 13:49:37 +00:00
//if chat.InvitationAdmin exists means we are waiting for invitation request approvement, and in that case
//we need to create a new chat instance like we don't have a chat and just use a regular invitation flow
2021-07-27 12:43:46 +00:00
waitingForApproval := chat != nil && len ( chat . InvitationAdmin ) > 0
2021-09-27 14:02:25 +00:00
ourKey := contactIDFromPublicKey ( & m . identity . PublicKey )
2023-01-20 14:28:30 +00:00
isActive := messageState . CurrentMessageState . Contact . added ( ) || messageState . CurrentMessageState . Contact . ID == ourKey || waitingForApproval
2021-11-19 14:32:04 +00:00
showPushNotification := isActive && messageState . CurrentMessageState . Contact . ID != ourKey
2021-07-27 12:43:46 +00:00
2021-11-19 14:32:04 +00:00
// wasUserAdded indicates whether the user has been added to the group with this update
wasUserAdded := false
2021-07-27 12:43:46 +00:00
if chat == nil || waitingForApproval {
2020-01-10 18:59:01 +00:00
if len ( message . Events ) == 0 {
return errors . New ( "can't create new group chat without events" )
2019-10-14 14:10:48 +00:00
}
2020-08-07 13:49:37 +00:00
//approve invitations
2021-07-27 12:43:46 +00:00
if waitingForApproval {
2020-08-07 13:49:37 +00:00
groupChatInvitation := & GroupChatInvitation {
2023-08-18 11:39:59 +00:00
GroupChatInvitation : & protobuf . GroupChatInvitation {
2020-08-07 13:49:37 +00:00
ChatId : message . ChatID ,
} ,
From : types . EncodeHex ( crypto . FromECDSAPub ( & m . identity . PublicKey ) ) ,
}
groupChatInvitation , err = m . persistence . InvitationByID ( groupChatInvitation . ID ( ) )
2020-10-08 10:46:03 +00:00
if err != nil && err != common . ErrRecordNotFound {
2020-08-07 13:49:37 +00:00
return err
}
if groupChatInvitation != nil {
groupChatInvitation . State = protobuf . GroupChatInvitation_APPROVED
err := m . persistence . SaveInvitation ( groupChatInvitation )
if err != nil {
return err
}
messageState . GroupChatInvitations [ groupChatInvitation . ID ( ) ] = groupChatInvitation
}
}
2020-01-10 18:59:01 +00:00
group , err = v1protocol . NewGroupWithEvents ( message . ChatID , message . Events )
2019-10-14 14:10:48 +00:00
if err != nil {
2020-01-10 18:59:01 +00:00
return err
2019-10-14 14:10:48 +00:00
}
2019-12-02 15:34:05 +00:00
2023-08-09 17:07:28 +00:00
// A new chat must have contained us at some point
wasEverMember , err := group . WasEverMember ( ourKey )
if err != nil {
return err
}
if ! wasEverMember {
2020-01-10 18:59:01 +00:00
return errors . New ( "can't create a new group chat without us being a member" )
2019-10-14 14:10:48 +00:00
}
2023-08-09 17:07:28 +00:00
wasUserAdded = group . IsMember ( ourKey )
2020-02-07 11:30:26 +00:00
newChat := CreateGroupChat ( messageState . Timesource )
2021-04-07 12:57:14 +00:00
// We set group chat inactive and create a notification instead
2023-01-05 15:37:50 +00:00
// unless is coming from us or a contact or were waiting for approval.
// Also, as message MEMBER_JOINED may come from member(not creator, not our contact)
// reach earlier than CHAT_CREATED from creator, we need check if creator is our contact
newChat . Active = isActive || m . checkIfCreatorIsOurContact ( group )
2021-11-25 15:21:42 +00:00
newChat . ReceivedInvitationAdmin = senderID
2020-01-10 18:59:01 +00:00
chat = & newChat
2021-09-27 14:02:25 +00:00
chat . updateChatFromGroupMembershipChanges ( group )
if err != nil {
return errors . Wrap ( err , "failed to get group creator" )
}
2023-10-25 18:11:04 +00:00
publicKeys , err := group . MemberPublicKeys ( )
if err != nil {
return errors . Wrap ( err , "failed to get group members" )
}
filters , err := m . transport . JoinGroup ( publicKeys )
if err != nil {
return errors . Wrap ( err , "failed to join group" )
}
ok , err := m . scheduleSyncFilters ( filters )
if err != nil {
return errors . Wrap ( err , "failed to schedule sync filter" )
}
m . logger . Debug ( "result of schedule sync filter" , zap . Bool ( "ok" , ok ) )
2020-01-10 18:59:01 +00:00
} else {
existingGroup , err := newProtocolGroupFromChat ( chat )
if err != nil {
return errors . Wrap ( err , "failed to create a Group from Chat" )
}
updateGroup , err := v1protocol . NewGroupWithEvents ( message . ChatID , message . Events )
if err != nil {
return errors . Wrap ( err , "invalid membership update" )
}
merged := v1protocol . MergeMembershipUpdateEvents ( existingGroup . Events ( ) , updateGroup . Events ( ) )
2020-03-09 06:19:23 +00:00
group , err = v1protocol . NewGroupWithEvents ( chat . ID , merged )
2020-01-10 18:59:01 +00:00
if err != nil {
return errors . Wrap ( err , "failed to create a group with new membership updates" )
}
2021-09-27 14:02:25 +00:00
chat . updateChatFromGroupMembershipChanges ( group )
2021-11-19 14:32:04 +00:00
// Reactivate deleted group chat on re-invite from contact
2023-05-30 13:49:46 +00:00
chat . Active = chat . Active || ( isActive && group . IsMember ( ourKey ) )
wasUserAdded = ! existingGroup . IsMember ( ourKey ) && group . IsMember ( ourKey )
2021-11-19 14:32:04 +00:00
// Show push notifications when our key is added to members list and chat is Active
showPushNotification = showPushNotification && wasUserAdded
2020-01-10 18:59:01 +00:00
}
2023-01-10 17:47:10 +00:00
maxClockVal := uint64 ( 0 )
for _ , event := range group . Events ( ) {
if event . ClockValue > maxClockVal {
maxClockVal = event . ClockValue
}
}
if chat . LastClockValue < maxClockVal {
chat . LastClockValue = maxClockVal
}
2020-01-10 18:59:01 +00:00
2021-11-19 14:32:04 +00:00
// Only create a message notification when the user is added, not when removed
if ! chat . Active && wasUserAdded {
chat . Highlight = true
2023-05-08 17:02:54 +00:00
m . createMessageNotification ( chat , messageState , chat . LastMessage )
2021-04-07 12:57:14 +00:00
}
2020-04-22 12:58:28 +00:00
2021-11-19 14:32:04 +00:00
profilePicturesVisibility , err := m . settings . GetProfilePicturesVisibility ( )
if err != nil {
return errors . Wrap ( err , "failed to get profilePicturesVisibility setting" )
}
if showPushNotification {
// chat is highlighted for new group invites or group re-invites
chat . Highlight = true
messageState . Response . AddNotification ( NewPrivateGroupInviteNotification ( chat . ID , chat , messageState . CurrentMessageState . Contact , profilePicturesVisibility ) )
}
2020-01-10 18:59:01 +00:00
systemMessages := buildSystemMessages ( message . Events , translations )
for _ , message := range systemMessages {
messageID := message . ID
exists , err := m . messageExists ( messageID , messageState . ExistingMessagesMap )
if err != nil {
m . logger . Warn ( "failed to check message exists" , zap . Error ( err ) )
}
if exists {
continue
}
2021-06-03 13:11:55 +00:00
messageState . Response . AddMessage ( message )
2020-01-10 18:59:01 +00:00
}
2021-04-07 12:57:14 +00:00
messageState . Response . AddChat ( chat )
2020-01-10 18:59:01 +00:00
// Store in chats map as it might be a new one
2021-03-29 15:41:30 +00:00
messageState . AllChats . Store ( chat . ID , chat )
2020-01-10 18:59:01 +00:00
2022-05-20 10:53:28 +00:00
// explicit join has been removed, mimic auto-join for backward compatibility
// no all cases are covered, e.g. if added to a group by non-contact
autoJoin := chat . Active && wasUserAdded
if autoJoin || waitingForApproval {
2021-07-27 12:43:46 +00:00
_ , err = m . ConfirmJoiningGroup ( context . Background ( ) , chat . ID )
if err != nil {
return err
}
}
2020-07-26 21:37:04 +00:00
if message . Message != nil {
2023-09-07 10:33:20 +00:00
return m . HandleChatMessage ( messageState , message . Message , nil , false )
2020-07-28 14:34:49 +00:00
} else if message . EmojiReaction != nil {
2023-08-18 11:39:59 +00:00
return m . HandleEmojiReaction ( messageState , message . EmojiReaction , nil )
2019-10-14 14:10:48 +00:00
}
2020-01-10 18:59:01 +00:00
return nil
}
2023-01-05 15:37:50 +00:00
func ( m * Messenger ) checkIfCreatorIsOurContact ( group * v1protocol . Group ) bool {
creator , err := group . Creator ( )
if err == nil {
contact , _ := m . allContacts . Load ( creator )
2023-01-20 14:28:30 +00:00
return contact != nil && contact . mutual ( )
2023-01-05 15:37:50 +00:00
}
m . logger . Warn ( "failed to get creator from group" , zap . String ( "group name" , group . Name ( ) ) , zap . String ( "group chat id" , group . ChatID ( ) ) , zap . Error ( err ) )
return false
}
2023-05-08 17:02:54 +00:00
func ( m * Messenger ) createMessageNotification ( chat * Chat , messageState * ReceivedMessageState , message * common . Message ) {
2021-05-26 06:38:25 +00:00
var notificationType ActivityCenterType
if chat . OneToOne ( ) {
notificationType = ActivityCenterNotificationTypeNewOneToOne
} else {
notificationType = ActivityCenterNotificationTypeNewPrivateGroupChat
}
notification := & ActivityCenterNotification {
ID : types . FromHex ( chat . ID ) ,
Name : chat . Name ,
2023-05-08 17:02:54 +00:00
LastMessage : message ,
2021-05-26 06:38:25 +00:00
Type : notificationType ,
2021-06-17 15:08:28 +00:00
Author : messageState . CurrentMessageState . Contact . ID ,
2021-05-26 06:38:25 +00:00
Timestamp : messageState . CurrentMessageState . WhisperTimestamp ,
ChatID : chat . ID ,
2022-10-25 22:06:20 +00:00
CommunityID : chat . CommunityID ,
2023-10-22 09:41:20 +00:00
UpdatedAt : m . GetCurrentTimeInMillis ( ) ,
2021-05-26 06:38:25 +00:00
}
2021-09-27 14:02:25 +00:00
2023-10-22 09:41:20 +00:00
err := m . addActivityCenterNotification ( messageState . Response , notification , nil )
2021-05-26 06:38:25 +00:00
if err != nil {
m . logger . Warn ( "failed to create activity center notification" , zap . Error ( err ) )
}
}
2022-06-13 10:57:51 +00:00
func ( m * Messenger ) PendingNotificationContactRequest ( contactID string ) ( * ActivityCenterNotification , error ) {
return m . persistence . ActiveContactRequestNotification ( contactID )
}
2023-04-25 11:27:15 +00:00
func ( m * Messenger ) createContactRequestForContactUpdate ( contact * Contact , messageState * ReceivedMessageState ) ( * common . Message , error ) {
2023-08-18 11:39:59 +00:00
2023-04-25 11:27:15 +00:00
contactRequest , err := m . generateContactRequest (
2023-08-18 11:39:59 +00:00
contact . ContactRequestRemoteClock ,
2023-04-25 11:27:15 +00:00
messageState . CurrentMessageState . WhisperTimestamp ,
contact ,
2023-05-22 12:22:33 +00:00
defaultContactRequestText ( ) ,
2023-04-25 11:27:15 +00:00
false ,
)
if err != nil {
return nil , err
}
contactRequest . ID = defaultContactRequestID ( contact . ID )
// save this message
messageState . Response . AddMessage ( contactRequest )
err = m . persistence . SaveMessages ( [ ] * common . Message { contactRequest } )
if err != nil {
return nil , err
}
return contactRequest , nil
}
2023-02-21 18:08:11 +00:00
func ( m * Messenger ) createIncomingContactRequestNotification ( contact * Contact , messageState * ReceivedMessageState , contactRequest * common . Message , createNewNotification bool ) error {
2023-04-25 11:27:15 +00:00
if contactRequest . ContactRequestState == common . ContactRequestStateAccepted {
2023-02-21 18:08:11 +00:00
// Pull one from the db if there
notification , err := m . persistence . GetActivityCenterNotificationByID ( types . FromHex ( contactRequest . ID ) )
if err != nil {
return err
}
if notification != nil {
2023-04-25 11:27:15 +00:00
notification . Name = contact . PrimaryName ( )
2023-02-21 18:08:11 +00:00
notification . Message = contactRequest
notification . Read = true
notification . Accepted = true
notification . Dismissed = false
2023-10-22 09:41:20 +00:00
notification . UpdatedAt = m . GetCurrentTimeInMillis ( )
2023-06-10 02:00:17 +00:00
_ , err = m . persistence . SaveActivityCenterNotification ( notification , true )
2023-02-21 18:08:11 +00:00
if err != nil {
return err
}
messageState . Response . AddMessage ( contactRequest )
messageState . Response . AddActivityCenterNotification ( notification )
}
return nil
}
2022-01-18 16:31:34 +00:00
2023-02-13 20:09:20 +00:00
if ! createNewNotification {
return nil
}
2022-01-18 16:31:34 +00:00
notification := & ActivityCenterNotification {
ID : types . FromHex ( contactRequest . ID ) ,
2023-02-22 16:57:33 +00:00
Name : contact . PrimaryName ( ) ,
2022-01-18 16:31:34 +00:00
Message : contactRequest ,
Type : ActivityCenterNotificationTypeContactRequest ,
2023-05-22 12:22:33 +00:00
Author : contactRequest . From ,
Timestamp : contactRequest . WhisperTimestamp ,
2022-01-18 16:31:34 +00:00
ChatID : contact . ID ,
2023-01-28 09:52:53 +00:00
Read : contactRequest . ContactRequestState == common . ContactRequestStateAccepted || contactRequest . ContactRequestState == common . ContactRequestStateDismissed ,
Accepted : contactRequest . ContactRequestState == common . ContactRequestStateAccepted ,
Dismissed : contactRequest . ContactRequestState == common . ContactRequestStateDismissed ,
2023-10-22 09:41:20 +00:00
UpdatedAt : m . GetCurrentTimeInMillis ( ) ,
2022-01-18 16:31:34 +00:00
}
2023-10-22 09:41:20 +00:00
return m . addActivityCenterNotification ( messageState . Response , notification , nil )
2022-01-18 16:31:34 +00:00
}
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) handleCommandMessage ( state * ReceivedMessageState , message * common . Message ) error {
2020-01-10 18:59:01 +00:00
message . ID = state . CurrentMessageState . MessageID
message . From = state . CurrentMessageState . Contact . ID
message . Alias = state . CurrentMessageState . Contact . Alias
message . SigPubKey = state . CurrentMessageState . PublicKey
message . Identicon = state . CurrentMessageState . Contact . Identicon
message . WhisperTimestamp = state . CurrentMessageState . WhisperTimestamp
2021-05-25 09:40:02 +00:00
if err := message . PrepareContent ( common . PubkeyToHex ( & m . identity . PublicKey ) ) ; err != nil {
2020-02-10 11:22:37 +00:00
return fmt . Errorf ( "failed to prepare content: %v" , err )
}
2024-03-01 17:15:38 +00:00
// Get Application layer messageType from commandState
// Currently this is not really used in `matchChatEntity`, but I did want to pass UNKNOWN there.
var messageType protobuf . ApplicationMetadataMessage_Type
switch message . CommandParameters . CommandState {
case common . CommandStateRequestAddressForTransaction :
messageType = protobuf . ApplicationMetadataMessage_REQUEST_ADDRESS_FOR_TRANSACTION
case common . CommandStateRequestAddressForTransactionAccepted :
messageType = protobuf . ApplicationMetadataMessage_ACCEPT_REQUEST_ADDRESS_FOR_TRANSACTION
case common . CommandStateRequestAddressForTransactionDeclined :
messageType = protobuf . ApplicationMetadataMessage_DECLINE_REQUEST_ADDRESS_FOR_TRANSACTION
case common . CommandStateRequestTransaction :
messageType = protobuf . ApplicationMetadataMessage_REQUEST_TRANSACTION
case common . CommandStateRequestTransactionDeclined :
messageType = protobuf . ApplicationMetadataMessage_DECLINE_REQUEST_TRANSACTION
default :
messageType = protobuf . ApplicationMetadataMessage_UNKNOWN
}
chat , err := m . matchChatEntity ( message , messageType )
2019-10-14 14:10:48 +00:00
if err != nil {
2020-01-10 18:59:01 +00:00
return err
2019-10-14 14:10:48 +00:00
}
2020-01-10 18:59:01 +00:00
2021-06-08 06:07:45 +00:00
allowed , err := m . isMessageAllowedFrom ( state . CurrentMessageState . Contact . ID , chat )
2021-04-07 12:57:14 +00:00
if err != nil {
return err
}
if ! allowed {
return ErrMessageNotAllowed
}
2020-01-10 18:59:01 +00:00
// If deleted-at is greater, ignore message
if chat . DeletedAtClockValue >= message . Clock {
return nil
}
// Set the LocalChatID for the message
message . LocalChatID = chat . ID
2021-03-29 15:41:30 +00:00
if c , ok := state . AllChats . Load ( chat . ID ) ; ok {
2020-01-10 18:59:01 +00:00
chat = c
}
// Set the LocalChatID for the message
message . LocalChatID = chat . ID
// Increase unviewed count
2020-07-06 08:54:22 +00:00
if ! common . IsPubKeyEqual ( message . SigPubKey , & m . identity . PublicKey ) {
2023-09-22 10:31:35 +00:00
m . updateUnviewedCounts ( chat , message )
2020-01-10 18:59:01 +00:00
message . OutgoingStatus = ""
} else {
// Our own message, mark as sent
2020-09-01 13:27:01 +00:00
message . OutgoingStatus = common . OutgoingStatusSent
2020-01-10 18:59:01 +00:00
}
2020-01-20 16:44:32 +00:00
err = chat . UpdateFromMessage ( message , state . Timesource )
2019-12-02 15:34:05 +00:00
if err != nil {
2020-01-10 18:59:01 +00:00
return err
2019-10-14 14:10:48 +00:00
}
2020-01-10 18:59:01 +00:00
2021-04-07 12:57:14 +00:00
if ! chat . Active {
2023-05-08 17:02:54 +00:00
m . createMessageNotification ( chat , state , chat . LastMessage )
2021-04-07 12:57:14 +00:00
}
2020-01-10 18:59:01 +00:00
// Add to response
2021-04-07 12:57:14 +00:00
state . Response . AddChat ( chat )
2020-01-10 18:59:01 +00:00
if message != nil {
2021-06-25 08:30:18 +00:00
message . New = true
2021-06-03 13:11:55 +00:00
state . Response . AddMessage ( message )
2020-01-10 18:59:01 +00:00
}
2021-04-07 12:57:14 +00:00
// Set in the modified maps chat
state . AllChats . Store ( chat . ID , chat )
2020-01-10 18:59:01 +00:00
return nil
}
2023-05-22 12:22:33 +00:00
func ( m * Messenger ) syncContactRequestForInstallationContact ( contact * Contact , state * ReceivedMessageState , chat * Chat , outgoing bool ) error {
2023-07-12 09:46:56 +00:00
2023-05-22 12:22:33 +00:00
if chat == nil {
return fmt . Errorf ( "no chat restored during the contact synchronisation, contact.ID = %s" , contact . ID )
}
contactRequestID , err := m . persistence . LatestPendingContactRequestIDForContact ( contact . ID )
if err != nil {
return err
}
if contactRequestID != "" {
2023-07-12 09:46:56 +00:00
m . logger . Warn ( "syncContactRequestForInstallationContact: skipping as contact request found" , zap . String ( "contactRequestID" , contactRequestID ) )
2023-05-22 12:22:33 +00:00
return nil
}
clock , timestamp := chat . NextClockAndTimestamp ( m . transport )
contactRequest , err := m . generateContactRequest ( clock , timestamp , contact , defaultContactRequestText ( ) , outgoing )
if err != nil {
return err
}
contactRequest . ID = defaultContactRequestID ( contact . ID )
state . Response . AddMessage ( contactRequest )
err = m . persistence . SaveMessages ( [ ] * common . Message { contactRequest } )
if err != nil {
return err
}
if outgoing {
notification := m . generateOutgoingContactRequestNotification ( contact , contactRequest )
2023-10-22 09:41:20 +00:00
err = m . addActivityCenterNotification ( state . Response , notification , nil )
2023-05-22 12:22:33 +00:00
if err != nil {
return err
}
} else {
2023-07-12 15:12:58 +00:00
err = m . createIncomingContactRequestNotification ( contact , state , contactRequest , true )
2023-05-22 12:22:33 +00:00
if err != nil {
return err
}
}
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleSyncInstallationAccount ( state * ReceivedMessageState , message * protobuf . SyncInstallationAccount , statusMessage * v1protocol . StatusMessage ) error {
// Noop
return nil
}
2023-10-12 21:15:05 +00:00
func ( m * Messenger ) handleSyncChats ( messageState * ReceivedMessageState , chats [ ] * protobuf . SyncChat ) error {
for _ , syncChat := range chats {
oldChat , ok := m . allChats . Load ( syncChat . Id )
clock := int64 ( syncChat . Clock )
if ok && oldChat . Timestamp > clock {
// We already know this chat and its timestamp is newer than the syncChat
continue
}
chat := & Chat {
ID : syncChat . Id ,
Name : syncChat . Name ,
Timestamp : clock ,
ReadMessagesAtClockValue : 0 ,
Active : syncChat . Active ,
Muted : syncChat . Muted ,
Joined : clock ,
ChatType : ChatType ( syncChat . ChatType ) ,
Highlight : false ,
}
if chat . PrivateGroupChat ( ) {
chat . MembershipUpdates = make ( [ ] v1protocol . MembershipUpdateEvent , len ( syncChat . MembershipUpdateEvents ) )
for i , membershipUpdate := range syncChat . MembershipUpdateEvents {
chat . MembershipUpdates [ i ] = v1protocol . MembershipUpdateEvent {
ClockValue : membershipUpdate . Clock ,
Type : protobuf . MembershipUpdateEvent_EventType ( membershipUpdate . Type ) ,
Members : membershipUpdate . Members ,
Name : membershipUpdate . Name ,
Signature : membershipUpdate . Signature ,
ChatID : membershipUpdate . ChatId ,
From : membershipUpdate . From ,
RawPayload : membershipUpdate . RawPayload ,
Color : membershipUpdate . Color ,
2023-10-17 13:24:15 +00:00
Image : membershipUpdate . Image ,
2023-10-12 21:15:05 +00:00
}
}
group , err := newProtocolGroupFromChat ( chat )
if err != nil {
return err
}
chat . updateChatFromGroupMembershipChanges ( group )
}
err := m . saveChat ( chat )
if err != nil {
return err
}
messageState . Response . AddChat ( chat )
}
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleSyncInstallationContactV2 ( state * ReceivedMessageState , message * protobuf . SyncInstallationContactV2 , statusMessage * v1protocol . StatusMessage ) error {
2023-05-22 12:22:33 +00:00
// Ignore own contact installation
2023-07-12 09:46:56 +00:00
2023-05-22 12:22:33 +00:00
if message . Id == m . myHexIdentity ( ) {
2023-08-18 11:39:59 +00:00
m . logger . Warn ( "HandleSyncInstallationContactV2: skipping own contact" )
2023-05-22 12:22:33 +00:00
return nil
}
2023-08-04 10:41:24 +00:00
removed := message . Removed && ! message . Blocked
2021-09-03 08:26:05 +00:00
chat , ok := state . AllChats . Load ( message . Id )
2023-08-04 10:41:24 +00:00
if ! ok && ( message . Added || message . HasAddedUs || message . Muted ) && ! removed {
2021-09-03 08:26:05 +00:00
pubKey , err := common . HexToPubkey ( message . Id )
if err != nil {
2021-11-05 15:11:10 +00:00
return err
2021-09-03 08:26:05 +00:00
}
chat = OneToOneFromPublicKey ( pubKey , state . Timesource )
2020-01-10 18:59:01 +00:00
// We don't want to show the chat to the user
chat . Active = false
}
2023-08-04 10:41:24 +00:00
contact , contactFound := state . AllContacts . Load ( message . Id )
if ! contactFound {
if message . Removed && ! message . Blocked {
2021-09-03 08:26:05 +00:00
// Nothing to do in case if contact doesn't exist
return nil
}
2020-12-22 10:49:25 +00:00
var err error
contact , err = buildContactFromPkString ( message . Id )
2020-01-10 18:59:01 +00:00
if err != nil {
return err
}
}
2023-01-20 14:28:30 +00:00
if message . ContactRequestRemoteClock != 0 || message . ContactRequestLocalClock != 0 {
// Some local action about contact requests were performed,
// process them
2023-02-17 10:02:03 +00:00
contact . ProcessSyncContactRequestState (
2023-01-20 14:28:30 +00:00
ContactRequestState ( message . ContactRequestRemoteState ) ,
uint64 ( message . ContactRequestRemoteClock ) ,
ContactRequestState ( message . ContactRequestLocalState ) ,
uint64 ( message . ContactRequestLocalClock ) )
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
2023-05-22 12:22:33 +00:00
err := m . syncContactRequestForInstallationContact ( contact , state , chat , contact . ContactRequestLocalState == ContactRequestStateSent )
if err != nil {
return err
}
2023-01-20 14:28:30 +00:00
} else if message . Added || message . HasAddedUs {
// NOTE(cammellos): this is for handling backward compatibility, old clients
// won't propagate ContactRequestRemoteClock or ContactRequestLocalClock
if message . Added && contact . LastUpdatedLocally < message . LastUpdatedLocally {
contact . ContactRequestSent ( message . LastUpdatedLocally )
2023-05-22 12:22:33 +00:00
err := m . syncContactRequestForInstallationContact ( contact , state , chat , true )
if err != nil {
return err
}
2023-01-20 14:28:30 +00:00
}
if message . HasAddedUs && contact . LastUpdated < message . LastUpdated {
contact . ContactRequestReceived ( message . LastUpdated )
2023-05-22 12:22:33 +00:00
err := m . syncContactRequestForInstallationContact ( contact , state , chat , false )
if err != nil {
return err
}
2023-01-20 14:28:30 +00:00
}
if message . Removed && contact . LastUpdatedLocally < message . LastUpdatedLocally {
err := m . removeContact ( context . Background ( ) , state . Response , contact . ID , false )
if err != nil {
return err
}
}
2021-10-22 14:20:42 +00:00
}
2023-02-14 17:44:00 +00:00
// Sync last updated field
// We don't set `LastUpdated`, since that would cause some issues
// as `LastUpdated` tracks both display name & picture.
// The case where it would break is as follow:
// 1) User A pairs A1 with device A2.
// 2) User B publishes display name and picture with LastUpdated = 3.
// 3) Device A1 receives message from step 2.
// 4) Device A1 syncs with A2 (which has not received message from step 3).
// 5) Device A2 saves Display name and sets LastUpdated = 3,
// note that picture has not been set as it's not synced.
// 6) Device A2 receives the message from 2. because LastUpdated is 3
// it will be discarded, A2 will not have B's picture.
// The correct solution is to either sync profile image (expensive)
// or split the clock for image/display name, so they can be synced
// independently.
2023-08-04 10:41:24 +00:00
if ! contactFound || ( contact . LastUpdated < message . LastUpdated ) {
2023-02-14 17:44:00 +00:00
if message . DisplayName != "" {
contact . DisplayName = message . DisplayName
}
2024-04-03 14:49:57 +00:00
contact . CustomizationColor = multiaccountscommon . IDToColorFallbackToBlue ( message . CustomizationColor )
2023-02-14 17:44:00 +00:00
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
}
2021-10-22 14:20:42 +00:00
if contact . LastUpdatedLocally < message . LastUpdatedLocally {
2023-01-20 14:28:30 +00:00
// NOTE(cammellos): probably is cleaner to pass a flag
// to method to tell them not to sync, or factor out in different
// methods
2021-09-03 08:26:05 +00:00
contact . IsSyncing = true
defer func ( ) {
contact . IsSyncing = false
} ( )
2022-02-17 15:13:10 +00:00
if message . EnsName != "" && contact . EnsName != message . EnsName {
contact . EnsName = message . EnsName
2021-09-03 08:26:05 +00:00
publicKey , err := contact . PublicKey ( )
if err != nil {
2021-11-05 15:11:10 +00:00
return err
2021-09-03 08:26:05 +00:00
}
err = m . ENSVerified ( common . PubkeyToHex ( publicKey ) , message . EnsName )
if err != nil {
contact . ENSVerified = false
}
contact . ENSVerified = true
2020-01-10 18:59:01 +00:00
}
2024-04-03 14:49:57 +00:00
contact . CustomizationColor = multiaccountscommon . IDToColorFallbackToBlue ( message . CustomizationColor )
2021-10-22 14:20:42 +00:00
contact . LastUpdatedLocally = message . LastUpdatedLocally
2020-08-20 14:06:38 +00:00
contact . LocalNickname = message . LocalNickname
2022-07-05 19:49:44 +00:00
contact . TrustStatus = verification . TrustStatus ( message . TrustStatus )
contact . VerificationStatus = VerificationStatus ( message . VerificationStatus )
_ , err := m . verificationDatabase . UpsertTrustStatus ( contact . ID , contact . TrustStatus , message . LastUpdatedLocally )
if err != nil {
return err
}
2020-08-20 14:06:38 +00:00
2021-10-01 14:50:16 +00:00
if message . Blocked != contact . Blocked {
2021-09-03 08:26:05 +00:00
if message . Blocked {
2021-11-05 15:11:10 +00:00
state . AllContacts . Store ( contact . ID , contact )
2023-10-22 09:41:20 +00:00
response , err := m . BlockContact ( context . TODO ( ) , contact . ID , true )
2021-12-06 12:44:40 +00:00
if err != nil {
return err
}
err = state . Response . Merge ( response )
2021-09-03 08:26:05 +00:00
if err != nil {
return err
}
} else {
2023-01-20 14:28:30 +00:00
contact . Unblock ( message . LastUpdatedLocally )
2021-09-03 08:26:05 +00:00
}
}
if chat != nil && message . Muted != chat . Muted {
if message . Muted {
2023-04-16 15:06:00 +00:00
_ , err := m . muteChat ( chat , contact , time . Time { } )
2021-09-03 08:26:05 +00:00
if err != nil {
return err
}
} else {
err := m . unmuteChat ( chat , contact )
if err != nil {
return err
}
}
state . Response . AddChat ( chat )
}
2021-03-29 15:41:30 +00:00
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
2020-01-10 18:59:01 +00:00
}
2021-09-03 08:26:05 +00:00
if chat != nil {
state . AllChats . Store ( chat . ID , chat )
}
2020-01-10 18:59:01 +00:00
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleSyncProfilePictures ( state * ReceivedMessageState , message * protobuf . SyncProfilePictures , statusMessage * v1protocol . StatusMessage ) error {
2022-03-24 09:35:56 +00:00
dbImages , err := m . multiAccounts . GetIdentityImages ( message . KeyUid )
if err != nil {
return err
}
dbImageMap := make ( map [ string ] * images . IdentityImage )
for _ , img := range dbImages {
dbImageMap [ img . Name ] = img
}
2022-06-27 13:48:00 +00:00
idImages := make ( [ ] images . IdentityImage , len ( message . Pictures ) )
2022-03-24 09:35:56 +00:00
i := 0
for _ , message := range message . Pictures {
dbImg := dbImageMap [ message . Name ]
if dbImg != nil && message . Clock <= dbImg . Clock {
continue
}
2022-06-27 13:48:00 +00:00
image := images . IdentityImage {
2022-03-24 09:35:56 +00:00
Name : message . Name ,
Payload : message . Payload ,
Width : int ( message . Width ) ,
Height : int ( message . Height ) ,
FileSize : int ( message . FileSize ) ,
ResizeTarget : int ( message . ResizeTarget ) ,
Clock : message . Clock ,
}
idImages [ i ] = image
i ++
}
if i == 0 {
return nil
}
err = m . multiAccounts . StoreIdentityImages ( message . KeyUid , idImages [ : i ] , false )
if err == nil {
state . Response . IdentityImages = idImages [ : i ]
}
return err
}
2023-10-17 13:24:15 +00:00
func ( m * Messenger ) HandleSyncChat ( state * ReceivedMessageState , message * protobuf . SyncChat , statusMessage * v1protocol . StatusMessage ) error {
2020-01-15 07:25:09 +00:00
chatID := message . Id
2021-10-05 17:26:02 +00:00
existingChat , ok := state . AllChats . Load ( chatID )
if ok && ( existingChat . Active || uint32 ( message . GetClock ( ) / 1000 ) < existingChat . SyncedTo ) {
2021-05-14 10:55:42 +00:00
return nil
2020-01-15 07:25:09 +00:00
}
2021-10-05 17:26:02 +00:00
chat := existingChat
if ! ok {
2023-10-17 13:24:15 +00:00
chats := make ( [ ] * protobuf . SyncChat , 1 )
chats [ 0 ] = message
return m . handleSyncChats ( state , chats )
2021-10-05 17:26:02 +00:00
}
2023-10-17 13:24:15 +00:00
existingChat . Joined = int64 ( message . Clock )
2021-03-29 15:41:30 +00:00
state . AllChats . Store ( chat . ID , chat )
2020-01-15 07:25:09 +00:00
2021-05-14 10:55:42 +00:00
state . Response . AddChat ( chat )
2023-10-17 13:24:15 +00:00
return nil
2020-01-15 07:25:09 +00:00
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleSyncChatRemoved ( state * ReceivedMessageState , message * protobuf . SyncChatRemoved , statusMessage * v1protocol . StatusMessage ) error {
2021-10-05 17:26:02 +00:00
chat , ok := m . allChats . Load ( message . Id )
if ! ok {
return ErrChatNotFound
}
if chat . Joined > int64 ( message . Clock ) {
return nil
}
2022-08-24 11:31:04 +00:00
if chat . DeletedAtClockValue > message . Clock {
return nil
}
2021-10-05 17:26:02 +00:00
if chat . PrivateGroupChat ( ) {
_ , err := m . leaveGroupChat ( context . Background ( ) , state . Response , message . Id , true , false )
if err != nil {
return err
}
}
2022-12-07 19:34:48 +00:00
response , err := m . deactivateChat ( message . Id , message . Clock , false , true )
2021-10-05 17:26:02 +00:00
if err != nil {
return err
}
return state . Response . Merge ( response )
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleSyncChatMessagesRead ( state * ReceivedMessageState , message * protobuf . SyncChatMessagesRead , statusMessage * v1protocol . StatusMessage ) error {
2021-10-12 10:33:32 +00:00
chat , ok := m . allChats . Load ( message . Id )
if ! ok {
return ErrChatNotFound
}
if chat . ReadMessagesAtClockValue > message . Clock {
return nil
}
err := m . markAllRead ( message . Id , message . Clock , false )
if err != nil {
return err
}
state . Response . AddChat ( chat )
return nil
}
2023-09-07 10:33:20 +00:00
func ( m * Messenger ) handlePinMessage ( pinner * Contact , whisperTimestamp uint64 , response * MessengerResponse , message * protobuf . PinMessage , forceSeen bool ) error {
2021-05-14 21:22:50 +00:00
logger := m . logger . With ( zap . String ( "site" , "HandlePinMessage" ) )
logger . Info ( "Handling pin message" )
2023-04-17 14:44:48 +00:00
publicKey , err := pinner . PublicKey ( )
if err != nil {
return err
}
2021-05-14 21:22:50 +00:00
pinMessage := & common . PinMessage {
PinMessage : message ,
// MessageID: message.MessageId,
2023-04-17 14:44:48 +00:00
WhisperTimestamp : whisperTimestamp ,
From : pinner . ID ,
SigPubKey : publicKey ,
Identicon : pinner . Identicon ,
Alias : pinner . Alias ,
2021-05-14 21:22:50 +00:00
}
2024-03-01 17:15:38 +00:00
chat , err := m . matchChatEntity ( pinMessage , protobuf . ApplicationMetadataMessage_PIN_MESSAGE )
2021-05-14 21:22:50 +00:00
if err != nil {
return err // matchChatEntity returns a descriptive error message
}
pinMessage . ID , err = generatePinMessageID ( & m . identity . PublicKey , pinMessage , chat )
if err != nil {
return err
}
// If deleted-at is greater, ignore message
if chat . DeletedAtClockValue >= pinMessage . Clock {
return nil
}
2023-04-17 14:44:48 +00:00
if c , ok := m . allChats . Load ( chat . ID ) ; ok {
2021-05-14 21:22:50 +00:00
chat = c
}
// Set the LocalChatID for the message
pinMessage . LocalChatID = chat . ID
2023-04-17 14:44:48 +00:00
inserted , err := m . persistence . SavePinMessage ( pinMessage )
if err != nil {
return err
}
// Nothing to do, returning
if ! inserted {
m . logger . Info ( "pin message already processed" )
return nil
}
if message . Pinned {
id , err := generatePinMessageNotificationID ( & m . identity . PublicKey , pinMessage , chat )
if err != nil {
return err
}
2023-09-07 10:33:20 +00:00
systemMessage := & common . Message {
2023-08-18 11:39:59 +00:00
ChatMessage : & protobuf . ChatMessage {
2023-04-17 14:44:48 +00:00
Clock : message . Clock ,
Timestamp : whisperTimestamp ,
ChatId : chat . ID ,
MessageType : message . MessageType ,
ResponseTo : message . MessageId ,
ContentType : protobuf . ChatMessage_SYSTEM_MESSAGE_PINNED_MESSAGE ,
} ,
WhisperTimestamp : whisperTimestamp ,
ID : id ,
LocalChatID : chat . ID ,
From : pinner . ID ,
}
2023-09-07 10:33:20 +00:00
if forceSeen {
systemMessage . Seen = true
}
response . AddMessage ( systemMessage )
2023-05-05 15:26:19 +00:00
chat . UnviewedMessagesCount ++
2023-04-17 14:44:48 +00:00
}
2021-05-14 21:22:50 +00:00
if chat . LastClockValue < message . Clock {
chat . LastClockValue = message . Clock
}
2023-04-17 14:44:48 +00:00
response . AddPinMessage ( pinMessage )
2021-05-14 21:22:50 +00:00
// Set in the modified maps chat
2023-04-17 14:44:48 +00:00
response . AddChat ( chat )
m . allChats . Store ( chat . ID , chat )
2021-05-14 21:22:50 +00:00
return nil
}
2023-09-07 10:33:20 +00:00
func ( m * Messenger ) HandlePinMessage ( state * ReceivedMessageState , message * protobuf . PinMessage , statusMessage * v1protocol . StatusMessage , fromArchive bool ) error {
return m . handlePinMessage ( state . CurrentMessageState . Contact , state . CurrentMessageState . WhisperTimestamp , state . Response , message , fromArchive )
2023-04-17 14:44:48 +00:00
}
2023-01-20 14:28:30 +00:00
func ( m * Messenger ) handleAcceptContactRequest (
response * MessengerResponse ,
contact * Contact ,
originalRequest * common . Message ,
2023-02-21 18:08:11 +00:00
clock uint64 ) ( ContactRequestProcessingResponse , error ) {
2022-01-18 16:31:34 +00:00
2023-02-21 18:08:11 +00:00
m . logger . Debug ( "received contact request" , zap . Uint64 ( "clock-sent" , clock ) , zap . Uint64 ( "current-clock" , contact . ContactRequestRemoteClock ) , zap . Uint64 ( "current-state" , uint64 ( contact . ContactRequestRemoteState ) ) )
if contact . ContactRequestRemoteClock > clock {
2023-02-08 14:58:10 +00:00
m . logger . Debug ( "not handling accept since clock lower" )
2023-01-20 14:28:30 +00:00
return ContactRequestProcessingResponse { } , nil
2022-01-18 16:31:34 +00:00
}
2023-01-20 14:28:30 +00:00
// The contact request accepted wasn't found, a reason for this might
// be that we sent a legacy contact request/contact-update, or another
// device has sent it, and we haven't synchronized it
if originalRequest == nil {
2023-02-21 18:08:11 +00:00
return contact . ContactRequestAccepted ( clock ) , nil
2022-01-18 16:31:34 +00:00
}
2023-01-20 14:28:30 +00:00
if originalRequest . LocalChatID != contact . ID {
return ContactRequestProcessingResponse { } , errors . New ( "can't accept contact request not sent to user" )
2022-01-18 16:31:34 +00:00
}
2023-02-21 18:08:11 +00:00
contact . ContactRequestAccepted ( clock )
2022-01-18 16:31:34 +00:00
2023-01-20 14:28:30 +00:00
originalRequest . ContactRequestState = common . ContactRequestStateAccepted
2022-01-18 16:31:34 +00:00
2023-01-20 14:28:30 +00:00
err := m . persistence . SetContactRequestState ( originalRequest . ID , originalRequest . ContactRequestState )
2022-01-18 16:31:34 +00:00
if err != nil {
2023-01-20 14:28:30 +00:00
return ContactRequestProcessingResponse { } , err
}
response . AddMessage ( originalRequest )
return ContactRequestProcessingResponse { } , nil
}
2023-02-21 18:08:11 +00:00
func ( m * Messenger ) handleAcceptContactRequestMessage ( state * ReceivedMessageState , clock uint64 , contactRequestID string , isOutgoing bool ) error {
request , err := m . persistence . MessageByID ( contactRequestID )
2023-01-20 14:28:30 +00:00
if err != nil && err != common . ErrRecordNotFound {
2022-01-18 16:31:34 +00:00
return err
}
2023-12-21 17:38:14 +00:00
// We still want to handle acceptance of the CR even it was already accepted
previouslyAccepted := request != nil && request . ContactRequestState == common . ContactRequestStateAccepted
2023-01-20 14:28:30 +00:00
contact := state . CurrentMessageState . Contact
2023-12-21 17:38:14 +00:00
// The request message will be added to the response here
2023-02-21 18:08:11 +00:00
processingResponse , err := m . handleAcceptContactRequest ( state . Response , contact , request , clock )
2022-01-18 16:31:34 +00:00
if err != nil {
2023-01-20 14:28:30 +00:00
return err
2022-01-18 16:31:34 +00:00
}
2023-01-20 14:28:30 +00:00
// If the state has changed from non-mutual contact, to mutual contact
// we want to notify the user
2023-04-25 11:27:15 +00:00
if contact . mutual ( ) {
2023-01-20 14:28:30 +00:00
// We set the chat as active, this is currently the expected behavior
// for mobile, it might change as we implement further the activity
// center
chat , _ , err := m . getOneToOneAndNextClock ( contact )
if err != nil {
return err
}
2022-01-18 16:31:34 +00:00
2023-02-21 18:08:11 +00:00
if chat . LastClockValue < clock {
chat . LastClockValue = clock
2023-01-20 14:28:30 +00:00
}
// NOTE(cammellos): This will re-enable the chat if it was deleted, and only
// after we became contact, currently seems safe, but that needs
// discussing with UX.
2023-02-21 18:08:11 +00:00
if chat . DeletedAtClockValue < clock {
2023-01-20 14:28:30 +00:00
chat . Active = true
}
2023-07-12 15:12:58 +00:00
// Add mutual state update message for incoming contact request
2023-12-21 17:38:14 +00:00
if ! previouslyAccepted {
clock , timestamp := chat . NextClockAndTimestamp ( m . transport )
2023-07-12 15:12:58 +00:00
2023-12-21 17:38:14 +00:00
updateMessage , err := m . prepareMutualStateUpdateMessage ( contact . ID , MutualStateUpdateTypeAdded , clock , timestamp , false )
if err != nil {
return err
}
2023-07-12 15:12:58 +00:00
2024-01-08 18:18:57 +00:00
err = m . prepareMessage ( updateMessage , m . httpServer )
if err != nil {
return err
}
2023-12-21 17:38:14 +00:00
err = m . persistence . SaveMessages ( [ ] * common . Message { updateMessage } )
if err != nil {
return err
}
state . Response . AddMessage ( updateMessage )
2023-07-12 15:12:58 +00:00
2023-12-21 17:38:14 +00:00
err = chat . UpdateFromMessage ( updateMessage , m . getTimesource ( ) )
if err != nil {
return err
}
chat . UnviewedMessagesCount ++
2024-02-09 09:36:54 +00:00
// Dispatch profile message to add a contact to the encrypted profile part
err = m . DispatchProfileShowcase ( )
if err != nil {
return err
}
2023-08-11 17:08:54 +00:00
}
2023-12-21 17:38:14 +00:00
2023-01-20 14:28:30 +00:00
state . Response . AddChat ( chat )
state . AllChats . Store ( chat . ID , chat )
}
2023-02-21 18:08:11 +00:00
if request != nil {
if isOutgoing {
notification := m . generateOutgoingContactRequestNotification ( contact , request )
2023-10-22 09:41:20 +00:00
err = m . addActivityCenterNotification ( state . Response , notification , nil )
2023-02-21 18:08:11 +00:00
if err != nil {
return err
}
} else {
2023-07-12 15:12:58 +00:00
err = m . createIncomingContactRequestNotification ( contact , state , request , processingResponse . newContactRequestReceived )
2023-06-08 12:00:19 +00:00
if err != nil {
return err
}
2023-10-22 09:41:20 +00:00
// With devices 1 and 2 paired, and userA logged in on both, while userB is on device 3:
// When userA on device 1 sends a contact request to userB, userB accepts it on device 3.
// The confirmation is sent to devices 1 and 2.
// However, the contactRequestID in `AcceptContactRequestMessage` uses keccak256(...) instead of defaultContactRequestID(contact.ID).
// Device 1 processes this, but device 2 doesn't due to an error `ErrRecordNotFound` from `m.persistence.MessageByID(contactRequestID)`.
// The correct notification ID on device 2 should be defaultContactRequestID(contact.ID).
// Thus, we must sync the accepted decision to device 2.
err = m . syncActivityCenterAcceptedByIDs ( context . TODO ( ) , [ ] types . HexBytes { types . FromHex ( defaultContactRequestID ( contact . ID ) ) } , m . GetCurrentTimeInMillis ( ) )
if err != nil {
m . logger . Warn ( "could not sync activity center notification as accepted" , zap . Error ( err ) )
}
2023-01-20 14:28:30 +00:00
}
}
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
2022-01-18 16:31:34 +00:00
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleAcceptContactRequest ( state * ReceivedMessageState , message * protobuf . AcceptContactRequest , statusMessage * v1protocol . StatusMessage ) error {
2023-04-25 11:27:15 +00:00
err := m . handleAcceptContactRequestMessage ( state , message . Clock , message . Id , false )
2023-02-21 18:08:11 +00:00
if err != nil {
m . logger . Warn ( "could not accept contact request" , zap . Error ( err ) )
}
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) handleRetractContactRequest ( state * ReceivedMessageState , contact * Contact , message * protobuf . RetractContactRequest ) error {
2023-01-20 14:28:30 +00:00
if contact . ID == m . myHexIdentity ( ) {
m . logger . Debug ( "retraction coming from us, ignoring" )
2022-01-18 16:31:34 +00:00
return nil
}
2023-02-02 18:12:20 +00:00
m . logger . Debug ( "handling retracted contact request" , zap . Uint64 ( "clock" , message . Clock ) )
2023-02-17 10:02:03 +00:00
r := contact . ContactRequestRetracted ( message . Clock , false )
2023-01-20 14:28:30 +00:00
if ! r . processed {
2023-02-08 14:58:10 +00:00
m . logger . Debug ( "not handling retract since clock lower" )
2023-01-20 14:28:30 +00:00
return nil
2022-06-08 16:18:56 +00:00
}
2022-06-13 10:57:51 +00:00
2023-06-08 12:00:19 +00:00
// System message for mutual state update
chat , clock , err := m . getOneToOneAndNextClock ( contact )
if err != nil {
return err
}
2023-08-11 17:08:54 +00:00
2023-06-08 12:00:19 +00:00
timestamp := m . getTimesource ( ) . GetCurrentTime ( )
updateMessage , err := m . prepareMutualStateUpdateMessage ( contact . ID , MutualStateUpdateTypeRemoved , clock , timestamp , false )
2022-06-13 10:57:51 +00:00
if err != nil {
return err
2022-06-08 16:18:56 +00:00
}
2022-06-13 10:57:51 +00:00
2024-02-09 09:36:54 +00:00
// Dispatch profile message to remove a contact from the encrypted profile part
err = m . DispatchProfileShowcase ( )
if err != nil {
return err
}
2024-01-08 18:18:57 +00:00
err = m . prepareMessage ( updateMessage , m . httpServer )
if err != nil {
return err
}
2023-06-08 12:00:19 +00:00
err = m . persistence . SaveMessages ( [ ] * common . Message { updateMessage } )
if err != nil {
return err
}
2023-08-11 17:08:54 +00:00
state . Response . AddMessage ( updateMessage )
err = chat . UpdateFromMessage ( updateMessage , m . getTimesource ( ) )
if err != nil {
return err
}
chat . UnviewedMessagesCount ++
state . Response . AddChat ( chat )
2023-06-08 12:00:19 +00:00
notification := & ActivityCenterNotification {
ID : types . FromHex ( uuid . New ( ) . String ( ) ) ,
Type : ActivityCenterNotificationTypeContactRemoved ,
Name : contact . PrimaryName ( ) ,
Author : contact . ID ,
Timestamp : m . getTimesource ( ) . GetCurrentTime ( ) ,
ChatID : contact . ID ,
Read : false ,
2023-10-22 09:41:20 +00:00
UpdatedAt : m . GetCurrentTimeInMillis ( ) ,
2023-06-08 12:00:19 +00:00
}
2023-10-22 09:41:20 +00:00
err = m . addActivityCenterNotification ( state . Response , notification , nil )
2023-06-08 12:00:19 +00:00
if err != nil {
m . logger . Warn ( "failed to create activity center notification" , zap . Error ( err ) )
return err
}
2023-02-02 18:12:20 +00:00
m . allContacts . Store ( contact . ID , contact )
2022-01-18 16:31:34 +00:00
2023-02-02 18:12:20 +00:00
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleRetractContactRequest ( state * ReceivedMessageState , message * protobuf . RetractContactRequest , statusMessage * v1protocol . StatusMessage ) error {
2023-02-02 18:12:20 +00:00
contact := state . CurrentMessageState . Contact
2023-08-11 17:08:54 +00:00
err := m . handleRetractContactRequest ( state , contact , message )
2023-02-02 18:12:20 +00:00
if err != nil {
return err
}
2023-02-08 14:58:10 +00:00
if contact . ID != m . myHexIdentity ( ) {
state . ModifiedContacts . Store ( contact . ID , true )
}
2022-01-18 16:31:34 +00:00
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleContactUpdate ( state * ReceivedMessageState , message * protobuf . ContactUpdate , statusMessage * v1protocol . StatusMessage ) error {
2020-01-10 18:59:01 +00:00
logger := m . logger . With ( zap . String ( "site" , "HandleContactUpdate" ) )
2023-08-18 11:39:59 +00:00
if common . IsPubKeyEqual ( state . CurrentMessageState . PublicKey , & m . identity . PublicKey ) {
logger . Warn ( "coming from us, ignoring" )
return nil
}
2020-01-10 18:59:01 +00:00
contact := state . CurrentMessageState . Contact
2021-03-29 15:41:30 +00:00
chat , ok := state . AllChats . Load ( contact . ID )
2021-04-07 12:57:14 +00:00
2021-06-08 06:07:45 +00:00
allowed , err := m . isMessageAllowedFrom ( state . CurrentMessageState . Contact . ID , chat )
2021-04-07 12:57:14 +00:00
if err != nil {
return err
}
if ! allowed {
return ErrMessageNotAllowed
}
2024-03-28 15:57:59 +00:00
if err = utils . ValidateDisplayName ( & message . DisplayName ) ; err != nil {
2022-02-17 15:13:10 +00:00
return err
}
2020-01-10 18:59:01 +00:00
if ! ok {
2020-01-20 16:44:32 +00:00
chat = OneToOneFromPublicKey ( state . CurrentMessageState . PublicKey , state . Timesource )
2020-01-10 18:59:01 +00:00
// We don't want to show the chat to the user
chat . Active = false
}
2023-02-08 14:58:10 +00:00
logger . Debug ( "Handling contact update" )
2020-01-10 18:59:01 +00:00
2023-02-02 18:12:20 +00:00
if message . ContactRequestPropagatedState != nil {
2023-02-08 14:58:10 +00:00
logger . Debug ( "handling contact request propagated state" , zap . Any ( "state before update" , contact . ContactRequestPropagatedState ( ) ) )
2023-02-02 18:12:20 +00:00
result := contact . ContactRequestPropagatedStateReceived ( message . ContactRequestPropagatedState )
if result . sendBackState {
2023-02-08 14:58:10 +00:00
logger . Debug ( "sending back state" )
2023-02-02 18:12:20 +00:00
// This is a bit dangerous, since it might trigger a ping-pong of contact updates
// also it should backoff/debounce
2024-04-03 14:49:57 +00:00
_ , err = m . sendContactUpdate ( context . Background ( ) , contact . ID , contact . DisplayName , contact . EnsName , "" , contact . CustomizationColor , m . dispatchMessage )
2023-02-02 18:12:20 +00:00
if err != nil {
return err
}
}
if result . newContactRequestReceived {
2023-04-25 11:27:15 +00:00
contactRequest , err := m . createContactRequestForContactUpdate ( contact , state )
2023-02-02 18:12:20 +00:00
if err != nil {
return err
}
2023-07-12 15:12:58 +00:00
err = m . createIncomingContactRequestNotification ( contact , state , contactRequest , true )
2023-04-25 11:27:15 +00:00
if err != nil {
return err
}
2023-02-02 18:12:20 +00:00
}
2023-04-25 11:27:15 +00:00
2023-02-08 14:58:10 +00:00
logger . Debug ( "handled propagated state" , zap . Any ( "state after update" , contact . ContactRequestPropagatedState ( ) ) )
2023-02-02 18:12:20 +00:00
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
}
2020-01-10 18:59:01 +00:00
if contact . LastUpdated < message . Clock {
2022-02-17 15:13:10 +00:00
if contact . EnsName != message . EnsName {
contact . EnsName = message . EnsName
2020-01-10 18:59:01 +00:00
contact . ENSVerified = false
}
2022-02-17 15:13:10 +00:00
if len ( message . DisplayName ) != 0 {
contact . DisplayName = message . DisplayName
}
2024-04-03 14:49:57 +00:00
contact . CustomizationColor = multiaccountscommon . IDToColorFallbackToBlue ( message . CustomizationColor )
2023-01-20 14:28:30 +00:00
r := contact . ContactRequestReceived ( message . ContactRequestClock )
if r . newContactRequestReceived {
2023-07-12 15:12:58 +00:00
err = m . createIncomingContactRequestNotification ( contact , state , nil , true )
2022-01-18 16:31:34 +00:00
if err != nil {
2023-06-08 12:00:19 +00:00
return err
2022-01-18 16:31:34 +00:00
}
2022-06-13 10:57:51 +00:00
}
2023-01-20 14:28:30 +00:00
contact . LastUpdated = message . Clock
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
2020-01-10 18:59:01 +00:00
}
if chat . LastClockValue < message . Clock {
chat . LastClockValue = message . Clock
}
2023-01-20 14:28:30 +00:00
if contact . mutual ( ) && chat . DeletedAtClockValue < message . Clock {
2022-08-26 16:50:41 +00:00
chat . Active = true
}
2021-01-11 10:32:51 +00:00
state . Response . AddChat ( chat )
2021-03-29 15:41:30 +00:00
// TODO(samyoul) remove storing of an updated reference pointer?
state . AllChats . Store ( chat . ID , chat )
2020-01-10 18:59:01 +00:00
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleSyncPairInstallation ( state * ReceivedMessageState , message * protobuf . SyncPairInstallation , statusMessage * v1protocol . StatusMessage ) error {
2020-01-10 18:59:01 +00:00
logger := m . logger . With ( zap . String ( "site" , "HandlePairInstallation" ) )
2023-08-18 11:39:59 +00:00
if err := ValidateReceivedPairInstallation ( message , state . CurrentMessageState . WhisperTimestamp ) ; err != nil {
2020-01-10 18:59:01 +00:00
logger . Warn ( "failed to validate message" , zap . Error ( err ) )
return err
}
2021-03-29 15:41:30 +00:00
installation , ok := state . AllInstallations . Load ( message . InstallationId )
2020-01-10 18:59:01 +00:00
if ! ok {
return errors . New ( "installation not found" )
}
metadata := & multidevice . InstallationMetadata {
Name : message . Name ,
DeviceType : message . DeviceType ,
}
installation . InstallationMetadata = metadata
2021-03-29 15:41:30 +00:00
// TODO(samyoul) remove storing of an updated reference pointer?
state . AllInstallations . Store ( message . InstallationId , installation )
state . ModifiedInstallations . Store ( message . InstallationId , true )
2020-01-10 18:59:01 +00:00
return nil
}
2022-04-22 07:42:22 +00:00
func ( m * Messenger ) HandleHistoryArchiveMagnetlinkMessage ( state * ReceivedMessageState , communityPubKey * ecdsa . PublicKey , magnetlink string , clock uint64 ) error {
id := types . HexBytes ( crypto . CompressPubkey ( communityPubKey ) )
2023-07-06 17:44:31 +00:00
community , err := m . communitiesManager . GetByID ( id )
2024-01-09 21:47:37 +00:00
if err != nil && err != communities . ErrOrgNotFound {
2023-07-06 17:44:31 +00:00
m . logger . Debug ( "Couldn't get community for community with id: " , zap . Any ( "id" , id ) )
return err
}
if community == nil {
return nil
}
2022-04-22 07:42:22 +00:00
settings , err := m . communitiesManager . GetCommunitySettingsByID ( id )
if err != nil {
m . logger . Debug ( "Couldn't get community settings for community with id: " , zap . Any ( "id" , id ) )
return err
}
2023-07-06 17:44:31 +00:00
if settings == nil {
return nil
}
2022-04-22 07:42:22 +00:00
2024-06-06 14:59:46 +00:00
if m . archiveManager . IsReady ( ) && settings . HistoryArchiveSupportEnabled {
2022-04-22 07:42:22 +00:00
lastClock , err := m . communitiesManager . GetMagnetlinkMessageClock ( id )
if err != nil {
return err
}
2022-12-19 08:34:37 +00:00
lastSeenMagnetlink , err := m . communitiesManager . GetLastSeenMagnetlink ( id )
if err != nil {
return err
}
2022-04-22 07:42:22 +00:00
// We are only interested in a community archive magnet link
// if it originates from a community that the current account is
// part of and doesn't own the private key at the same time
2023-07-06 17:44:31 +00:00
if ! community . IsControlNode ( ) && community . Joined ( ) && clock >= lastClock {
2022-12-19 08:34:37 +00:00
if lastSeenMagnetlink == magnetlink {
2024-06-06 13:59:27 +00:00
m . logger . Debug ( "already processed this magnetlink" )
2022-12-19 08:34:37 +00:00
return nil
}
2022-04-22 07:42:22 +00:00
2024-06-06 14:59:46 +00:00
m . archiveManager . UnseedHistoryArchiveTorrent ( id )
currentTask := m . archiveManager . GetHistoryArchiveDownloadTask ( id . String ( ) )
2022-12-12 09:22:37 +00:00
2022-12-19 08:34:37 +00:00
go func ( currentTask * communities . HistoryArchiveDownloadTask , communityID types . HexBytes ) {
2022-12-12 09:22:37 +00:00
// Cancel ongoing download/import task
2023-01-23 14:32:35 +00:00
if currentTask != nil && ! currentTask . IsCancelled ( ) {
currentTask . Cancel ( )
2022-12-12 09:22:37 +00:00
currentTask . Waiter . Wait ( )
}
// Create new task
task := & communities . HistoryArchiveDownloadTask {
2023-01-23 14:32:35 +00:00
CancelChan : make ( chan struct { } ) ,
Waiter : * new ( sync . WaitGroup ) ,
Cancelled : false ,
2022-12-12 09:22:37 +00:00
}
2024-06-06 14:59:46 +00:00
m . archiveManager . AddHistoryArchiveDownloadTask ( communityID . String ( ) , task )
2022-12-12 09:22:37 +00:00
// this wait groups tracks the ongoing task for a particular community
task . Waiter . Add ( 1 )
2023-01-23 14:32:35 +00:00
defer task . Waiter . Done ( )
2022-12-12 09:22:37 +00:00
// this wait groups tracks all ongoing tasks across communities
2023-11-25 23:24:20 +00:00
m . shutdownWaitGroup . Add ( 1 )
defer m . shutdownWaitGroup . Done ( )
2023-01-23 14:32:35 +00:00
m . downloadAndImportHistoryArchives ( communityID , magnetlink , task . CancelChan )
2022-12-19 08:34:37 +00:00
} ( currentTask , id )
2022-12-12 09:22:37 +00:00
2022-12-09 14:26:12 +00:00
return m . communitiesManager . UpdateMagnetlinkMessageClock ( id , clock )
}
}
return nil
}
func ( m * Messenger ) downloadAndImportHistoryArchives ( id types . HexBytes , magnetlink string , cancel chan struct { } ) {
2024-06-06 14:59:46 +00:00
downloadTaskInfo , err := m . archiveManager . DownloadHistoryArchivesByMagnetlink ( id , magnetlink , cancel )
2022-12-09 14:26:12 +00:00
if err != nil {
logMsg := "failed to download history archive data"
if err == communities . ErrTorrentTimedout {
2024-06-06 13:59:27 +00:00
m . logger . Debug ( "torrent has timed out, trying once more..." )
2024-06-06 14:59:46 +00:00
downloadTaskInfo , err = m . archiveManager . DownloadHistoryArchivesByMagnetlink ( id , magnetlink , cancel )
2022-12-09 14:26:12 +00:00
if err != nil {
2024-06-06 13:59:27 +00:00
m . logger . Error ( logMsg , zap . Error ( err ) )
2022-12-09 14:26:12 +00:00
return
}
} else {
2024-06-06 13:59:27 +00:00
m . logger . Debug ( logMsg , zap . Error ( err ) )
2022-12-09 14:26:12 +00:00
return
}
}
if downloadTaskInfo . Cancelled {
2022-12-19 08:34:37 +00:00
if downloadTaskInfo . TotalDownloadedArchivesCount > 0 {
2024-06-06 13:59:27 +00:00
m . logger . Debug ( fmt . Sprintf ( "downloaded %d of %d archives so far" , downloadTaskInfo . TotalDownloadedArchivesCount , downloadTaskInfo . TotalArchivesCount ) )
2022-12-19 08:34:37 +00:00
}
2022-12-09 14:26:12 +00:00
return
}
2023-01-18 12:16:07 +00:00
err = m . communitiesManager . UpdateLastSeenMagnetlink ( id , magnetlink )
if err != nil {
2024-06-06 13:59:27 +00:00
m . logger . Error ( "couldn't update last seen magnetlink" , zap . Error ( err ) )
2023-01-18 12:16:07 +00:00
}
2023-09-15 07:42:28 +00:00
err = m . checkIfIMemberOfCommunity ( id )
if err != nil {
return
}
2023-01-19 13:23:48 +00:00
err = m . importHistoryArchives ( id , cancel )
if err != nil {
2024-06-06 13:59:27 +00:00
m . logger . Error ( "failed to import history archives" , zap . Error ( err ) )
2023-01-19 13:23:48 +00:00
m . config . messengerSignalsHandler . DownloadingHistoryArchivesFinished ( types . EncodeHex ( id ) )
return
2022-04-22 07:42:22 +00:00
}
2023-01-19 13:23:48 +00:00
2022-12-09 14:26:12 +00:00
m . config . messengerSignalsHandler . DownloadingHistoryArchivesFinished ( types . EncodeHex ( id ) )
2022-04-22 07:42:22 +00:00
}
2023-07-07 08:39:52 +00:00
func ( m * Messenger ) handleArchiveMessages ( archiveMessages [ ] * protobuf . WakuMessage ) ( * MessengerResponse , error ) {
2023-01-13 15:40:39 +00:00
messagesToHandle := make ( map [ transport . Filter ] [ ] * types . Message )
for _ , message := range archiveMessages {
filter := m . transport . FilterByTopic ( message . Topic )
if filter != nil {
shhMessage := & types . Message {
Sig : message . Sig ,
Timestamp : uint32 ( message . Timestamp ) ,
Topic : types . BytesToTopic ( message . Topic ) ,
Payload : message . Payload ,
Padding : message . Padding ,
Hash : message . Hash ,
ThirdPartyID : message . ThirdPartyId ,
}
messagesToHandle [ * filter ] = append ( messagesToHandle [ * filter ] , shhMessage )
}
}
importedMessages := make ( map [ transport . Filter ] [ ] * types . Message , 0 )
otherMessages := make ( map [ transport . Filter ] [ ] * types . Message , 0 )
for filter , messages := range messagesToHandle {
for _ , message := range messages {
if message . ThirdPartyID != "" {
importedMessages [ filter ] = append ( importedMessages [ filter ] , message )
} else {
otherMessages [ filter ] = append ( otherMessages [ filter ] , message )
}
}
}
err := m . handleImportedMessages ( importedMessages )
if err != nil {
2024-06-06 13:59:27 +00:00
m . logger . Error ( "failed to handle imported messages" , zap . Error ( err ) )
2023-01-13 15:40:39 +00:00
return nil , err
}
2023-09-07 10:33:20 +00:00
response , err := m . handleRetrievedMessages ( otherMessages , false , true )
2023-01-13 15:40:39 +00:00
if err != nil {
2024-06-06 13:59:27 +00:00
m . logger . Error ( "failed to write history archive messages to database" , zap . Error ( err ) )
2023-01-13 15:40:39 +00:00
return nil , err
}
return response , nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleCommunityCancelRequestToJoin ( state * ReceivedMessageState , cancelRequestToJoinProto * protobuf . CommunityCancelRequestToJoin , statusMessage * v1protocol . StatusMessage ) error {
signer := state . CurrentMessageState . PublicKey
2022-10-28 08:41:20 +00:00
if cancelRequestToJoinProto . CommunityId == nil {
2023-07-10 14:11:37 +00:00
return ErrInvalidCommunityID
2022-10-28 08:41:20 +00:00
}
2023-08-18 11:39:59 +00:00
requestToJoin , err := m . communitiesManager . HandleCommunityCancelRequestToJoin ( signer , cancelRequestToJoinProto )
2022-10-28 08:41:20 +00:00
if err != nil {
return err
}
2024-02-08 09:24:12 +00:00
state . Response . AddRequestToJoinCommunity ( requestToJoin )
2023-04-06 14:06:24 +00:00
// delete activity center notification
notification , err := m . persistence . GetActivityCenterNotificationByID ( requestToJoin . ID )
if err != nil {
return err
}
if notification != nil {
2023-10-22 09:41:20 +00:00
updatedAt := m . GetCurrentTimeInMillis ( )
notification . UpdatedAt = updatedAt
// we shouldn't sync deleted notification here,
// as the same user on different devices will receive the same message(CommunityCancelRequestToJoin) ?
err = m . persistence . DeleteActivityCenterNotificationByID ( types . FromHex ( requestToJoin . ID . String ( ) ) , updatedAt )
2023-04-06 14:06:24 +00:00
if err != nil {
m . logger . Error ( "failed to delete notification from Activity Center" , zap . Error ( err ) )
return err
}
// sending signal to client to remove the activity center notification from UI
response := & MessengerResponse { }
response . AddActivityCenterNotification ( notification )
signal . SendNewMessages ( response )
}
2022-10-28 08:41:20 +00:00
return nil
}
2021-01-11 10:32:51 +00:00
// HandleCommunityRequestToJoin handles an community request to join
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleCommunityRequestToJoin ( state * ReceivedMessageState , requestToJoinProto * protobuf . CommunityRequestToJoin , statusMessage * v1protocol . StatusMessage ) error {
2023-09-20 08:37:46 +00:00
signer := state . CurrentMessageState . PublicKey
2023-11-08 18:05:33 +00:00
community , requestToJoin , err := m . communitiesManager . HandleCommunityRequestToJoin ( signer , statusMessage . TransportLayer . Dst , requestToJoinProto )
2021-01-11 10:32:51 +00:00
if err != nil {
return err
}
2023-10-12 21:42:03 +00:00
switch requestToJoin . State {
case communities . RequestToJoinStatePending :
contact , _ := state . AllContacts . Load ( contactIDFromPublicKey ( signer ) )
2024-04-03 14:49:57 +00:00
contact . CustomizationColor = multiaccountscommon . IDToColorFallbackToBlue ( requestToJoinProto . CustomizationColor )
2023-10-12 21:42:03 +00:00
if len ( requestToJoinProto . DisplayName ) != 0 {
contact . DisplayName = requestToJoinProto . DisplayName
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
state . ModifiedContacts . Store ( contact . ID , true )
2022-07-01 13:54:02 +00:00
}
2021-03-31 16:23:45 +00:00
2024-02-08 09:24:12 +00:00
state . Response . AddRequestToJoinCommunity ( requestToJoin )
2022-07-01 13:54:02 +00:00
state . Response . AddNotification ( NewCommunityRequestToJoinNotification ( requestToJoin . ID . String ( ) , community , contact ) )
2022-10-25 22:06:20 +00:00
// Activity Center notification, new for pending state
notification := & ActivityCenterNotification {
ID : types . FromHex ( requestToJoin . ID . String ( ) ) ,
Type : ActivityCenterNotificationTypeCommunityMembershipRequest ,
Timestamp : m . getTimesource ( ) . GetCurrentTime ( ) ,
Author : contact . ID ,
CommunityID : community . IDString ( ) ,
MembershipStatus : ActivityCenterMembershipStatusPending ,
2023-04-21 09:18:47 +00:00
Deleted : false ,
2023-10-22 09:41:20 +00:00
UpdatedAt : m . GetCurrentTimeInMillis ( ) ,
2022-10-25 22:06:20 +00:00
}
2023-10-22 09:41:20 +00:00
err = m . addActivityCenterNotification ( state . Response , notification , nil )
2023-01-28 09:52:53 +00:00
if err != nil {
m . logger . Error ( "failed to save notification" , zap . Error ( err ) )
return err
2022-10-25 22:06:20 +00:00
}
2023-10-12 21:42:03 +00:00
case communities . RequestToJoinStateDeclined :
response , err := m . declineRequestToJoinCommunity ( requestToJoin )
if err == nil {
err := state . Response . Merge ( response )
if err != nil {
return err
}
} else {
2022-10-25 22:06:20 +00:00
return err
}
2023-10-12 21:42:03 +00:00
case communities . RequestToJoinStateAccepted :
response , err := m . acceptRequestToJoinCommunity ( requestToJoin )
if err == nil {
err := state . Response . Merge ( response ) // new member has been added
2023-01-28 09:52:53 +00:00
if err != nil {
return err
2022-10-25 22:06:20 +00:00
}
2023-10-12 21:42:03 +00:00
} else if err == communities . ErrNoPermissionToJoin {
// only control node will end up here as it's the only one that
// performed token permission checks
response , err = m . declineRequestToJoinCommunity ( requestToJoin )
if err == nil {
err := state . Response . Merge ( response )
if err != nil {
return err
}
} else {
return err
}
} else {
return err
2022-10-25 22:06:20 +00:00
}
2023-10-12 21:42:03 +00:00
case communities . RequestToJoinStateCanceled :
// cancellation is handled by separate message
fallthrough
2023-10-31 14:20:40 +00:00
case communities . RequestToJoinStateAwaitingAddresses :
// ownership changed request is handled only if owner kicked members and saved
// temporary RequestToJoinStateAwaitingAddresses request
fallthrough
2023-10-12 21:42:03 +00:00
case communities . RequestToJoinStateAcceptedPending , communities . RequestToJoinStateDeclinedPending :
// request can be marked as pending only manually
return errors . New ( "invalid request state" )
2022-07-01 13:54:02 +00:00
}
return nil
}
2023-07-10 14:11:37 +00:00
// HandleCommunityEditSharedAddresses handles an edit a user has made to their shared addresses
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleCommunityEditSharedAddresses ( state * ReceivedMessageState , editRevealedAddressesProto * protobuf . CommunityEditSharedAddresses , statusMessage * v1protocol . StatusMessage ) error {
signer := state . CurrentMessageState . PublicKey
2023-07-10 14:11:37 +00:00
if editRevealedAddressesProto . CommunityId == nil {
return ErrInvalidCommunityID
}
2023-08-18 11:39:59 +00:00
err := m . communitiesManager . HandleCommunityEditSharedAddresses ( signer , editRevealedAddressesProto )
2023-07-10 14:11:37 +00:00
if err != nil {
return err
}
community , err := m . communitiesManager . GetByIDString ( string ( editRevealedAddressesProto . GetCommunityId ( ) ) )
if err != nil {
return err
}
state . Response . AddCommunity ( community )
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleCommunityRequestToJoinResponse ( state * ReceivedMessageState , requestToJoinResponseProto * protobuf . CommunityRequestToJoinResponse , statusMessage * v1protocol . StatusMessage ) error {
signer := state . CurrentMessageState . PublicKey
2022-07-01 13:54:02 +00:00
if requestToJoinResponseProto . CommunityId == nil {
2023-07-10 14:11:37 +00:00
return ErrInvalidCommunityID
2022-07-01 13:54:02 +00:00
}
2024-05-31 17:16:45 +00:00
myRequestToJoinId := communities . CalculateRequestID ( m . IdentityPublicKeyString ( ) , requestToJoinResponseProto . CommunityId )
2023-12-09 12:46:30 +00:00
2024-05-31 17:16:45 +00:00
requestToJoin , err := m . communitiesManager . GetRequestToJoin ( myRequestToJoinId )
2023-12-09 12:46:30 +00:00
if err != nil {
return err
}
2024-05-31 17:16:45 +00:00
if requestToJoin . State == communities . RequestToJoinStateCanceled {
2023-12-09 12:46:30 +00:00
return nil
}
2024-05-31 17:16:45 +00:00
community , err := m . communitiesManager . GetByID ( requestToJoinResponseProto . CommunityId )
if err != nil {
return err
}
// check if it is outdated approved request to join
2024-08-07 15:57:02 +00:00
clockSeconds := requestToJoinResponseProto . Clock / 1000
isClockOutdated := clockSeconds < requestToJoin . Clock
isDuplicateAfterMemberLeaves := clockSeconds == requestToJoin . Clock &&
requestToJoin . State == communities . RequestToJoinStateAccepted && ! community . Joined ( )
if requestToJoin . State != communities . RequestToJoinStatePending &&
( isClockOutdated || isDuplicateAfterMemberLeaves ) {
2024-05-31 17:16:45 +00:00
m . logger . Error ( ErrOutdatedCommunityRequestToJoin . Error ( ) ,
zap . String ( "communityId" , community . IDString ( ) ) ,
2024-08-07 15:57:02 +00:00
zap . Bool ( "joined" , community . Joined ( ) ) ,
zap . Uint64 ( "requestToJoinResponseProto.Clock" , requestToJoinResponseProto . Clock ) ,
zap . Uint64 ( "requestToJoin.Clock" , requestToJoin . Clock ) ,
2024-05-31 17:16:45 +00:00
zap . Uint8 ( "state" , uint8 ( requestToJoin . State ) ) )
return ErrOutdatedCommunityRequestToJoin
}
2023-08-18 11:39:59 +00:00
updatedRequest , err := m . communitiesManager . HandleCommunityRequestToJoinResponse ( signer , requestToJoinResponseProto )
2022-07-01 13:54:02 +00:00
if err != nil {
return err
}
2021-03-31 16:23:45 +00:00
2023-02-03 16:33:16 +00:00
if updatedRequest != nil {
2024-02-08 09:24:12 +00:00
state . Response . AddRequestToJoinCommunity ( updatedRequest )
2023-02-03 16:33:16 +00:00
}
2024-05-31 17:16:45 +00:00
community , err = m . communitiesManager . GetByID ( requestToJoinResponseProto . CommunityId )
2024-01-23 16:56:51 +00:00
if err != nil {
return err
}
if requestToJoinResponseProto . Accepted && community != nil && community . HasMember ( & m . identity . PublicKey ) {
2023-10-12 19:21:49 +00:00
2024-01-26 15:29:43 +00:00
communityShardKey := & protobuf . CommunityShardKey {
CommunityId : requestToJoinResponseProto . CommunityId ,
PrivateKey : requestToJoinResponseProto . ProtectedTopicPrivateKey ,
Clock : requestToJoinResponseProto . Community . Clock ,
Shard : requestToJoinResponseProto . Shard ,
}
err = m . handleCommunityShardAndFiltersFromProto ( community , communityShardKey )
2023-10-12 19:21:49 +00:00
if err != nil {
return err
}
2024-05-31 17:16:45 +00:00
// Note: we can't guarantee that REQUEST_TO_JOIN_RESPONSE msg will be delivered before
// COMMUNITY_DESCRIPTION msg, so this msg can return an ErrOrgAlreadyJoined if we
// have been joined during COMMUNITY_DESCRIPTION
2023-05-29 17:57:05 +00:00
response , err := m . JoinCommunity ( context . Background ( ) , requestToJoinResponseProto . CommunityId , false )
2024-05-31 17:16:45 +00:00
if err != nil && err != communities . ErrOrgAlreadyJoined {
2022-07-01 13:54:02 +00:00
return err
}
2023-12-07 16:02:37 +00:00
2024-05-31 17:16:45 +00:00
var communitySettings * communities . CommunitySettings
if response != nil {
// we merge to include chats in response signal to joining a community
err = state . Response . Merge ( response )
if err != nil {
return err
}
if len ( response . Communities ( ) ) > 0 {
communitySettings = response . CommunitiesSettings ( ) [ 0 ]
community = response . Communities ( ) [ 0 ]
}
2023-12-07 16:02:37 +00:00
}
2024-05-31 17:16:45 +00:00
if communitySettings == nil {
communitySettings , err = m . communitiesManager . GetCommunitySettingsByID ( requestToJoinResponseProto . CommunityId )
if err != nil {
return nil
}
}
2022-12-09 14:26:12 +00:00
2024-05-31 17:16:45 +00:00
magnetlink := requestToJoinResponseProto . MagnetUri
2024-06-06 14:59:46 +00:00
if m . archiveManager . IsReady ( ) && communitySettings != nil && communitySettings . HistoryArchiveSupportEnabled && magnetlink != "" {
2022-12-09 14:26:12 +00:00
2024-06-06 14:59:46 +00:00
currentTask := m . archiveManager . GetHistoryArchiveDownloadTask ( community . IDString ( ) )
2024-05-31 17:16:45 +00:00
go func ( currentTask * communities . HistoryArchiveDownloadTask ) {
2022-12-09 14:26:12 +00:00
2024-05-31 17:16:45 +00:00
// Cancel ongoing download/import task
if currentTask != nil && ! currentTask . IsCancelled ( ) {
currentTask . Cancel ( )
currentTask . Waiter . Wait ( )
}
2022-12-09 14:26:12 +00:00
2024-05-31 17:16:45 +00:00
task := & communities . HistoryArchiveDownloadTask {
CancelChan : make ( chan struct { } ) ,
Waiter : * new ( sync . WaitGroup ) ,
Cancelled : false ,
}
2024-06-06 14:59:46 +00:00
m . archiveManager . AddHistoryArchiveDownloadTask ( community . IDString ( ) , task )
2022-12-09 14:26:12 +00:00
2024-05-31 17:16:45 +00:00
task . Waiter . Add ( 1 )
defer task . Waiter . Done ( )
2022-12-09 14:26:12 +00:00
2024-05-31 17:16:45 +00:00
m . shutdownWaitGroup . Add ( 1 )
defer m . shutdownWaitGroup . Done ( )
2022-12-09 14:26:12 +00:00
2024-05-31 17:16:45 +00:00
m . downloadAndImportHistoryArchives ( community . ID ( ) , magnetlink , task . CancelChan )
} ( currentTask )
2022-12-09 14:26:12 +00:00
2024-05-31 17:16:45 +00:00
clock := requestToJoinResponseProto . Community . ArchiveMagnetlinkClock
return m . communitiesManager . UpdateMagnetlinkMessageClock ( community . ID ( ) , clock )
2022-07-01 13:54:02 +00:00
}
}
2022-10-25 22:06:20 +00:00
2021-01-11 10:32:51 +00:00
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleCommunityRequestToLeave ( state * ReceivedMessageState , requestToLeaveProto * protobuf . CommunityRequestToLeave , statusMessage * v1protocol . StatusMessage ) error {
signer := state . CurrentMessageState . PublicKey
2022-08-22 10:10:31 +00:00
if requestToLeaveProto . CommunityId == nil {
2023-07-10 14:11:37 +00:00
return ErrInvalidCommunityID
2022-08-22 10:10:31 +00:00
}
2023-08-18 11:39:59 +00:00
err := m . communitiesManager . HandleCommunityRequestToLeave ( signer , requestToLeaveProto )
2022-08-22 10:10:31 +00:00
if err != nil {
return err
}
response , err := m . RemoveUserFromCommunity ( requestToLeaveProto . CommunityId , common . PubkeyToHex ( signer ) )
if err != nil {
return err
}
if len ( response . Communities ( ) ) > 0 {
state . Response . AddCommunity ( response . Communities ( ) [ 0 ] )
}
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) handleEditMessage ( state * ReceivedMessageState , editMessage EditMessage ) error {
2021-06-25 10:20:40 +00:00
if err := ValidateEditMessage ( editMessage . EditMessage ) ; err != nil {
return err
}
2021-06-07 11:45:06 +00:00
messageID := editMessage . MessageId
2023-04-17 14:44:48 +00:00
originalMessage , err := m . getMessageFromResponseOrDatabase ( state . Response , messageID )
2021-06-07 11:45:06 +00:00
2023-04-17 14:44:48 +00:00
if err == common . ErrRecordNotFound {
2023-08-18 11:39:59 +00:00
return m . persistence . SaveEdit ( & editMessage )
2023-04-17 14:44:48 +00:00
} else if err != nil {
return err
2021-06-07 11:45:06 +00:00
}
2023-03-30 19:37:41 +00:00
originalMessageMentioned := originalMessage . Mentioned
2021-06-07 11:45:06 +00:00
chat , ok := m . allChats . Load ( originalMessage . LocalChatID )
if ! ok {
return errors . New ( "chat not found" )
}
// Check edit is valid
if originalMessage . From != editMessage . From {
return errors . New ( "invalid edit, not the right author" )
}
// Check that edit should be applied
if originalMessage . EditedAt >= editMessage . Clock {
2023-08-18 11:39:59 +00:00
return m . persistence . SaveEdit ( & editMessage )
2021-06-07 11:45:06 +00:00
}
2023-04-27 14:22:26 +00:00
// applyEditMessage modifies the message. Changing the variable name to make it clearer
editedMessage := originalMessage
2021-06-07 11:45:06 +00:00
// Update message and return it
2023-08-18 11:39:59 +00:00
err = m . applyEditMessage ( editMessage . EditMessage , editedMessage )
2021-06-07 11:45:06 +00:00
if err != nil {
return err
}
2023-05-29 17:57:05 +00:00
needToSaveChat := false
2023-04-27 14:22:26 +00:00
if chat . LastMessage != nil && chat . LastMessage . ID == editedMessage . ID {
chat . LastMessage = editedMessage
2023-05-29 17:57:05 +00:00
needToSaveChat = true
2021-06-23 10:07:26 +00:00
}
2023-04-27 14:22:26 +00:00
responseTo , err := m . persistence . MessageByID ( editedMessage . ResponseTo )
2023-02-06 10:38:37 +00:00
if err != nil && err != common . ErrRecordNotFound {
return err
}
2023-04-27 14:22:26 +00:00
err = state . updateExistingActivityCenterNotification ( m . identity . PublicKey , m , editedMessage , responseTo )
2023-02-06 10:38:37 +00:00
if err != nil {
return err
}
2023-03-30 19:37:41 +00:00
2023-04-27 14:22:26 +00:00
editedMessageHasMentions := editedMessage . Mentioned
2023-03-30 19:37:41 +00:00
2023-09-25 15:29:36 +00:00
// Messages in OneToOne chats increase the UnviewedMentionsCount whether or not they include a Mention
if ! chat . OneToOne ( ) {
if editedMessageHasMentions && ! originalMessageMentioned && ! editedMessage . Seen {
// Increase unviewed count when the edited message has a mention and didn't have one before
chat . UnviewedMentionsCount ++
needToSaveChat = true
} else if ! editedMessageHasMentions && originalMessageMentioned && ! editedMessage . Seen {
// Opposite of above, the message had a mention, but no longer does, so we reduce the count
chat . UnviewedMentionsCount --
needToSaveChat = true
}
2023-04-27 14:22:26 +00:00
}
2023-09-25 15:29:36 +00:00
2023-04-27 14:22:26 +00:00
if needToSaveChat {
err := m . saveChat ( chat )
if err != nil {
return err
}
2023-03-30 19:37:41 +00:00
}
2023-04-27 14:22:26 +00:00
state . Response . AddMessage ( editedMessage )
2023-04-17 14:44:48 +00:00
// pull updated messages
updatedMessages , err := m . persistence . MessagesByResponseTo ( messageID )
if err != nil {
return err
}
state . Response . AddMessages ( updatedMessages )
2023-02-06 10:38:37 +00:00
state . Response . AddChat ( chat )
2021-06-07 11:45:06 +00:00
2021-06-07 08:31:27 +00:00
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleEditMessage ( state * ReceivedMessageState , editProto * protobuf . EditMessage , statusMessage * v1protocol . StatusMessage ) error {
return m . handleEditMessage ( state , EditMessage {
EditMessage : editProto ,
From : state . CurrentMessageState . Contact . ID ,
ID : state . CurrentMessageState . MessageID ,
SigPubKey : state . CurrentMessageState . PublicKey ,
} )
}
func ( m * Messenger ) handleDeleteMessage ( state * ReceivedMessageState , deleteMessage * DeleteMessage ) error {
if deleteMessage == nil {
return nil
}
2021-07-26 21:06:32 +00:00
if err := ValidateDeleteMessage ( deleteMessage . DeleteMessage ) ; err != nil {
return err
}
messageID := deleteMessage . MessageId
// Check if it's already in the response
2021-08-19 13:16:45 +00:00
originalMessage := state . Response . GetMessage ( messageID )
2021-07-26 21:06:32 +00:00
// otherwise pull from database
if originalMessage == nil {
var err error
originalMessage , err = m . persistence . MessageByID ( messageID )
if err != nil && err != common . ErrRecordNotFound {
return err
}
}
if originalMessage == nil {
return m . persistence . SaveDelete ( deleteMessage )
}
chat , ok := m . allChats . Load ( originalMessage . LocalChatID )
if ! ok {
return errors . New ( "chat not found" )
}
2022-12-02 11:34:02 +00:00
var canDeleteMessageForEveryone = false
2021-07-26 21:06:32 +00:00
if originalMessage . From != deleteMessage . From {
2023-03-07 14:38:09 +00:00
fromPublicKey , err := common . HexToPubkey ( deleteMessage . From )
if err != nil {
return err
}
2022-12-02 11:34:02 +00:00
if chat . ChatType == ChatTypeCommunityChat {
2023-03-07 14:38:09 +00:00
canDeleteMessageForEveryone = m . CanDeleteMessageForEveryoneInCommunity ( chat . CommunityID , fromPublicKey )
if ! canDeleteMessageForEveryone {
return ErrInvalidDeletePermission
2022-12-02 11:34:02 +00:00
}
2023-03-07 14:38:09 +00:00
} else if chat . ChatType == ChatTypePrivateGroupChat {
canDeleteMessageForEveryone = m . CanDeleteMessageForEveryoneInPrivateGroupChat ( chat , fromPublicKey )
2022-12-02 11:34:02 +00:00
if ! canDeleteMessageForEveryone {
return ErrInvalidDeletePermission
}
}
2023-03-07 14:38:09 +00:00
2022-12-02 11:34:02 +00:00
// Check edit is valid
if ! canDeleteMessageForEveryone {
return errors . New ( "invalid delete, not the right author" )
}
2021-07-26 21:06:32 +00:00
}
2023-08-31 07:21:25 +00:00
messagesToDelete , err := m . getOtherMessagesInAlbum ( originalMessage , originalMessage . LocalChatID )
2021-07-26 21:06:32 +00:00
if err != nil {
return err
}
2023-04-27 14:22:26 +00:00
unreadCountDecreased := false
2023-04-14 17:17:56 +00:00
for _ , messageToDelete := range messagesToDelete {
messageToDelete . Deleted = true
messageToDelete . DeletedBy = deleteMessage . DeleteMessage . DeletedBy
err := m . persistence . SaveMessages ( [ ] * common . Message { messageToDelete } )
if err != nil {
return err
}
2021-08-23 14:23:55 +00:00
2023-10-22 09:41:20 +00:00
// we shouldn't sync deleted notification here,
// as the same user on different devices will receive the same message(DeleteMessage) ?
2023-04-14 17:17:56 +00:00
m . logger . Debug ( "deleting activity center notification for message" , zap . String ( "chatID" , chat . ID ) , zap . String ( "messageID" , messageToDelete . ID ) )
2023-10-22 09:41:20 +00:00
_ , err = m . persistence . DeleteActivityCenterNotificationForMessage ( chat . ID , messageToDelete . ID , m . GetCurrentTimeInMillis ( ) )
2021-08-23 14:23:55 +00:00
2023-04-14 17:17:56 +00:00
if err != nil {
m . logger . Warn ( "failed to delete notifications for deleted message" , zap . Error ( err ) )
2021-07-26 21:06:32 +00:00
return err
}
2022-09-12 13:39:07 +00:00
2023-04-27 14:22:26 +00:00
// Reduce chat mention count and unread count if unread
if ! messageToDelete . Seen && ! unreadCountDecreased {
unreadCountDecreased = true
2023-05-16 16:11:52 +00:00
if chat . UnviewedMessagesCount > 0 {
chat . UnviewedMessagesCount --
}
if chat . UnviewedMentionsCount > 0 && ( messageToDelete . Mentioned || messageToDelete . Replied ) {
2023-04-27 14:22:26 +00:00
chat . UnviewedMentionsCount --
}
err := m . saveChat ( chat )
if err != nil {
return err
}
}
2023-04-14 17:17:56 +00:00
state . Response . AddRemovedMessage ( & RemovedMessage { MessageID : messageToDelete . ID , ChatID : chat . ID , DeletedBy : deleteMessage . DeleteMessage . DeletedBy } )
state . Response . AddNotification ( DeletedMessageNotification ( messageToDelete . ID , chat ) )
state . Response . AddActivityCenterNotification ( & ActivityCenterNotification {
ID : types . FromHex ( messageToDelete . ID ) ,
Deleted : true ,
} )
2023-05-08 17:02:54 +00:00
if chat . LastMessage != nil && chat . LastMessage . ID == messageToDelete . ID {
chat . LastMessage = messageToDelete
err = m . saveChat ( chat )
if err != nil {
return nil
2023-04-14 17:17:56 +00:00
}
2023-05-08 17:02:54 +00:00
}
2023-04-14 17:17:56 +00:00
2023-05-08 17:02:54 +00:00
messages , err := m . persistence . LatestMessageByChatID ( chat . ID )
if err != nil {
return err
}
if len ( messages ) > 0 {
previousNotDeletedMessage := messages [ 0 ]
if previousNotDeletedMessage != nil && ! previousNotDeletedMessage . Seen && chat . OneToOne ( ) && ! chat . Active {
m . createMessageNotification ( chat , state , previousNotDeletedMessage )
2023-04-14 17:17:56 +00:00
}
2022-09-12 13:39:07 +00:00
}
2023-04-17 14:44:48 +00:00
// pull updated messages
updatedMessages , err := m . persistence . MessagesByResponseTo ( messageToDelete . ID )
if err != nil {
return err
}
state . Response . AddMessages ( updatedMessages )
2021-07-26 21:06:32 +00:00
}
2021-08-19 13:16:45 +00:00
state . Response . AddChat ( chat )
2021-07-26 21:06:32 +00:00
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleDeleteMessage ( state * ReceivedMessageState , deleteProto * protobuf . DeleteMessage , statusMessage * v1protocol . StatusMessage ) error {
return m . handleDeleteMessage ( state , & DeleteMessage {
DeleteMessage : deleteProto ,
From : state . CurrentMessageState . Contact . ID ,
ID : state . CurrentMessageState . MessageID ,
SigPubKey : state . CurrentMessageState . PublicKey ,
} )
}
2023-04-17 14:44:48 +00:00
func ( m * Messenger ) getMessageFromResponseOrDatabase ( response * MessengerResponse , messageID string ) ( * common . Message , error ) {
originalMessage := response . GetMessage ( messageID )
// otherwise pull from database
if originalMessage != nil {
return originalMessage , nil
}
return m . persistence . MessageByID ( messageID )
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleSyncDeleteForMeMessage ( state * ReceivedMessageState , deleteForMeMessage * protobuf . SyncDeleteForMeMessage , statusMessage * v1protocol . StatusMessage ) error {
2023-05-12 08:31:34 +00:00
if err := ValidateDeleteForMeMessage ( deleteForMeMessage ) ; err != nil {
2022-09-28 11:42:17 +00:00
return err
}
messageID := deleteForMeMessage . MessageId
// Check if it's already in the response
2023-04-17 14:44:48 +00:00
originalMessage , err := m . getMessageFromResponseOrDatabase ( state . Response , messageID )
2022-09-28 11:42:17 +00:00
2023-04-17 14:44:48 +00:00
if err == common . ErrRecordNotFound {
2023-08-18 11:39:59 +00:00
return m . persistence . SaveOrUpdateDeleteForMeMessage ( deleteForMeMessage )
2023-04-17 14:44:48 +00:00
} else if err != nil {
return err
2022-09-28 11:42:17 +00:00
}
chat , ok := m . allChats . Load ( originalMessage . LocalChatID )
if ! ok {
return errors . New ( "chat not found" )
}
2023-08-31 07:21:25 +00:00
messagesToDelete , err := m . getOtherMessagesInAlbum ( originalMessage , originalMessage . LocalChatID )
2022-09-28 11:42:17 +00:00
if err != nil {
return err
}
2023-04-14 17:17:56 +00:00
for _ , messageToDelete := range messagesToDelete {
messageToDelete . DeletedForMe = true
2022-09-28 11:42:17 +00:00
2023-04-14 17:17:56 +00:00
err := m . persistence . SaveMessages ( [ ] * common . Message { messageToDelete } )
if err != nil {
return err
}
2022-09-28 11:42:17 +00:00
2023-10-22 09:41:20 +00:00
// we shouldn't sync deleted notification here,
// as the same user on different devices will receive the same message(DeleteForMeMessage) ?
2023-04-14 17:17:56 +00:00
m . logger . Debug ( "deleting activity center notification for message" , zap . String ( "chatID" , chat . ID ) , zap . String ( "messageID" , messageToDelete . ID ) )
2023-10-22 09:41:20 +00:00
_ , err = m . persistence . DeleteActivityCenterNotificationForMessage ( chat . ID , messageToDelete . ID , m . GetCurrentTimeInMillis ( ) )
2023-04-14 17:17:56 +00:00
if err != nil {
m . logger . Warn ( "failed to delete notifications for deleted message" , zap . Error ( err ) )
2022-11-17 10:11:58 +00:00
return err
}
2023-04-14 17:17:56 +00:00
if chat . LastMessage != nil && chat . LastMessage . ID == messageToDelete . ID {
2023-05-08 17:02:54 +00:00
chat . LastMessage = messageToDelete
err = m . saveChat ( chat )
if err != nil {
return nil
2023-04-14 17:17:56 +00:00
}
}
state . Response . AddMessage ( messageToDelete )
}
2022-09-28 11:42:17 +00:00
state . Response . AddChat ( chat )
return nil
}
2023-01-20 14:28:30 +00:00
func handleContactRequestChatMessage ( receivedMessage * common . Message , contact * Contact , outgoing bool , logger * zap . Logger ) ( bool , error ) {
receivedMessage . ContactRequestState = common . ContactRequestStatePending
var response ContactRequestProcessingResponse
if outgoing {
response = contact . ContactRequestSent ( receivedMessage . Clock )
} else {
response = contact . ContactRequestReceived ( receivedMessage . Clock )
}
if ! response . processed {
logger . Info ( "not handling contact message since clock lower" )
return false , nil
}
if contact . mutual ( ) {
receivedMessage . ContactRequestState = common . ContactRequestStateAccepted
}
return response . newContactRequestReceived , nil
}
2023-05-10 16:26:31 +00:00
func ( m * Messenger ) handleChatMessage ( state * ReceivedMessageState , forceSeen bool ) error {
2020-01-10 18:59:01 +00:00
logger := m . logger . With ( zap . String ( "site" , "handleChatMessage" ) )
2023-08-18 11:39:59 +00:00
if err := ValidateReceivedChatMessage ( state . CurrentMessageState . Message , state . CurrentMessageState . WhisperTimestamp ) ; err != nil {
2020-01-10 18:59:01 +00:00
logger . Warn ( "failed to validate message" , zap . Error ( err ) )
return err
}
2024-02-22 10:25:13 +00:00
2020-09-01 13:27:01 +00:00
receivedMessage := & common . Message {
2020-01-10 18:59:01 +00:00
ID : state . CurrentMessageState . MessageID ,
ChatMessage : state . CurrentMessageState . Message ,
From : state . CurrentMessageState . Contact . ID ,
Alias : state . CurrentMessageState . Contact . Alias ,
SigPubKey : state . CurrentMessageState . PublicKey ,
Identicon : state . CurrentMessageState . Contact . Identicon ,
WhisperTimestamp : state . CurrentMessageState . WhisperTimestamp ,
}
2023-01-20 14:28:30 +00:00
// is the message coming from us?
isSyncMessage := common . IsPubKeyEqual ( receivedMessage . SigPubKey , & m . identity . PublicKey )
2023-05-10 16:26:31 +00:00
if forceSeen || isSyncMessage {
2021-09-23 11:52:15 +00:00
receivedMessage . Seen = true
}
2023-01-20 14:28:30 +00:00
err := receivedMessage . PrepareContent ( m . myHexIdentity ( ) )
2020-02-10 11:22:37 +00:00
if err != nil {
return fmt . Errorf ( "failed to prepare message content: %v" , err )
}
2023-01-10 17:59:32 +00:00
// If the message is a reply, we check if it's a reply to one of own own messages
if receivedMessage . ResponseTo != "" {
repliedTo , err := m . persistence . MessageByID ( receivedMessage . ResponseTo )
2023-01-11 17:43:04 +00:00
if err != nil && ( err == sql . ErrNoRows || err == common . ErrRecordNotFound ) {
2023-01-10 17:59:32 +00:00
logger . Error ( "failed to get quoted message" , zap . Error ( err ) )
} else if err != nil {
return err
2023-01-20 14:28:30 +00:00
} else if repliedTo . From == m . myHexIdentity ( ) {
2023-01-10 17:59:32 +00:00
receivedMessage . Replied = true
}
}
2024-03-01 17:15:38 +00:00
chat , err := m . matchChatEntity ( receivedMessage , protobuf . ApplicationMetadataMessage_CHAT_MESSAGE )
2020-01-10 18:59:01 +00:00
if err != nil {
2020-07-25 14:16:00 +00:00
return err // matchChatEntity returns a descriptive error message
2020-01-10 18:59:01 +00:00
}
2021-02-15 07:19:14 +00:00
2022-06-21 16:31:15 +00:00
if chat . ReadMessagesAtClockValue >= receivedMessage . Clock {
2021-10-12 10:33:32 +00:00
receivedMessage . Seen = true
}
2021-06-08 06:07:45 +00:00
allowed , err := m . isMessageAllowedFrom ( state . CurrentMessageState . Contact . ID , chat )
2021-04-07 12:57:14 +00:00
if err != nil {
return err
}
if ! allowed {
return ErrMessageNotAllowed
}
2024-02-22 10:25:13 +00:00
if chat . ChatType == ChatTypeCommunityChat {
communityID , err := types . DecodeHex ( chat . CommunityID )
if err != nil {
return err
}
community , err := m . GetCommunityByID ( communityID )
if err != nil {
return err
}
if community == nil {
logger . Warn ( "community not found for msg" ,
zap . String ( "messageID" , receivedMessage . ID ) ,
zap . String ( "from" , receivedMessage . From ) ,
zap . String ( "communityID" , chat . CommunityID ) )
return communities . ErrOrgNotFound
}
pk , err := common . HexToPubkey ( state . CurrentMessageState . Contact . ID )
if err != nil {
return err
}
if community . IsBanned ( pk ) {
logger . Warn ( "skipping msg from banned user" ,
zap . String ( "messageID" , receivedMessage . ID ) ,
zap . String ( "from" , receivedMessage . From ) ,
zap . String ( "communityID" , chat . CommunityID ) )
return errors . New ( "received a messaged from banned user" )
}
}
2022-07-17 13:37:14 +00:00
// It looks like status-mobile created profile chats as public chats
2021-03-04 13:02:08 +00:00
// so for now we need to check for the presence of "@" in their chatID
2021-04-13 03:01:37 +00:00
if chat . Public ( ) && ! chat . ProfileUpdates ( ) {
switch receivedMessage . ContentType {
case protobuf . ChatMessage_IMAGE :
return errors . New ( "images are not allowed in public chats" )
case protobuf . ChatMessage_AUDIO :
return errors . New ( "audio messages are not allowed in public chats" )
}
2021-02-15 07:19:14 +00:00
}
2020-01-10 18:59:01 +00:00
2020-10-20 15:10:28 +00:00
// If profile updates check if author is the same as chat profile public key
if chat . ProfileUpdates ( ) && receivedMessage . From != chat . Profile {
return nil
}
2020-01-10 18:59:01 +00:00
// If deleted-at is greater, ignore message
2020-11-06 13:47:02 +00:00
if chat . DeletedAtClockValue >= receivedMessage . Clock {
2020-01-10 18:59:01 +00:00
return nil
}
// Set the LocalChatID for the message
receivedMessage . LocalChatID = chat . ID
2022-09-02 08:36:07 +00:00
if err := m . updateChatFirstMessageTimestamp ( chat , whisperToUnixTimestamp ( receivedMessage . WhisperTimestamp ) , state . Response ) ; err != nil {
return err
}
2023-01-20 14:28:30 +00:00
// Our own message, mark as sent
if isSyncMessage {
2020-09-01 13:27:01 +00:00
receivedMessage . OutgoingStatus = common . OutgoingStatusSent
2023-01-20 14:28:30 +00:00
} else if ! receivedMessage . Seen {
// Increase unviewed count
2023-03-31 09:15:06 +00:00
skipUpdateUnviewedCountForAlbums := false
if receivedMessage . ContentType == protobuf . ChatMessage_IMAGE {
image := receivedMessage . GetImage ( )
if image != nil && image . AlbumId != "" {
// Skip unviewed counts increasing for other messages from album if we have it in memory
for _ , message := range state . Response . Messages ( ) {
if receivedMessage . ContentType == protobuf . ChatMessage_IMAGE {
img := message . GetImage ( )
if img != nil && img . AlbumId != "" && img . AlbumId == image . AlbumId {
skipUpdateUnviewedCountForAlbums = true
break
}
}
}
if ! skipUpdateUnviewedCountForAlbums {
messages , err := m . persistence . AlbumMessages ( chat . ID , image . AlbumId )
if err != nil {
return err
}
// Skip unviewed counts increasing for other messages from album if we have it in db
skipUpdateUnviewedCountForAlbums = len ( messages ) > 0
}
}
}
if ! skipUpdateUnviewedCountForAlbums {
2023-09-22 10:31:35 +00:00
m . updateUnviewedCounts ( chat , receivedMessage )
2023-03-31 09:15:06 +00:00
}
2020-01-10 18:59:01 +00:00
}
2022-01-18 16:31:34 +00:00
contact := state . CurrentMessageState . Contact
2022-09-29 11:50:23 +00:00
if receivedMessage . ContentType == protobuf . ChatMessage_DISCORD_MESSAGE {
discordMessage := receivedMessage . GetDiscordMessage ( )
discordMessageAuthor := discordMessage . GetAuthor ( )
discordMessageAttachments := discordMessage . GetAttachments ( )
state . Response . AddDiscordMessage ( discordMessage )
state . Response . AddDiscordMessageAuthor ( discordMessageAuthor )
if len ( discordMessageAttachments ) > 0 {
state . Response . AddDiscordMessageAttachments ( discordMessageAttachments )
}
}
2021-06-08 06:07:45 +00:00
err = m . checkForEdits ( receivedMessage )
if err != nil {
return err
}
2021-07-26 21:06:32 +00:00
err = m . checkForDeletes ( receivedMessage )
if err != nil {
return err
}
2022-09-28 11:42:17 +00:00
err = m . checkForDeleteForMes ( receivedMessage )
if err != nil {
return err
}
2023-05-08 17:02:54 +00:00
if ! receivedMessage . Deleted && ! receivedMessage . DeletedForMe {
2021-08-13 17:10:32 +00:00
err = chat . UpdateFromMessage ( receivedMessage , m . getTimesource ( ) )
if err != nil {
return err
}
2020-01-10 18:59:01 +00:00
}
// Set in the modified maps chat
2021-01-11 10:32:51 +00:00
state . Response . AddChat ( chat )
2021-03-29 15:41:30 +00:00
// TODO(samyoul) remove storing of an updated reference pointer?
2021-06-08 06:07:45 +00:00
m . allChats . Store ( chat . ID , chat )
2020-01-10 18:59:01 +00:00
2023-01-20 14:28:30 +00:00
if ! isSyncMessage && receivedMessage . EnsName != "" {
2021-01-11 10:32:51 +00:00
oldRecord , err := m . ensVerifier . Add ( contact . ID , receivedMessage . EnsName , receivedMessage . Clock )
if err != nil {
m . logger . Warn ( "failed to verify ENS name" , zap . Error ( err ) )
} else if oldRecord == nil {
// If oldRecord is nil, a new verification process will take place
// so we reset the record
contact . ENSVerified = false
2021-03-29 15:41:30 +00:00
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
2021-01-11 10:32:51 +00:00
}
2020-02-05 10:09:33 +00:00
}
2023-01-20 14:28:30 +00:00
if ! isSyncMessage && contact . DisplayName != receivedMessage . DisplayName && len ( receivedMessage . DisplayName ) != 0 {
2022-02-17 15:13:10 +00:00
contact . DisplayName = receivedMessage . DisplayName
state . ModifiedContacts . Store ( contact . ID , true )
}
2024-04-03 14:49:57 +00:00
if customizationColor := multiaccountscommon . IDToColorFallbackToBlue ( receivedMessage . CustomizationColor ) ; ! isSyncMessage && receivedMessage . CustomizationColor != 0 && contact . CustomizationColor != customizationColor {
contact . CustomizationColor = customizationColor
state . ModifiedContacts . Store ( contact . ID , true )
}
2020-11-18 09:16:51 +00:00
if receivedMessage . ContentType == protobuf . ChatMessage_COMMUNITY {
m . logger . Debug ( "Handling community content type" )
2023-10-25 15:01:33 +00:00
signer , description , err := communities . UnwrapCommunityDescriptionMessage ( receivedMessage . GetCommunity ( ) )
2020-11-18 09:16:51 +00:00
if err != nil {
return err
}
2023-10-19 11:03:41 +00:00
2024-01-08 15:57:57 +00:00
err = m . handleCommunityDescription ( state , signer , description , receivedMessage . GetCommunity ( ) , nil , receivedMessage . GetShard ( ) )
2023-10-25 15:01:33 +00:00
if err != nil {
return err
2023-10-19 11:03:41 +00:00
}
2023-10-25 15:01:33 +00:00
if len ( description . ID ) != 0 {
receivedMessage . CommunityID = description . ID
} else {
// Backward compatibility
receivedMessage . CommunityID = types . EncodeHex ( crypto . CompressPubkey ( signer ) )
}
2020-11-18 09:16:51 +00:00
}
2021-04-07 12:57:14 +00:00
2023-12-15 16:16:18 +00:00
err = m . addPeersyncingMessage ( chat , state . CurrentMessageState . StatusMessage )
if err != nil {
m . logger . Warn ( "failed to add peersyncing message" , zap . Error ( err ) )
}
2024-08-08 14:36:51 +00:00
// If we receive some propagated state from someone who's not
// our paired device, we handle it
if receivedMessage . ContactRequestPropagatedState != nil && ! isSyncMessage {
result := contact . ContactRequestPropagatedStateReceived ( receivedMessage . ContactRequestPropagatedState )
if result . sendBackState {
_ , err = m . sendContactUpdate ( context . Background ( ) , contact . ID , "" , "" , "" , "" , m . dispatchMessage )
if err != nil {
return err
}
}
if result . newContactRequestReceived {
if contact . hasAddedUs ( ) && ! contact . mutual ( ) {
receivedMessage . ContactRequestState = common . ContactRequestStatePending
}
// Add mutual state update message for outgoing contact request
clock := receivedMessage . Clock - 1
updateMessage , err := m . prepareMutualStateUpdateMessage ( contact . ID , MutualStateUpdateTypeSent , clock , receivedMessage . Timestamp , false )
if err != nil {
return err
}
err = m . prepareMessage ( updateMessage , m . httpServer )
if err != nil {
return err
}
err = m . persistence . SaveMessages ( [ ] * common . Message { updateMessage } )
if err != nil {
return err
}
state . Response . AddMessage ( updateMessage )
err = m . createIncomingContactRequestNotification ( contact , state , receivedMessage , true )
if err != nil {
return err
}
}
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
}
if receivedMessage . ContentType == protobuf . ChatMessage_CONTACT_REQUEST && chat . OneToOne ( ) {
chatContact := contact
if isSyncMessage {
chatContact , err = m . BuildContact ( & requests . BuildContact { PublicKey : chat . ID } )
if err != nil {
return err
}
}
if receivedMessage . CustomizationColor != 0 {
chatContact . CustomizationColor = multiaccountscommon . IDToColorFallbackToBlue ( receivedMessage . CustomizationColor )
}
if chatContact . mutual ( ) || chatContact . dismissed ( ) {
m . logger . Info ( "ignoring contact request message for a mutual or dismissed contact" )
return nil
}
sendNotification , err := handleContactRequestChatMessage ( receivedMessage , chatContact , isSyncMessage , m . logger )
if err != nil {
m . logger . Error ( "failed to handle contact request message" , zap . Error ( err ) )
return err
}
state . ModifiedContacts . Store ( chatContact . ID , true )
state . AllContacts . Store ( chatContact . ID , chatContact )
if sendNotification {
err = m . createIncomingContactRequestNotification ( chatContact , state , receivedMessage , true )
if err != nil {
return err
}
}
} else if receivedMessage . ContentType == protobuf . ChatMessage_COMMUNITY {
chat . Highlight = true
}
2021-06-25 08:30:18 +00:00
receivedMessage . New = true
2021-06-03 13:11:55 +00:00
state . Response . AddMessage ( receivedMessage )
2020-01-10 18:59:01 +00:00
return nil
}
2023-12-15 16:16:18 +00:00
func ( m * Messenger ) addPeersyncingMessage ( chat * Chat , msg * v1protocol . StatusMessage ) error {
if msg == nil {
return nil
}
var syncMessageType peersyncing . SyncMessageType
if chat . OneToOne ( ) {
syncMessageType = peersyncing . SyncMessageOneToOneType
} else if chat . CommunityChat ( ) {
syncMessageType = peersyncing . SyncMessageCommunityType
} else if chat . PrivateGroupChat ( ) {
syncMessageType = peersyncing . SyncMessagePrivateGroup
}
syncMessage := peersyncing . SyncMessage {
Type : syncMessageType ,
ID : msg . ApplicationLayer . ID ,
2024-05-14 10:20:13 +00:00
ChatID : [ ] byte ( chat . ID ) ,
2023-12-15 16:16:18 +00:00
Payload : msg . EncryptionLayer . Payload ,
Timestamp : uint64 ( msg . TransportLayer . Message . Timestamp ) ,
}
return m . peersyncing . Add ( syncMessage )
}
2023-09-07 10:33:20 +00:00
func ( m * Messenger ) HandleChatMessage ( state * ReceivedMessageState , message * protobuf . ChatMessage , statusMessage * v1protocol . StatusMessage , fromArchive bool ) error {
2023-08-18 11:39:59 +00:00
state . CurrentMessageState . Message = message
2023-09-07 10:33:20 +00:00
return m . handleChatMessage ( state , fromArchive )
2023-05-10 16:26:31 +00:00
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleRequestAddressForTransaction ( messageState * ReceivedMessageState , command * protobuf . RequestAddressForTransaction , statusMessage * v1protocol . StatusMessage ) error {
err := ValidateReceivedRequestAddressForTransaction ( command , messageState . CurrentMessageState . WhisperTimestamp )
2020-01-10 18:59:01 +00:00
if err != nil {
return err
}
2020-09-01 13:27:01 +00:00
message := & common . Message {
2023-08-18 11:39:59 +00:00
ChatMessage : & protobuf . ChatMessage {
2021-07-07 11:18:18 +00:00
Clock : command . Clock ,
Timestamp : messageState . CurrentMessageState . WhisperTimestamp ,
Text : "Request address for transaction" ,
// ChatId is only used as-is for messages sent to oneself (i.e: mostly sync) so no need to check it here
ChatId : command . GetChatId ( ) ,
2020-07-25 11:46:43 +00:00
MessageType : protobuf . MessageType_ONE_TO_ONE ,
2020-01-10 18:59:01 +00:00
ContentType : protobuf . ChatMessage_TRANSACTION_COMMAND ,
} ,
2020-09-01 13:27:01 +00:00
CommandParameters : & common . CommandParameters {
2020-01-10 18:59:01 +00:00
ID : messageState . CurrentMessageState . MessageID ,
Value : command . Value ,
Contract : command . Contract ,
2020-09-01 13:27:01 +00:00
CommandState : common . CommandStateRequestAddressForTransaction ,
2020-01-10 18:59:01 +00:00
} ,
}
return m . handleCommandMessage ( messageState , message )
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleSyncSetting ( messageState * ReceivedMessageState , message * protobuf . SyncSetting , statusMessage * v1protocol . StatusMessage ) error {
2023-02-14 16:49:57 +00:00
settingField , err := m . extractAndSaveSyncSetting ( message )
2022-12-29 06:16:19 +00:00
if err != nil {
return err
}
2023-04-13 00:45:25 +00:00
if settingField == nil {
return nil
}
switch message . GetType ( ) {
case protobuf . SyncSetting_DISPLAY_NAME :
if newName := message . GetValueString ( ) ; newName != "" && m . account . Name != newName {
m . account . Name = newName
if err := m . multiAccounts . SaveAccount ( * m . account ) ; err != nil {
2023-01-12 03:00:24 +00:00
return err
}
}
2023-04-13 00:45:25 +00:00
case protobuf . SyncSetting_MNEMONIC_REMOVED :
if message . GetValueBool ( ) {
if err := m . settings . DeleteMnemonic ( ) ; err != nil {
return err
}
messageState . Response . AddSetting ( & settings . SyncSettingField { SettingField : settings . Mnemonic } )
}
return nil
2022-12-29 06:16:19 +00:00
}
2023-04-13 00:45:25 +00:00
messageState . Response . AddSetting ( settingField )
2022-12-29 06:16:19 +00:00
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleSyncAccountCustomizationColor ( state * ReceivedMessageState , message * protobuf . SyncAccountCustomizationColor , statusMessage * v1protocol . StatusMessage ) error {
2024-02-07 15:20:54 +00:00
affected , err := m . multiAccounts . UpdateAccountCustomizationColor ( message . GetKeyUid ( ) , message . GetCustomizationColor ( ) , message . GetUpdatedAt ( ) )
2023-07-18 13:35:06 +00:00
if err != nil {
return err
}
2024-01-19 10:26:39 +00:00
if affected > 0 {
2024-04-03 14:49:57 +00:00
m . account . CustomizationColor = multiaccountscommon . CustomizationColor ( message . GetCustomizationColor ( ) )
2024-01-19 10:26:39 +00:00
state . Response . CustomizationColor = message . GetCustomizationColor ( )
}
2023-07-18 13:35:06 +00:00
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleRequestTransaction ( messageState * ReceivedMessageState , command * protobuf . RequestTransaction , statusMessage * v1protocol . StatusMessage ) error {
err := ValidateReceivedRequestTransaction ( command , messageState . CurrentMessageState . WhisperTimestamp )
2020-01-10 18:59:01 +00:00
if err != nil {
return err
}
2020-09-01 13:27:01 +00:00
message := & common . Message {
2023-08-18 11:39:59 +00:00
ChatMessage : & protobuf . ChatMessage {
2021-07-07 11:18:18 +00:00
Clock : command . Clock ,
Timestamp : messageState . CurrentMessageState . WhisperTimestamp ,
Text : "Request transaction" ,
// ChatId is only used for messages sent to oneself (i.e: mostly sync) so no need to check it here
ChatId : command . GetChatId ( ) ,
2020-07-25 11:46:43 +00:00
MessageType : protobuf . MessageType_ONE_TO_ONE ,
2020-01-10 18:59:01 +00:00
ContentType : protobuf . ChatMessage_TRANSACTION_COMMAND ,
} ,
2020-09-01 13:27:01 +00:00
CommandParameters : & common . CommandParameters {
2020-01-10 18:59:01 +00:00
ID : messageState . CurrentMessageState . MessageID ,
Value : command . Value ,
Contract : command . Contract ,
2020-09-01 13:27:01 +00:00
CommandState : common . CommandStateRequestTransaction ,
2020-01-10 18:59:01 +00:00
Address : command . Address ,
} ,
}
return m . handleCommandMessage ( messageState , message )
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleAcceptRequestAddressForTransaction ( messageState * ReceivedMessageState , command * protobuf . AcceptRequestAddressForTransaction , statusMessage * v1protocol . StatusMessage ) error {
err := ValidateReceivedAcceptRequestAddressForTransaction ( command , messageState . CurrentMessageState . WhisperTimestamp )
2020-01-10 18:59:01 +00:00
if err != nil {
return err
}
initialMessage , err := m . persistence . MessageByID ( command . Id )
if err != nil {
return err
}
if initialMessage == nil {
return errors . New ( "message not found" )
}
if initialMessage . LocalChatID != messageState . CurrentMessageState . Contact . ID {
return errors . New ( "From must match" )
}
if initialMessage . OutgoingStatus == "" {
return errors . New ( "Initial message must originate from us" )
}
2020-09-01 13:27:01 +00:00
if initialMessage . CommandParameters . CommandState != common . CommandStateRequestAddressForTransaction {
2020-01-10 18:59:01 +00:00
return errors . New ( "Wrong state for command" )
}
initialMessage . Clock = command . Clock
initialMessage . Timestamp = messageState . CurrentMessageState . WhisperTimestamp
2020-02-21 14:48:53 +00:00
initialMessage . Text = requestAddressForTransactionAcceptedMessage
2020-01-10 18:59:01 +00:00
initialMessage . CommandParameters . Address = command . Address
2020-04-06 12:08:53 +00:00
initialMessage . Seen = false
2020-09-01 13:27:01 +00:00
initialMessage . CommandParameters . CommandState = common . CommandStateRequestAddressForTransactionAccepted
2021-07-07 11:18:18 +00:00
initialMessage . ChatId = command . GetChatId ( )
2020-01-10 18:59:01 +00:00
// Hide previous message
2020-01-17 12:39:09 +00:00
previousMessage , err := m . persistence . MessageByCommandID ( messageState . CurrentMessageState . Contact . ID , command . Id )
2020-10-08 10:46:03 +00:00
if err != nil && err != common . ErrRecordNotFound {
2020-01-10 18:59:01 +00:00
return err
}
if previousMessage != nil {
err = m . persistence . HideMessage ( previousMessage . ID )
if err != nil {
return err
}
initialMessage . Replace = previousMessage . ID
}
return m . handleCommandMessage ( messageState , initialMessage )
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleSendTransaction ( messageState * ReceivedMessageState , command * protobuf . SendTransaction , statusMessage * v1protocol . StatusMessage ) error {
err := ValidateReceivedSendTransaction ( command , messageState . CurrentMessageState . WhisperTimestamp )
2020-01-10 18:59:01 +00:00
if err != nil {
return err
}
transactionToValidate := & TransactionToValidate {
MessageID : messageState . CurrentMessageState . MessageID ,
CommandID : command . Id ,
TransactionHash : command . TransactionHash ,
FirstSeen : messageState . CurrentMessageState . WhisperTimestamp ,
Signature : command . Signature ,
Validate : true ,
From : messageState . CurrentMessageState . PublicKey ,
RetryCount : 0 ,
}
m . logger . Info ( "Saving transction to validate" , zap . Any ( "transaction" , transactionToValidate ) )
return m . persistence . SaveTransactionToValidate ( transactionToValidate )
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleDeclineRequestAddressForTransaction ( messageState * ReceivedMessageState , command * protobuf . DeclineRequestAddressForTransaction , statusMessage * v1protocol . StatusMessage ) error {
err := ValidateReceivedDeclineRequestAddressForTransaction ( command , messageState . CurrentMessageState . WhisperTimestamp )
2020-01-10 18:59:01 +00:00
if err != nil {
return err
}
oldMessage , err := m . persistence . MessageByID ( command . Id )
if err != nil {
return err
}
if oldMessage == nil {
return errors . New ( "message not found" )
}
if oldMessage . LocalChatID != messageState . CurrentMessageState . Contact . ID {
return errors . New ( "From must match" )
}
if oldMessage . OutgoingStatus == "" {
return errors . New ( "Initial message must originate from us" )
}
2020-09-01 13:27:01 +00:00
if oldMessage . CommandParameters . CommandState != common . CommandStateRequestAddressForTransaction {
2020-01-10 18:59:01 +00:00
return errors . New ( "Wrong state for command" )
}
oldMessage . Clock = command . Clock
oldMessage . Timestamp = messageState . CurrentMessageState . WhisperTimestamp
2020-02-21 14:48:53 +00:00
oldMessage . Text = requestAddressForTransactionDeclinedMessage
2020-04-06 12:08:53 +00:00
oldMessage . Seen = false
2020-09-01 13:27:01 +00:00
oldMessage . CommandParameters . CommandState = common . CommandStateRequestAddressForTransactionDeclined
2021-07-07 11:18:18 +00:00
oldMessage . ChatId = command . GetChatId ( )
2020-01-10 18:59:01 +00:00
// Hide previous message
err = m . persistence . HideMessage ( command . Id )
if err != nil {
return err
}
oldMessage . Replace = command . Id
return m . handleCommandMessage ( messageState , oldMessage )
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleDeclineRequestTransaction ( messageState * ReceivedMessageState , command * protobuf . DeclineRequestTransaction , statusMessage * v1protocol . StatusMessage ) error {
err := ValidateReceivedDeclineRequestTransaction ( command , messageState . CurrentMessageState . WhisperTimestamp )
2020-01-10 18:59:01 +00:00
if err != nil {
return err
}
oldMessage , err := m . persistence . MessageByID ( command . Id )
if err != nil {
return err
}
if oldMessage == nil {
return errors . New ( "message not found" )
}
if oldMessage . LocalChatID != messageState . CurrentMessageState . Contact . ID {
return errors . New ( "From must match" )
}
if oldMessage . OutgoingStatus == "" {
return errors . New ( "Initial message must originate from us" )
}
2020-09-01 13:27:01 +00:00
if oldMessage . CommandParameters . CommandState != common . CommandStateRequestTransaction {
2020-01-10 18:59:01 +00:00
return errors . New ( "Wrong state for command" )
}
oldMessage . Clock = command . Clock
oldMessage . Timestamp = messageState . CurrentMessageState . WhisperTimestamp
2020-02-21 14:48:53 +00:00
oldMessage . Text = transactionRequestDeclinedMessage
2020-04-06 12:08:53 +00:00
oldMessage . Seen = false
2020-09-01 13:27:01 +00:00
oldMessage . CommandParameters . CommandState = common . CommandStateRequestTransactionDeclined
2021-07-07 11:18:18 +00:00
oldMessage . ChatId = command . GetChatId ( )
2020-01-10 18:59:01 +00:00
// Hide previous message
err = m . persistence . HideMessage ( command . Id )
if err != nil {
return err
}
oldMessage . Replace = command . Id
return m . handleCommandMessage ( messageState , oldMessage )
}
2024-03-01 17:15:38 +00:00
func ( m * Messenger ) matchChatEntity ( chatEntity common . ChatEntity , messageType protobuf . ApplicationMetadataMessage_Type ) ( * Chat , error ) {
2020-07-25 14:16:00 +00:00
if chatEntity . GetSigPubKey ( ) == nil {
2020-01-10 18:59:01 +00:00
m . logger . Error ( "public key can't be empty" )
2020-07-25 14:16:00 +00:00
return nil , errors . New ( "received a chatEntity with empty public key" )
2020-01-10 18:59:01 +00:00
}
switch {
2020-07-25 14:16:00 +00:00
case chatEntity . GetMessageType ( ) == protobuf . MessageType_PUBLIC_GROUP :
2020-01-10 18:59:01 +00:00
// For public messages, all outgoing and incoming messages have the same chatID
// equal to a public chat name.
2020-07-25 14:16:00 +00:00
chatID := chatEntity . GetChatId ( )
2021-06-08 06:07:45 +00:00
chat , ok := m . allChats . Load ( chatID )
2021-03-29 15:41:30 +00:00
if ! ok {
2020-07-25 14:16:00 +00:00
return nil , errors . New ( "received a public chatEntity from non-existing chat" )
2020-01-10 18:59:01 +00:00
}
2021-09-21 10:14:54 +00:00
if ! chat . Public ( ) && ! chat . ProfileUpdates ( ) && ! chat . Timeline ( ) {
2021-09-08 11:10:53 +00:00
return nil , ErrMessageForWrongChatType
}
2020-01-10 18:59:01 +00:00
return chat , nil
2020-07-27 10:13:22 +00:00
case chatEntity . GetMessageType ( ) == protobuf . MessageType_ONE_TO_ONE && common . IsPubKeyEqual ( chatEntity . GetSigPubKey ( ) , & m . identity . PublicKey ) :
2020-02-10 11:22:37 +00:00
// It's a private message coming from us so we rely on Message.ChatID
2020-01-10 18:59:01 +00:00
// If chat does not exist, it should be created to support multidevice synchronization.
2020-07-25 14:16:00 +00:00
chatID := chatEntity . GetChatId ( )
2021-06-08 06:07:45 +00:00
chat , ok := m . allChats . Load ( chatID )
2021-03-29 15:41:30 +00:00
if ! ok {
2020-01-10 18:59:01 +00:00
if len ( chatID ) != PubKeyStringLength {
return nil , errors . New ( "invalid pubkey length" )
}
bytePubKey , err := hex . DecodeString ( chatID [ 2 : ] )
if err != nil {
return nil , errors . Wrap ( err , "failed to decode hex chatID" )
}
pubKey , err := crypto . UnmarshalPubkey ( bytePubKey )
if err != nil {
return nil , errors . Wrap ( err , "failed to decode pubkey" )
}
2021-06-08 06:07:45 +00:00
chat = CreateOneToOneChat ( chatID [ : 8 ] , pubKey , m . getTimesource ( ) )
2020-01-10 18:59:01 +00:00
}
2021-12-07 14:34:43 +00:00
// if we are the sender, the chat must be active
chat . Active = true
2020-01-10 18:59:01 +00:00
return chat , nil
2020-07-25 14:16:00 +00:00
case chatEntity . GetMessageType ( ) == protobuf . MessageType_ONE_TO_ONE :
// It's an incoming private chatEntity. ChatID is calculated from the signature.
2020-01-10 18:59:01 +00:00
// If a chat does not exist, a new one is created and saved.
2020-07-25 14:16:00 +00:00
chatID := contactIDFromPublicKey ( chatEntity . GetSigPubKey ( ) )
2021-06-08 06:07:45 +00:00
chat , ok := m . allChats . Load ( chatID )
2021-03-29 15:41:30 +00:00
if ! ok {
2020-01-10 18:59:01 +00:00
// TODO: this should be a three-word name used in the mobile client
2021-06-08 06:07:45 +00:00
chat = CreateOneToOneChat ( chatID [ : 8 ] , chatEntity . GetSigPubKey ( ) , m . getTimesource ( ) )
2021-04-07 12:57:14 +00:00
chat . Active = false
2020-01-10 18:59:01 +00:00
}
2021-04-07 12:57:14 +00:00
// We set the chat as inactive and will create a notification
// if it's not coming from a contact
2021-06-08 06:07:45 +00:00
contact , ok := m . allContacts . Load ( chatID )
2023-01-20 14:28:30 +00:00
chat . Active = chat . Active || ( ok && contact . added ( ) )
2020-11-18 09:16:51 +00:00
return chat , nil
case chatEntity . GetMessageType ( ) == protobuf . MessageType_COMMUNITY_CHAT :
chatID := chatEntity . GetChatId ( )
2021-06-08 06:07:45 +00:00
chat , ok := m . allChats . Load ( chatID )
2021-03-29 15:41:30 +00:00
if ! ok {
2020-11-18 09:16:51 +00:00
return nil , errors . New ( "received community chat chatEntity for non-existing chat" )
}
if chat . CommunityID == "" || chat . ChatType != ChatTypeCommunityChat {
return nil , errors . New ( "not an community chat" )
}
2024-03-01 17:15:38 +00:00
canPost , err := m . communitiesManager . CanPost ( chatEntity . GetSigPubKey ( ) , chat . CommunityID , chat . CommunityChatID ( ) , messageType )
2020-11-18 09:16:51 +00:00
if err != nil {
return nil , err
}
2022-05-10 14:21:38 +00:00
2023-10-25 14:26:18 +00:00
if ! canPost {
return nil , errors . New ( "user can't post in community" )
2022-05-10 14:21:38 +00:00
}
2020-01-10 18:59:01 +00:00
return chat , nil
2024-03-01 17:15:38 +00:00
2020-07-25 14:16:00 +00:00
case chatEntity . GetMessageType ( ) == protobuf . MessageType_PRIVATE_GROUP :
// In the case of a group chatEntity, ChatID is the same for all messages belonging to a group.
2020-01-10 18:59:01 +00:00
// It needs to be verified if the signature public key belongs to the chat.
2020-07-25 14:16:00 +00:00
chatID := chatEntity . GetChatId ( )
2021-06-08 06:07:45 +00:00
chat , ok := m . allChats . Load ( chatID )
2021-03-29 15:41:30 +00:00
if ! ok {
2020-07-25 14:16:00 +00:00
return nil , errors . New ( "received group chat chatEntity for non-existing chat" )
2020-01-10 18:59:01 +00:00
}
2022-05-20 10:53:28 +00:00
senderKeyHex := contactIDFromPublicKey ( chatEntity . GetSigPubKey ( ) )
2020-01-15 07:25:09 +00:00
myKeyHex := contactIDFromPublicKey ( & m . identity . PublicKey )
2022-05-20 10:53:28 +00:00
senderIsMember := false
iAmMember := false
2020-01-10 18:59:01 +00:00
for _ , member := range chat . Members {
2022-05-20 10:53:28 +00:00
if member . ID == senderKeyHex {
senderIsMember = true
2020-01-10 18:59:01 +00:00
}
2022-05-20 10:53:28 +00:00
if member . ID == myKeyHex {
iAmMember = true
2020-01-10 18:59:01 +00:00
}
}
2022-05-20 10:53:28 +00:00
if senderIsMember && iAmMember {
2020-01-10 18:59:01 +00:00
return chat , nil
}
return nil , errors . New ( "did not find a matching group chat" )
default :
return nil , errors . New ( "can not match a chat because there is no valid case" )
}
}
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) messageExists ( messageID string , existingMessagesMap map [ string ] bool ) ( bool , error ) {
2020-01-10 18:59:01 +00:00
if _ , ok := existingMessagesMap [ messageID ] ; ok {
return true , nil
}
existingMessagesMap [ messageID ] = true
// Check against the database, this is probably a bit slow for
// each message, but for now might do, we'll make it faster later
existingMessage , err := m . persistence . MessageByID ( messageID )
2020-10-08 10:46:03 +00:00
if err != nil && err != common . ErrRecordNotFound {
2020-01-10 18:59:01 +00:00
return false , err
}
if existingMessage != nil {
return true , nil
2019-10-14 14:10:48 +00:00
}
2020-01-10 18:59:01 +00:00
return false , nil
2019-10-14 14:10:48 +00:00
}
2020-07-24 13:47:58 +00:00
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleEmojiReaction ( state * ReceivedMessageState , pbEmojiR * protobuf . EmojiReaction , statusMessage * v1protocol . StatusMessage ) error {
2020-07-24 13:47:58 +00:00
logger := m . logger . With ( zap . String ( "site" , "HandleEmojiReaction" ) )
2023-08-18 11:39:59 +00:00
if err := ValidateReceivedEmojiReaction ( pbEmojiR , state . Timesource . GetCurrentTime ( ) ) ; err != nil {
2020-07-28 07:53:32 +00:00
logger . Error ( "invalid emoji reaction" , zap . Error ( err ) )
return err
}
2020-07-27 12:27:48 +00:00
from := state . CurrentMessageState . Contact . ID
2020-07-28 07:53:32 +00:00
emojiReaction := & EmojiReaction {
EmojiReaction : pbEmojiR ,
From : from ,
SigPubKey : state . CurrentMessageState . PublicKey ,
}
existingEmoji , err := m . persistence . EmojiReactionByID ( emojiReaction . ID ( ) )
2020-10-08 10:46:03 +00:00
if err != common . ErrRecordNotFound && err != nil {
2020-07-27 12:27:48 +00:00
return err
}
if existingEmoji != nil && existingEmoji . Clock >= pbEmojiR . Clock {
// this is not a valid emoji, ignoring
return nil
}
2024-03-01 17:15:38 +00:00
chat , err := m . matchChatEntity ( emojiReaction , protobuf . ApplicationMetadataMessage_EMOJI_REACTION )
2020-07-25 14:16:00 +00:00
if err != nil {
return err // matchChatEntity returns a descriptive error message
2020-07-24 13:47:58 +00:00
}
2020-07-28 07:53:32 +00:00
// Set local chat id
emojiReaction . LocalChatID = chat . ID
logger . Debug ( "Handling emoji reaction" )
2020-07-24 13:47:58 +00:00
2020-07-25 14:16:00 +00:00
if chat . LastClockValue < pbEmojiR . Clock {
chat . LastClockValue = pbEmojiR . Clock
2020-07-24 13:47:58 +00:00
}
2021-01-11 10:32:51 +00:00
state . Response . AddChat ( chat )
2021-03-29 15:41:30 +00:00
// TODO(samyoul) remove storing of an updated reference pointer?
state . AllChats . Store ( chat . ID , chat )
2020-07-24 13:47:58 +00:00
2020-07-27 12:27:48 +00:00
// save emoji reaction
err = m . persistence . SaveEmojiReaction ( emojiReaction )
if err != nil {
return err
}
state . EmojiReactions [ emojiReaction . ID ( ) ] = emojiReaction
2020-07-27 10:13:22 +00:00
return nil
}
2020-08-07 13:49:37 +00:00
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleGroupChatInvitation ( state * ReceivedMessageState , pbGHInvitations * protobuf . GroupChatInvitation , statusMessage * v1protocol . StatusMessage ) error {
2021-06-08 06:07:45 +00:00
allowed , err := m . isMessageAllowedFrom ( state . CurrentMessageState . Contact . ID , nil )
2021-04-07 12:57:14 +00:00
if err != nil {
return err
}
if ! allowed {
return ErrMessageNotAllowed
}
2020-08-07 13:49:37 +00:00
logger := m . logger . With ( zap . String ( "site" , "HandleGroupChatInvitation" ) )
2023-08-18 11:39:59 +00:00
if err := ValidateReceivedGroupChatInvitation ( pbGHInvitations ) ; err != nil {
2020-08-07 13:49:37 +00:00
logger . Error ( "invalid group chat invitation" , zap . Error ( err ) )
return err
}
groupChatInvitation := & GroupChatInvitation {
GroupChatInvitation : pbGHInvitations ,
SigPubKey : state . CurrentMessageState . PublicKey ,
}
//From is the PK of author of invitation request
if groupChatInvitation . State == protobuf . GroupChatInvitation_REJECTED {
//rejected so From is the current user who received this rejection
groupChatInvitation . From = types . EncodeHex ( crypto . FromECDSAPub ( & m . identity . PublicKey ) )
} else {
//invitation request, so From is the author of message
groupChatInvitation . From = state . CurrentMessageState . Contact . ID
}
existingInvitation , err := m . persistence . InvitationByID ( groupChatInvitation . ID ( ) )
2020-10-08 10:46:03 +00:00
if err != common . ErrRecordNotFound && err != nil {
2020-08-07 13:49:37 +00:00
return err
}
if existingInvitation != nil && existingInvitation . Clock >= pbGHInvitations . Clock {
// this is not a valid invitation, ignoring
return nil
}
// save invitation
err = m . persistence . SaveInvitation ( groupChatInvitation )
if err != nil {
return err
}
state . GroupChatInvitations [ groupChatInvitation . ID ( ) ] = groupChatInvitation
return nil
}
2020-11-18 12:41:36 +00:00
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleContactCodeAdvertisement ( state * ReceivedMessageState , cca * protobuf . ContactCodeAdvertisement , statusMessage * v1protocol . StatusMessage ) error {
if cca . ChatIdentity == nil {
return nil
}
return m . HandleChatIdentity ( state , cca . ChatIdentity , nil )
}
2020-11-18 12:41:36 +00:00
// HandleChatIdentity handles an incoming protobuf.ChatIdentity
// extracts contact information stored in the protobuf and adds it to the user's contact for update.
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleChatIdentity ( state * ReceivedMessageState , ci * protobuf . ChatIdentity , statusMessage * v1protocol . StatusMessage ) error {
2021-02-17 23:14:48 +00:00
s , err := m . settings . GetSettings ( )
2021-04-07 12:57:14 +00:00
if err != nil {
return err
}
2022-02-17 15:13:10 +00:00
contact := state . CurrentMessageState . Contact
2022-03-23 18:47:00 +00:00
viewFromContacts := s . ProfilePicturesVisibility == settings . ProfilePicturesVisibilityContactsOnly
viewFromNoOne := s . ProfilePicturesVisibility == settings . ProfilePicturesVisibilityNone
2021-02-17 23:14:48 +00:00
m . logger . Debug ( "settings found" ,
zap . Bool ( "viewFromContacts" , viewFromContacts ) ,
zap . Bool ( "viewFromNoOne" , viewFromNoOne ) ,
)
// If we don't want to view profile images from anyone, don't process identity images.
// We don't want to store the profile images of other users, even if we don't display images.
2021-10-12 11:18:15 +00:00
inOurContacts , ok := m . allContacts . Load ( state . CurrentMessageState . Contact . ID )
2023-01-20 14:28:30 +00:00
isContact := ok && inOurContacts . added ( )
2021-10-12 11:18:15 +00:00
if viewFromNoOne && ! isContact {
2021-02-17 23:14:48 +00:00
return nil
}
// If there are no images attached to a ChatIdentity, check if message is allowed
// Or if there are images and visibility is set to from contacts only, check if message is allowed
// otherwise process the images without checking if the message is allowed
if len ( ci . Images ) == 0 || ( len ( ci . Images ) > 0 && ( viewFromContacts ) ) {
allowed , err := m . isMessageAllowedFrom ( state . CurrentMessageState . Contact . ID , nil )
if err != nil {
return err
}
if ! allowed {
return ErrMessageNotAllowed
}
2021-04-07 12:57:14 +00:00
}
2020-11-18 12:41:36 +00:00
2021-02-17 23:14:48 +00:00
err = DecryptIdentityImagesWithIdentityPrivateKey ( ci . Images , m . identity , state . CurrentMessageState . PublicKey )
2021-02-15 14:51:49 +00:00
if err != nil {
return err
}
// Remove any images still encrypted after the decryption process
for name , image := range ci . Images {
if image . Encrypted {
delete ( ci . Images , name )
}
}
2024-01-24 20:09:28 +00:00
if len ( ci . Images ) == 0 {
contact . Images = nil
}
clockChanged , imagesChanged , err := m . persistence . UpdateContactChatIdentity ( contact . ID , ci )
2020-12-15 15:28:05 +00:00
if err != nil {
return err
}
2022-08-02 12:56:26 +00:00
contactModified := false
if imagesChanged {
2020-12-15 15:28:05 +00:00
for imageType , image := range ci . Images {
if contact . Images == nil {
contact . Images = make ( map [ string ] images . IdentityImage )
2020-11-30 11:03:44 +00:00
}
2022-08-07 07:35:54 +00:00
contact . Images [ imageType ] = images . IdentityImage { Name : imageType , Payload : image . Payload , Clock : ci . Clock }
2020-12-02 09:31:48 +00:00
2020-11-18 12:41:36 +00:00
}
2023-06-06 18:06:11 +00:00
if err = m . updateContactImagesURL ( contact ) ; err != nil {
return err
}
2022-08-02 12:56:26 +00:00
contactModified = true
}
if clockChanged {
2024-03-28 15:57:59 +00:00
if err = utils . ValidateDisplayName ( & ci . DisplayName ) ; err != nil {
2022-08-02 12:56:26 +00:00
return err
}
if contact . DisplayName != ci . DisplayName && len ( ci . DisplayName ) != 0 {
contact . DisplayName = ci . DisplayName
contactModified = true
}
2024-04-03 14:49:57 +00:00
if customizationColor := multiaccountscommon . IDToColorFallbackToBlue ( ci . CustomizationColor ) ; contact . CustomizationColor != customizationColor {
contact . CustomizationColor = customizationColor
contactModified = true
}
2022-08-10 13:09:15 +00:00
if err = ValidateBio ( & ci . Description ) ; err != nil {
return err
}
2022-08-05 11:22:35 +00:00
if contact . Bio != ci . Description {
contact . Bio = ci . Description
contactModified = true
}
2023-11-13 17:35:41 +00:00
if ci . ProfileShowcase != nil {
err := m . BuildProfileShowcaseFromIdentity ( state , ci . ProfileShowcase )
if err != nil {
return err
}
2024-03-29 10:22:44 +00:00
state . Response . AddUpdatedProfileShowcaseContactID ( contact . ID )
2023-11-09 18:59:01 +00:00
}
2022-08-02 12:56:26 +00:00
}
if contactModified {
2021-03-29 15:41:30 +00:00
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
2020-11-18 12:41:36 +00:00
}
return nil
}
2021-04-07 12:57:14 +00:00
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleAnonymousMetricBatch ( state * ReceivedMessageState , amb * protobuf . AnonymousMetricBatch , statusMessage * v1protocol . StatusMessage ) error {
2021-09-01 12:02:18 +00:00
// TODO
return nil
}
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) checkForEdits ( message * common . Message ) error {
2021-06-07 08:31:27 +00:00
// Check for any pending edit
// If any pending edits are available and valid, apply them
2021-06-08 06:07:45 +00:00
edits , err := m . persistence . GetEdits ( message . ID , message . From )
if err != nil {
return err
}
if len ( edits ) == 0 {
return nil
}
// Apply the first edit that is valid
for _ , e := range edits {
if e . Clock >= message . Clock {
// Update message and return it
2023-08-18 11:39:59 +00:00
err := m . applyEditMessage ( e . EditMessage , message )
2021-06-08 06:07:45 +00:00
if err != nil {
return err
}
return nil
}
}
2021-06-07 08:31:27 +00:00
return nil
}
2023-04-14 17:17:56 +00:00
func ( m * Messenger ) getMessagesToCheckForDelete ( message * common . Message ) ( [ ] * common . Message , error ) {
var messagesToCheck [ ] * common . Message
if message . ContentType == protobuf . ChatMessage_IMAGE {
image := message . GetImage ( )
2023-05-16 15:00:49 +00:00
if image != nil && image . AlbumId != "" {
messagesInTheAlbum , err := m . persistence . albumMessages ( message . ChatId , image . GetAlbumId ( ) )
if err != nil {
return nil , err
}
messagesToCheck = append ( messagesToCheck , messagesInTheAlbum ... )
2023-04-14 17:17:56 +00:00
}
}
messagesToCheck = append ( messagesToCheck , message )
return messagesToCheck , nil
}
2021-07-26 21:06:32 +00:00
func ( m * Messenger ) checkForDeletes ( message * common . Message ) error {
2023-05-16 15:00:49 +00:00
// Get all messages part of the album
2023-04-14 17:17:56 +00:00
messagesToCheck , err := m . getMessagesToCheckForDelete ( message )
2021-07-26 21:06:32 +00:00
if err != nil {
return err
}
2023-04-14 17:17:56 +00:00
var messageDeletes [ ] * DeleteMessage
applyDelete := false
2023-05-16 15:00:49 +00:00
// Loop all messages part of the album, if one of them is marked as deleted, we delete them all
2023-04-14 17:17:56 +00:00
for _ , messageToCheck := range messagesToCheck {
2023-05-16 15:00:49 +00:00
// Check for any pending deletes
// If any pending deletes are available and valid, apply them
messageDeletes , err = m . persistence . GetDeletes ( messageToCheck . ID , messageToCheck . From )
if err != nil {
return err
}
2023-04-14 17:17:56 +00:00
2023-05-16 15:00:49 +00:00
if len ( messageDeletes ) == 0 {
continue
2023-04-14 17:17:56 +00:00
}
// Once one messageDelete has been found, we apply it to all the images in the album
applyDelete = true
2023-05-16 15:00:49 +00:00
break
}
if applyDelete {
for _ , messageToCheck := range messagesToCheck {
err := m . applyDeleteMessage ( messageDeletes , messageToCheck )
if err != nil {
return err
}
2023-04-14 17:17:56 +00:00
}
}
return nil
2021-07-26 21:06:32 +00:00
}
2022-09-28 11:42:17 +00:00
func ( m * Messenger ) checkForDeleteForMes ( message * common . Message ) error {
2023-04-14 17:17:56 +00:00
messagesToCheck , err := m . getMessagesToCheckForDelete ( message )
2022-09-28 11:42:17 +00:00
if err != nil {
return err
}
2023-08-18 11:39:59 +00:00
var messageDeleteForMes [ ] * protobuf . SyncDeleteForMeMessage
2023-04-14 17:17:56 +00:00
applyDelete := false
for _ , messageToCheck := range messagesToCheck {
if ! applyDelete {
// Check for any pending delete for mes
// If any pending deletes are available and valid, apply them
2023-05-12 08:31:34 +00:00
messageDeleteForMes , err = m . persistence . GetDeleteForMeMessagesByMessageID ( messageToCheck . ID )
2023-04-14 17:17:56 +00:00
if err != nil {
return err
}
2022-09-28 11:42:17 +00:00
2023-04-14 17:17:56 +00:00
if len ( messageDeleteForMes ) == 0 {
continue
}
}
// Once one messageDeleteForMes has been found, we apply it to all the images in the album
applyDelete = true
2023-05-12 08:31:34 +00:00
err := m . applyDeleteForMeMessage ( messageToCheck )
2023-04-14 17:17:56 +00:00
if err != nil {
return err
}
}
return nil
2022-09-28 11:42:17 +00:00
}
2021-06-08 06:07:45 +00:00
func ( m * Messenger ) isMessageAllowedFrom ( publicKey string , chat * Chat ) ( bool , error ) {
2021-04-07 12:57:14 +00:00
onlyFromContacts , err := m . settings . GetMessagesFromContactsOnly ( )
if err != nil {
return false , err
}
if ! onlyFromContacts {
return true , nil
}
// if it's from us, it's allowed
2023-01-20 14:28:30 +00:00
if m . myHexIdentity ( ) == publicKey {
2021-04-07 12:57:14 +00:00
return true , nil
}
2024-02-13 15:15:42 +00:00
// If the chat is public, we allow it
if chat != nil && chat . Public ( ) {
2021-04-07 12:57:14 +00:00
return true , nil
}
2024-02-13 15:15:42 +00:00
contact , contactOk := m . allContacts . Load ( publicKey )
// If the chat is active, we allow it
if chat != nil && chat . Active {
if contactOk {
// If the chat is active and it is a 1x1 chat, we need to make sure the contact is added and not removed
return contact . added ( ) , nil
}
2021-04-07 12:57:14 +00:00
return true , nil
}
2024-02-13 15:15:42 +00:00
if ! contactOk {
2021-04-07 12:57:14 +00:00
// If it's not in contacts, we don't allow it
return false , nil
}
// Otherwise we check if we added it
2023-01-20 14:28:30 +00:00
return contact . added ( ) , nil
2021-04-07 12:57:14 +00:00
}
2021-05-26 06:38:25 +00:00
2023-09-22 10:31:35 +00:00
func ( m * Messenger ) updateUnviewedCounts ( chat * Chat , message * common . Message ) {
2023-10-12 12:27:56 +00:00
chat . UnviewedMessagesCount ++
if message . Mentioned || message . Replied || chat . OneToOne ( ) {
chat . UnviewedMentionsCount ++
2021-05-26 06:38:25 +00:00
}
}
2023-06-28 19:45:36 +00:00
func mapSyncAccountToAccount ( message * protobuf . SyncAccount , accountOperability accounts . AccountOperable , accType accounts . AccountType ) * accounts . Account {
2023-05-16 10:48:00 +00:00
return & accounts . Account {
2023-07-20 13:30:23 +00:00
Address : types . BytesToAddress ( message . Address ) ,
KeyUID : message . KeyUid ,
PublicKey : types . HexBytes ( message . PublicKey ) ,
Type : accType ,
Path : message . Path ,
Name : message . Name ,
ColorID : multiaccountscommon . CustomizationColor ( message . ColorId ) ,
Emoji : message . Emoji ,
Wallet : message . Wallet ,
Chat : message . Chat ,
Hidden : message . Hidden ,
Clock : message . Clock ,
Operable : accountOperability ,
Removed : message . Removed ,
Position : message . Position ,
ProdPreferredChainIDs : message . ProdPreferredChainIDs ,
TestPreferredChainIDs : message . TestPreferredChainIDs ,
2023-05-16 10:48:00 +00:00
}
}
2024-02-29 12:44:35 +00:00
func ( m * Messenger ) resolveAccountOperability ( syncAcc * protobuf . SyncAccount , recoverinrecoveringFromWakuInitiatedByKeycard bool ,
syncKpMigratedToKeycard bool , dbKpMigratedToKeycard bool , accountReceivedFromLocalPairing bool ) ( accounts . AccountOperable , error ) {
2023-08-09 10:36:05 +00:00
if accountReceivedFromLocalPairing {
return accounts . AccountOperable ( syncAcc . Operable ) , nil
}
2023-08-25 13:20:53 +00:00
2024-02-29 12:44:35 +00:00
if syncKpMigratedToKeycard || recoverinrecoveringFromWakuInitiatedByKeycard && m . account . KeyUID == syncAcc . KeyUid {
2023-08-25 13:20:53 +00:00
return accounts . AccountFullyOperable , nil
}
2023-06-28 19:45:36 +00:00
accountsOperability := accounts . AccountNonOperable
dbAccount , err := m . settings . GetAccountByAddress ( types . BytesToAddress ( syncAcc . Address ) )
if err != nil && err != accounts . ErrDbAccountNotFound {
return accountsOperability , err
}
if dbAccount != nil {
2023-08-29 15:09:15 +00:00
// We're here when we receive a keypair from the paired device which has just migrated from keycard to app.
if ! syncKpMigratedToKeycard && dbKpMigratedToKeycard {
return accounts . AccountNonOperable , nil
}
2023-06-28 19:45:36 +00:00
return dbAccount . Operable , nil
}
if ! syncKpMigratedToKeycard {
// We're here when we receive a keypair from the paired device which is either:
// 1. regular keypair or
// 2. was just converted from keycard to a regular keypair.
2023-07-05 12:41:58 +00:00
dbKeycardsForKeyUID , err := m . settings . GetKeycardsWithSameKeyUID ( syncAcc . KeyUid )
if err != nil {
2023-06-28 19:45:36 +00:00
return accounts . AccountNonOperable , err
}
if len ( dbKeycardsForKeyUID ) > 0 {
2023-07-05 12:41:58 +00:00
// We're here in case 2. from above and in this case we need to mark all accounts for this keypair non operable
2023-05-16 10:48:00 +00:00
return accounts . AccountNonOperable , nil
}
}
2023-08-25 13:20:53 +00:00
if syncAcc . Chat || syncAcc . Wallet {
2023-05-16 10:48:00 +00:00
accountsOperability = accounts . AccountFullyOperable
} else {
2023-06-28 19:45:36 +00:00
partiallyOrFullyOperable , err := m . settings . IsAnyAccountPartiallyOrFullyOperableForKeyUID ( syncAcc . KeyUid )
2023-05-16 10:48:00 +00:00
if err != nil {
if err == accounts . ErrDbKeypairNotFound {
return accounts . AccountNonOperable , nil
}
return accounts . AccountNonOperable , err
}
if partiallyOrFullyOperable {
accountsOperability = accounts . AccountPartiallyOperable
}
}
return accountsOperability , nil
}
2022-05-18 10:42:51 +00:00
2023-07-27 13:20:40 +00:00
func ( m * Messenger ) handleSyncWatchOnlyAccount ( message * protobuf . SyncAccount , fromBackup bool ) ( * accounts . Account , error ) {
2023-06-28 19:45:36 +00:00
if message . KeyUid != "" {
return nil , ErrNotWatchOnlyAccount
2022-05-18 10:42:51 +00:00
}
2023-05-16 10:48:00 +00:00
accountOperability := accounts . AccountFullyOperable
2023-04-19 14:44:57 +00:00
accAddress := types . BytesToAddress ( message . Address )
dbAccount , err := m . settings . GetAccountByAddress ( accAddress )
2023-05-16 10:48:00 +00:00
if err != nil && err != accounts . ErrDbAccountNotFound {
2023-04-19 14:44:57 +00:00
return nil , err
2022-05-18 10:42:51 +00:00
}
2023-05-16 10:48:00 +00:00
if dbAccount != nil {
if message . Clock <= dbAccount . Clock {
return nil , ErrTryingToStoreOldWalletAccount
}
2023-04-19 14:44:57 +00:00
2023-05-16 10:48:00 +00:00
if message . Removed {
2023-07-20 15:00:39 +00:00
err = m . settings . RemoveAccount ( accAddress , message . Clock )
2023-07-19 11:50:16 +00:00
if err != nil {
return nil , err
}
2023-07-27 13:20:40 +00:00
// if keypair is retrieved from backed up data, no need for resolving accounts positions
if ! fromBackup {
err = m . settings . ResolveAccountsPositions ( message . Clock )
if err != nil {
return nil , err
}
}
2023-05-16 10:48:00 +00:00
dbAccount . Removed = true
2023-07-27 13:20:40 +00:00
return dbAccount , nil
2023-04-19 14:44:57 +00:00
}
}
2023-06-28 19:45:36 +00:00
acc := mapSyncAccountToAccount ( message , accountOperability , accounts . AccountTypeWatch )
2023-05-16 10:48:00 +00:00
2023-06-28 19:45:36 +00:00
err = m . settings . SaveOrUpdateAccounts ( [ ] * accounts . Account { acc } , false )
2023-04-19 14:44:57 +00:00
if err != nil {
return nil , err
}
2024-08-30 19:28:03 +00:00
if m . config . accountsFeed != nil {
var eventType accountsevent . EventType
if acc . Removed {
eventType = accountsevent . EventTypeRemoved
} else {
eventType = accountsevent . EventTypeAdded
}
m . config . accountsFeed . Send ( accountsevent . Event {
Type : eventType ,
Accounts : [ ] gethcommon . Address { gethcommon . Address ( acc . Address ) } ,
} )
}
2023-04-19 14:44:57 +00:00
return acc , nil
}
2023-12-04 10:18:05 +00:00
func ( m * Messenger ) handleSyncTokenPreferences ( message * protobuf . SyncTokenPreferences ) ( [ ] walletsettings . TokenPreferences , error ) {
if len ( message . Preferences ) == 0 {
return nil , nil
}
dbLastUpdate , err := m . settings . GetClockOfLastTokenPreferencesChange ( )
if err != nil {
return nil , err
}
groupByCommunity , err := m . settings . GetTokenGroupByCommunity ( )
if err != nil {
return nil , err
}
// Since adding new token preferences updates `ClockOfLastTokenPreferencesChange` we should handle token preferences changes
// even they are with the same clock, that ensures the correct order in case of syncing devices.
if message . Clock < dbLastUpdate {
return nil , ErrTryingToApplyOldTokenPreferences
}
var tokenPreferences [ ] walletsettings . TokenPreferences
for _ , pref := range message . Preferences {
tokenPref := walletsettings . TokenPreferences {
Key : pref . Key ,
Position : int ( pref . Position ) ,
GroupPosition : int ( pref . GroupPosition ) ,
Visible : pref . Visible ,
CommunityID : pref . CommunityId ,
}
tokenPreferences = append ( tokenPreferences , tokenPref )
}
err = m . settings . UpdateTokenPreferences ( tokenPreferences , groupByCommunity , message . Testnet , message . Clock )
if err != nil {
return nil , err
}
return tokenPreferences , nil
}
2024-01-17 13:12:49 +00:00
func ( m * Messenger ) handleSyncCollectiblePreferences ( message * protobuf . SyncCollectiblePreferences ) ( [ ] walletsettings . CollectiblePreferences , error ) {
if len ( message . Preferences ) == 0 {
return nil , nil
}
dbLastUpdate , err := m . settings . GetClockOfLastCollectiblePreferencesChange ( )
if err != nil {
return nil , err
}
groupByCommunity , err := m . settings . GetCollectibleGroupByCommunity ( )
if err != nil {
return nil , err
}
groupByCollection , err := m . settings . GetCollectibleGroupByCollection ( )
if err != nil {
return nil , err
}
// Since adding new collectible preferences updates `ClockOfLastCollectiblePreferencesChange` we should handle collectible
// preferences changes even they are with the same clock, that ensures the correct order in case of syncing devices.
if message . Clock < dbLastUpdate {
return nil , ErrTryingToApplyOldCollectiblePreferences
}
var collectiblePreferences [ ] walletsettings . CollectiblePreferences
for _ , pref := range message . Preferences {
collectiblePref := walletsettings . CollectiblePreferences {
Type : walletsettings . CollectiblePreferencesType ( pref . Type ) ,
Key : pref . Key ,
Position : int ( pref . Position ) ,
Visible : pref . Visible ,
}
collectiblePreferences = append ( collectiblePreferences , collectiblePref )
}
err = m . settings . UpdateCollectiblePreferences ( collectiblePreferences , groupByCommunity , groupByCollection , message . Testnet , message . Clock )
if err != nil {
return nil , err
}
return collectiblePreferences , nil
}
2023-07-16 11:11:48 +00:00
func ( m * Messenger ) handleSyncAccountsPositions ( message * protobuf . SyncAccountsPositions ) ( [ ] * accounts . Account , error ) {
if len ( message . Accounts ) == 0 {
return nil , nil
}
dbLastUpdate , err := m . settings . GetClockOfLastAccountsPositionChange ( )
if err != nil {
return nil , err
}
2023-07-19 11:50:16 +00:00
// Since adding new account updates `ClockOfLastAccountsPositionChange` we should handle account order changes
// even they are with the same clock, that ensures the correct order in case of syncing devices.
if message . Clock < dbLastUpdate {
2023-07-16 11:11:48 +00:00
return nil , ErrTryingToApplyOldWalletAccountsOrder
}
var accs [ ] * accounts . Account
for _ , sAcc := range message . Accounts {
acc := & accounts . Account {
Address : types . BytesToAddress ( sAcc . Address ) ,
KeyUID : sAcc . KeyUid ,
Position : sAcc . Position ,
}
accs = append ( accs , acc )
}
err = m . settings . SetWalletAccountsPositions ( accs , message . Clock )
if err != nil {
return nil , err
}
return accs , nil
}
2023-08-02 07:39:58 +00:00
func ( m * Messenger ) handleProfileKeypairMigration ( state * ReceivedMessageState , fromLocalPairing bool , message * protobuf . SyncKeypair ) ( handled bool , err error ) {
if message == nil {
return false , errors . New ( "handleProfileKeypairMigration receive a nil message" )
}
if fromLocalPairing {
return false , nil
}
if m . account . KeyUID != message . KeyUid {
return false , nil
}
dbKeypair , err := m . settings . GetKeypairByKeyUID ( message . KeyUid )
if err != nil {
return false , err
}
if dbKeypair . Clock >= message . Clock {
return false , nil
}
migrationNeeded := dbKeypair . MigratedToKeycard ( ) && len ( message . Keycards ) == 0 || // `true` if profile keypair was migrated to the app on one of paired devices
! dbKeypair . MigratedToKeycard ( ) && len ( message . Keycards ) > 0 // `true` if profile keypair was migrated to a Keycard on one of paired devices
err = m . settings . SaveSettingField ( settings . ProfileMigrationNeeded , migrationNeeded )
if err != nil {
return false , err
}
state . Response . AddSetting ( & settings . SyncSettingField { SettingField : settings . ProfileMigrationNeeded , Value : migrationNeeded } )
return migrationNeeded , nil
}
2023-08-24 13:05:04 +00:00
func ( m * Messenger ) handleSyncKeypair ( message * protobuf . SyncKeypair , fromLocalPairing bool , acNofificationCallback func ( ) error ) ( * accounts . Keypair , error ) {
2023-05-26 16:37:40 +00:00
if message == nil {
return nil , errors . New ( "handleSyncKeypair receive a nil message" )
}
2023-05-16 10:48:00 +00:00
dbKeypair , err := m . settings . GetKeypairByKeyUID ( message . KeyUid )
if err != nil && err != accounts . ErrDbKeypairNotFound {
return nil , err
}
kp := & accounts . Keypair {
KeyUID : message . KeyUid ,
Name : message . Name ,
Type : accounts . KeypairType ( message . Type ) ,
DerivedFrom : message . DerivedFrom ,
LastUsedDerivationIndex : message . LastUsedDerivationIndex ,
SyncedFrom : message . SyncedFrom ,
Clock : message . Clock ,
2023-06-28 19:45:36 +00:00
Removed : message . Removed ,
2023-05-16 10:48:00 +00:00
}
if dbKeypair != nil {
2023-06-28 19:45:36 +00:00
if dbKeypair . Clock >= kp . Clock {
return nil , ErrTryingToStoreOldKeypair
}
2023-05-16 10:48:00 +00:00
// in case of keypair update, we need to keep `synced_from` field as it was when keypair was introduced to this device for the first time
2023-08-09 11:14:17 +00:00
// but in case if keypair on this device came from the backup (e.g. device A recovered from waku, then device B paired with the device A
// via local pairing, before device A made its keypairs fully operable) we need to update syncedFrom when user on this device when that
// keypair becomes operable on any of other paired devices
if dbKeypair . SyncedFrom != accounts . SyncedFromBackup {
kp . SyncedFrom = dbKeypair . SyncedFrom
}
2023-05-16 10:48:00 +00:00
}
2023-08-25 13:20:53 +00:00
syncKpMigratedToKeycard := len ( message . Keycards ) > 0
2024-02-29 12:44:35 +00:00
recoveringFromWaku := message . SyncedFrom == accounts . SyncedFromBackup
multiAcc , err := m . multiAccounts . GetAccount ( kp . KeyUID )
if err != nil {
return nil , err
}
recoverinrecoveringFromWakuInitiatedByKeycard := recoveringFromWaku && multiAcc != nil && multiAcc . RefersToKeycard ( )
2023-06-28 19:45:36 +00:00
for _ , sAcc := range message . Accounts {
2024-02-29 12:44:35 +00:00
accountOperability , err := m . resolveAccountOperability ( sAcc ,
recoverinrecoveringFromWakuInitiatedByKeycard ,
syncKpMigratedToKeycard ,
dbKeypair != nil && dbKeypair . MigratedToKeycard ( ) ,
fromLocalPairing )
2023-06-28 19:45:36 +00:00
if err != nil {
return nil , err
2023-05-16 10:48:00 +00:00
}
2023-06-28 19:45:36 +00:00
acc := mapSyncAccountToAccount ( sAcc , accountOperability , accounts . GetAccountTypeForKeypairType ( kp . Type ) )
2023-05-16 10:48:00 +00:00
2023-06-28 19:45:36 +00:00
kp . Accounts = append ( kp . Accounts , acc )
}
2024-02-29 12:44:35 +00:00
if ! fromLocalPairing && ! recoverinrecoveringFromWakuInitiatedByKeycard {
2023-08-25 13:20:53 +00:00
if kp . Removed ||
dbKeypair != nil && ! dbKeypair . MigratedToKeycard ( ) && syncKpMigratedToKeycard {
// delete all keystore files
err = m . deleteKeystoreFilesForKeypair ( dbKeypair )
if err != nil {
return nil , err
2023-06-28 19:45:36 +00:00
}
2023-08-25 13:20:53 +00:00
if syncKpMigratedToKeycard {
err = m . settings . MarkKeypairFullyOperable ( dbKeypair . KeyUID , 0 , false )
2023-06-28 19:45:36 +00:00
if err != nil {
return nil , err
}
2023-04-19 14:44:57 +00:00
}
2023-08-25 13:20:53 +00:00
} else if dbKeypair != nil {
for _ , dbAcc := range dbKeypair . Accounts {
removeAcc := false
for _ , acc := range kp . Accounts {
if dbAcc . Address == acc . Address && acc . Removed && ! dbAcc . Removed {
removeAcc = true
break
}
}
if removeAcc {
err = m . deleteKeystoreFileForAddress ( dbAcc . Address )
if err != nil {
return nil , err
}
}
}
2023-04-19 14:44:57 +00:00
}
2023-06-28 19:45:36 +00:00
}
2023-03-20 07:33:08 +00:00
2023-07-20 15:00:39 +00:00
// deleting keypair will delete related keycards as well
err = m . settings . RemoveKeypair ( message . KeyUid , message . Clock )
2023-06-28 19:45:36 +00:00
if err != nil && err != accounts . ErrDbKeypairNotFound {
return nil , err
2023-05-16 10:48:00 +00:00
}
2023-07-28 13:06:50 +00:00
// if entire keypair was removed and keypair is already in db, there is no point to continue
if kp . Removed && dbKeypair != nil {
2023-07-27 13:20:40 +00:00
// if keypair is retrieved from backed up data, no need for resolving accounts positions
if message . SyncedFrom != accounts . SyncedFromBackup {
err = m . settings . ResolveAccountsPositions ( message . Clock )
if err != nil {
return nil , err
}
2023-07-19 11:50:16 +00:00
}
2023-06-28 19:45:36 +00:00
return kp , nil
}
2023-05-16 10:48:00 +00:00
2023-07-19 11:50:16 +00:00
// save keypair first
2023-06-28 19:45:36 +00:00
err = m . settings . SaveOrUpdateKeypair ( kp )
2023-05-16 10:48:00 +00:00
if err != nil {
2023-06-28 19:45:36 +00:00
return nil , err
2023-05-16 10:48:00 +00:00
}
2023-07-27 13:20:40 +00:00
// if keypair is retrieved from backed up data, no need for resolving accounts positions
if message . SyncedFrom != accounts . SyncedFromBackup {
// then resolve accounts positions, cause some accounts might be removed
err = m . settings . ResolveAccountsPositions ( message . Clock )
if err != nil {
return nil , err
}
2023-08-24 13:05:04 +00:00
// if keypair is coming from paired device (means not from backup) and it's not among known, active keypairs,
// we need to add an activity center notification
if ! kp . Removed && dbKeypair == nil {
defer func ( ) {
err = acNofificationCallback ( )
} ( )
}
2023-07-19 11:50:16 +00:00
}
2023-05-16 10:48:00 +00:00
for _ , sKc := range message . Keycards {
kc := accounts . Keycard { }
kc . FromSyncKeycard ( sKc )
2023-07-05 12:41:58 +00:00
err = m . settings . SaveOrUpdateKeycard ( kc , message . Clock , false )
if err != nil {
return nil , err
}
2023-06-28 19:45:36 +00:00
kp . Keycards = append ( kp . Keycards , & kc )
2023-05-16 10:48:00 +00:00
}
2023-07-19 11:50:16 +00:00
// getting keypair form the db, cause keypair related accounts positions might be changed
dbKeypair , err = m . settings . GetKeypairByKeyUID ( message . KeyUid )
if err != nil {
return nil , err
}
2024-08-30 19:28:03 +00:00
if m . config . accountsFeed != nil {
addedAddresses := [ ] gethcommon . Address { }
removedAddresses := [ ] gethcommon . Address { }
if dbKeypair . Removed {
for _ , acc := range dbKeypair . Accounts {
removedAddresses = append ( removedAddresses , gethcommon . Address ( acc . Address ) )
}
} else {
for _ , acc := range dbKeypair . Accounts {
if acc . Chat {
continue
}
if acc . Removed {
removedAddresses = append ( removedAddresses , gethcommon . Address ( acc . Address ) )
} else {
addedAddresses = append ( addedAddresses , gethcommon . Address ( acc . Address ) )
}
}
}
if len ( addedAddresses ) > 0 {
m . config . accountsFeed . Send ( accountsevent . Event {
Type : accountsevent . EventTypeAdded ,
Accounts : addedAddresses ,
} )
}
if len ( removedAddresses ) > 0 {
m . config . accountsFeed . Send ( accountsevent . Event {
Type : accountsevent . EventTypeRemoved ,
Accounts : removedAddresses ,
} )
}
}
2023-07-19 11:50:16 +00:00
return dbKeypair , nil
2023-05-16 10:48:00 +00:00
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleSyncAccountsPositions ( state * ReceivedMessageState , message * protobuf . SyncAccountsPositions , statusMessage * v1protocol . StatusMessage ) error {
accs , err := m . handleSyncAccountsPositions ( message )
2023-07-16 11:11:48 +00:00
if err != nil {
if err == ErrTryingToApplyOldWalletAccountsOrder ||
err == accounts . ErrAccountWrongPosition ||
err == accounts . ErrNotTheSameNumberOdAccountsToApplyReordering ||
err == accounts . ErrNotTheSameAccountsToApplyReordering {
m . logger . Warn ( "syncing accounts order issue" , zap . Error ( err ) )
return nil
}
return err
}
state . Response . AccountsPositions = append ( state . Response . AccountsPositions , accs ... )
return nil
}
2023-12-04 10:18:05 +00:00
func ( m * Messenger ) HandleSyncTokenPreferences ( state * ReceivedMessageState , message * protobuf . SyncTokenPreferences , statusMessage * v1protocol . StatusMessage ) error {
tokenPreferences , err := m . handleSyncTokenPreferences ( message )
if err != nil {
if err == ErrTryingToApplyOldTokenPreferences {
m . logger . Warn ( "syncing token preferences issue" , zap . Error ( err ) )
return nil
}
return err
}
state . Response . TokenPreferences = append ( state . Response . TokenPreferences , tokenPreferences ... )
return nil
}
2024-01-17 13:12:49 +00:00
func ( m * Messenger ) HandleSyncCollectiblePreferences ( state * ReceivedMessageState , message * protobuf . SyncCollectiblePreferences , statusMessage * v1protocol . StatusMessage ) error {
collectiblePreferences , err := m . handleSyncCollectiblePreferences ( message )
if err != nil {
if err == ErrTryingToApplyOldCollectiblePreferences {
m . logger . Warn ( "syncing collectible preferences issue" , zap . Error ( err ) )
return nil
}
return err
}
state . Response . CollectiblePreferences = append ( state . Response . CollectiblePreferences , collectiblePreferences ... )
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleSyncAccount ( state * ReceivedMessageState , message * protobuf . SyncAccount , statusMessage * v1protocol . StatusMessage ) error {
acc , err := m . handleSyncWatchOnlyAccount ( message , false )
2023-05-16 10:48:00 +00:00
if err != nil {
2023-06-28 19:45:36 +00:00
if err == ErrTryingToStoreOldWalletAccount {
return nil
}
2023-05-16 10:48:00 +00:00
return err
}
2023-06-28 19:45:36 +00:00
state . Response . WatchOnlyAccounts = append ( state . Response . WatchOnlyAccounts , acc )
2023-05-16 10:48:00 +00:00
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleSyncKeypair ( state * ReceivedMessageState , message * protobuf . SyncKeypair , statusMessage * v1protocol . StatusMessage ) error {
return m . handleSyncKeypairInternal ( state , message , false )
}
func ( m * Messenger ) handleSyncKeypairInternal ( state * ReceivedMessageState , message * protobuf . SyncKeypair , fromLocalPairing bool ) error {
2023-09-12 12:45:32 +00:00
if message == nil {
return errors . New ( "handleSyncKeypairInternal receive a nil message" )
}
if m . walletAPI != nil {
err := m . walletAPI . SetPairingsJSONFileContent ( message . KeycardPairings )
if err != nil {
return err
}
}
2023-08-02 07:39:58 +00:00
// check for the profile keypair migration first on paired device
handled , err := m . handleProfileKeypairMigration ( state , fromLocalPairing , message )
if err != nil {
return err
}
if handled {
return nil
}
2023-08-24 13:05:04 +00:00
kp , err := m . handleSyncKeypair ( message , fromLocalPairing , func ( ) error {
return m . addNewKeypairAddedOnPairedDeviceACNotification ( message . KeyUid , state . Response )
} )
2023-05-16 10:48:00 +00:00
if err != nil {
2023-06-28 19:45:36 +00:00
if err == ErrTryingToStoreOldKeypair {
return nil
}
2023-05-16 10:48:00 +00:00
return err
}
state . Response . Keypairs = append ( state . Response . Keypairs , kp )
return nil
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandleSyncContactRequestDecision ( state * ReceivedMessageState , message * protobuf . SyncContactRequestDecision , statusMessage * v1protocol . StatusMessage ) error {
2022-08-07 14:25:03 +00:00
var err error
var response * MessengerResponse
2023-02-21 18:08:11 +00:00
2022-08-07 14:25:03 +00:00
if message . DecisionStatus == protobuf . SyncContactRequestDecision_ACCEPTED {
2024-05-15 00:01:47 +00:00
response , err = m . updateAcceptedContactRequest ( nil , message . RequestId , message . ContactId , true )
2022-08-07 14:25:03 +00:00
} else {
2024-05-15 00:01:47 +00:00
response , err = m . declineContactRequest ( message . RequestId , message . ContactId , true )
2022-08-07 14:25:03 +00:00
}
if err != nil {
return err
}
2024-05-15 00:01:47 +00:00
return state . Response . Merge ( response )
2022-08-07 14:25:03 +00:00
}
2023-08-18 11:39:59 +00:00
func ( m * Messenger ) HandlePushNotificationRegistration ( state * ReceivedMessageState , encryptedRegistration [ ] byte , statusMessage * v1protocol . StatusMessage ) error {
if m . pushNotificationServer == nil {
return nil
}
publicKey := state . CurrentMessageState . PublicKey
return m . pushNotificationServer . HandlePushNotificationRegistration ( publicKey , encryptedRegistration )
}
func ( m * Messenger ) HandlePushNotificationResponse ( state * ReceivedMessageState , message * protobuf . PushNotificationResponse , statusMessage * v1protocol . StatusMessage ) error {
if m . pushNotificationClient == nil {
return nil
}
publicKey := state . CurrentMessageState . PublicKey
return m . pushNotificationClient . HandlePushNotificationResponse ( publicKey , message )
}
func ( m * Messenger ) HandlePushNotificationRegistrationResponse ( state * ReceivedMessageState , message * protobuf . PushNotificationRegistrationResponse , statusMessage * v1protocol . StatusMessage ) error {
if m . pushNotificationClient == nil {
return nil
}
publicKey := state . CurrentMessageState . PublicKey
return m . pushNotificationClient . HandlePushNotificationRegistrationResponse ( publicKey , message )
}
func ( m * Messenger ) HandlePushNotificationQuery ( state * ReceivedMessageState , message * protobuf . PushNotificationQuery , statusMessage * v1protocol . StatusMessage ) error {
if m . pushNotificationServer == nil {
return nil
}
publicKey := state . CurrentMessageState . PublicKey
2023-11-08 18:05:33 +00:00
return m . pushNotificationServer . HandlePushNotificationQuery ( publicKey , statusMessage . ApplicationLayer . ID , message )
2023-08-18 11:39:59 +00:00
}
func ( m * Messenger ) HandlePushNotificationQueryResponse ( state * ReceivedMessageState , message * protobuf . PushNotificationQueryResponse , statusMessage * v1protocol . StatusMessage ) error {
if m . pushNotificationClient == nil {
return nil
}
publicKey := state . CurrentMessageState . PublicKey
return m . pushNotificationClient . HandlePushNotificationQueryResponse ( publicKey , message )
}
func ( m * Messenger ) HandlePushNotificationRequest ( state * ReceivedMessageState , message * protobuf . PushNotificationRequest , statusMessage * v1protocol . StatusMessage ) error {
if m . pushNotificationServer == nil {
return nil
}
publicKey := state . CurrentMessageState . PublicKey
2023-11-08 18:05:33 +00:00
return m . pushNotificationServer . HandlePushNotificationRequest ( publicKey , statusMessage . ApplicationLayer . ID , message )
2023-08-18 11:39:59 +00:00
}
func ( m * Messenger ) HandleCommunityDescription ( state * ReceivedMessageState , message * protobuf . CommunityDescription , statusMessage * v1protocol . StatusMessage ) error {
2024-07-15 15:25:12 +00:00
// shard passed as nil since it is handled within by using default shard
2024-01-08 15:57:57 +00:00
err := m . handleCommunityDescription ( state , state . CurrentMessageState . PublicKey , message , statusMessage . EncryptionLayer . Payload , nil , nil )
2023-08-18 11:39:59 +00:00
if err != nil {
m . logger . Warn ( "failed to handle CommunityDescription" , zap . Error ( err ) )
return err
}
return nil
}
func ( m * Messenger ) HandleSyncBookmark ( state * ReceivedMessageState , message * protobuf . SyncBookmark , statusMessage * v1protocol . StatusMessage ) error {
bookmark := & browsers . Bookmark {
URL : message . Url ,
Name : message . Name ,
ImageURL : message . ImageUrl ,
Removed : message . Removed ,
Clock : message . Clock ,
}
state . AllBookmarks [ message . Url ] = bookmark
return nil
}
func ( m * Messenger ) HandleSyncClearHistory ( state * ReceivedMessageState , message * protobuf . SyncClearHistory , statusMessage * v1protocol . StatusMessage ) error {
chatID := message . ChatId
existingChat , ok := state . AllChats . Load ( chatID )
if ! ok {
return ErrChatNotFound
}
if existingChat . DeletedAtClockValue >= message . ClearedAt {
return nil
}
err := m . persistence . ClearHistoryFromSyncMessage ( existingChat , message . ClearedAt )
if err != nil {
return err
}
if existingChat . Public ( ) {
err = m . transport . ClearProcessedMessageIDsCache ( )
if err != nil {
return err
}
}
state . AllChats . Store ( chatID , existingChat )
state . Response . AddChat ( existingChat )
state . Response . AddClearedHistory ( & ClearedHistory {
ClearedAt : message . ClearedAt ,
ChatID : chatID ,
} )
return nil
}
func ( m * Messenger ) HandleSyncTrustedUser ( state * ReceivedMessageState , message * protobuf . SyncTrustedUser , statusMessage * v1protocol . StatusMessage ) error {
updated , err := m . verificationDatabase . UpsertTrustStatus ( message . Id , verification . TrustStatus ( message . Status ) , message . Clock )
if err != nil {
return err
}
if updated {
state . AllTrustStatus [ message . Id ] = verification . TrustStatus ( message . Status )
contact , ok := m . allContacts . Load ( message . Id )
if ! ok {
m . logger . Info ( "contact not found" )
return nil
}
contact . TrustStatus = verification . TrustStatus ( message . Status )
m . allContacts . Store ( contact . ID , contact )
state . ModifiedContacts . Store ( contact . ID , true )
}
return nil
}
func ( m * Messenger ) HandleCommunityMessageArchiveMagnetlink ( state * ReceivedMessageState , message * protobuf . CommunityMessageArchiveMagnetlink , statusMessage * v1protocol . StatusMessage ) error {
return m . HandleHistoryArchiveMagnetlinkMessage ( state , state . CurrentMessageState . PublicKey , message . MagnetUri , message . Clock )
}
2023-08-24 13:05:04 +00:00
func ( m * Messenger ) addNewKeypairAddedOnPairedDeviceACNotification ( keyUID string , response * MessengerResponse ) error {
kp , err := m . settings . GetKeypairByKeyUID ( keyUID )
if err != nil {
return err
}
notification := & ActivityCenterNotification {
ID : types . FromHex ( uuid . New ( ) . String ( ) ) ,
Type : ActivityCenterNotificationTypeNewKeypairAddedToPairedDevice ,
Timestamp : m . getTimesource ( ) . GetCurrentTime ( ) ,
Read : false ,
2023-10-22 09:41:20 +00:00
UpdatedAt : m . GetCurrentTimeInMillis ( ) ,
2023-08-24 13:05:04 +00:00
Message : & common . Message {
ChatMessage : & protobuf . ChatMessage {
Text : kp . Name ,
} ,
ID : kp . KeyUID ,
} ,
}
2023-10-22 09:41:20 +00:00
err = m . addActivityCenterNotification ( response , notification , nil )
2023-08-24 13:05:04 +00:00
if err != nil {
m . logger . Warn ( "failed to create activity center notification" , zap . Error ( err ) )
return err
}
return nil
}
2024-02-17 18:07:20 +00:00
2024-02-26 13:53:40 +00:00
func ( m * Messenger ) HandleSyncProfileShowcasePreferences ( state * ReceivedMessageState , p * protobuf . SyncProfileShowcasePreferences , statusMessage * v1protocol . StatusMessage ) error {
_ , err := m . saveProfileShowcasePreferencesProto ( p , false )
return err
2024-02-17 18:07:20 +00:00
}