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"
2021-03-05 14:38:32 +00:00
2022-10-25 22:06:20 +00:00
"github.com/pborman/uuid"
2019-10-14 14:10:48 +00:00
"github.com/pkg/errors"
2021-05-14 21:22:50 +00:00
"go.uber.org/zap"
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"
2022-03-23 18:47:00 +00:00
"github.com/status-im/status-go/multiaccounts/settings"
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"
2022-08-02 12:56:26 +00:00
"github.com/status-im/status-go/protocol/identity"
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"
)
2021-04-07 12:57:14 +00:00
var ErrMessageNotAllowed = errors . New ( "message from a non-contact" )
2021-09-08 11:10:53 +00:00
var ErrMessageForWrongChatType = errors . New ( "message for the wrong chat type" )
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.
2021-06-07 12:38:13 +00:00
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
2020-01-20 16:44:32 +00:00
logger := m . logger . With ( zap . String ( "site" , "HandleMembershipUpdate" ) )
2020-01-10 18:59:01 +00:00
message , err := v1protocol . MembershipUpdateMessageFromProtobuf ( & rawMembershipUpdate )
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 {
GroupChatInvitation : protobuf . GroupChatInvitation {
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
// A new chat must contain us
2021-04-07 12:57:14 +00:00
if ! group . IsMember ( ourKey ) {
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
}
2021-11-19 14:32:04 +00:00
// A new chat always adds us
wasUserAdded = true
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" )
}
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
wasUserAdded = ! existingGroup . IsMember ( ourKey ) &&
2022-05-20 10:53:28 +00:00
group . IsMember ( ourKey )
2021-11-19 14:32:04 +00:00
// Reactivate deleted group chat on re-invite from contact
chat . Active = chat . Active || ( isActive && wasUserAdded )
// 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
2021-05-26 06:38:25 +00:00
m . createMessageNotification ( chat , messageState )
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 {
messageState . CurrentMessageState . Message = * message . Message
2020-01-10 18:59:01 +00:00
return m . HandleChatMessage ( messageState )
2020-07-28 14:34:49 +00:00
} else if message . EmojiReaction != nil {
return m . HandleEmojiReaction ( messageState , * message . EmojiReaction )
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
}
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) createMessageNotification ( chat * Chat , messageState * ReceivedMessageState ) {
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 ,
LastMessage : chat . LastMessage ,
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 ,
2021-05-26 06:38:25 +00:00
}
2021-09-27 14:02:25 +00:00
2022-12-26 04:35:46 +00:00
err := m . addActivityCenterNotification ( messageState . Response , notification )
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-02-21 18:08:11 +00:00
func ( m * Messenger ) createIncomingContactRequestNotification ( contact * Contact , messageState * ReceivedMessageState , contactRequest * common . Message , createNewNotification bool ) error {
if contactRequest != nil && contactRequest . ContactRequestState == common . ContactRequestStateAccepted {
// Pull one from the db if there
notification , err := m . persistence . GetActivityCenterNotificationByID ( types . FromHex ( contactRequest . ID ) )
if err != nil {
return err
}
if notification != nil {
notification . Message = contactRequest
notification . Read = true
notification . Accepted = true
notification . Dismissed = false
err = m . persistence . SaveActivityCenterNotification ( notification )
if err != nil {
return err
}
messageState . Response . AddMessage ( contactRequest )
messageState . Response . AddActivityCenterNotification ( notification )
}
return nil
}
2022-01-18 16:31:34 +00:00
2022-06-13 10:57:51 +00:00
if contactRequest == nil || contactRequest . ContactRequestState == common . ContactRequestStatePending {
notification , err := m . PendingNotificationContactRequest ( contact . ID )
if err != nil {
return err
}
// If there's already a notification, we will check whether is a default notification
// that has not been dismissed (nor accepted???)
// If it is, we replace it with a non-default, since it contains a message
if notification != nil {
// Check if it's the default notification
if notification . Message . ID == defaultContactRequestID ( contact . ID ) {
// Nothing to do, we already have a default notification
if contactRequest == nil {
return nil
}
// We first dismiss it in the database
err := m . persistence . DismissActivityCenterNotifications ( [ ] types . HexBytes { types . Hex2Bytes ( notification . Message . ID ) } )
if err != nil {
return err
}
2023-01-28 09:52:53 +00:00
// we mark the notification as dismissed & read
2022-06-13 10:57:51 +00:00
notification . Dismissed = true
2023-01-28 09:52:53 +00:00
notification . Read = true
2022-06-13 10:57:51 +00:00
// We remove it from the response, since the client has never seen it, better to just remove it
found := messageState . Response . RemoveActivityCenterNotification ( notification . Message . ID )
// Otherwise, it means we have already passed it to the client, so we add it with a `dismissed` flag
// so it can clean up
if ! found {
messageState . Response . AddActivityCenterNotification ( notification )
}
}
}
}
2022-01-18 16:31:34 +00:00
2022-06-13 10:57:51 +00:00
// Legacy//ContactUpdate contact request
if contactRequest == nil {
2022-01-18 16:31:34 +00:00
if messageState . CurrentMessageState == nil || messageState . CurrentMessageState . MessageID == "" {
return errors . New ( "no available id" )
}
2022-06-13 10:57:51 +00:00
// We use a known id so that we can check if already in the database
defaultID := defaultContactRequestID ( contact . ID )
// Pull one from the db if there
notification , err := m . persistence . GetActivityCenterNotificationByID ( types . FromHex ( defaultID ) )
if err != nil {
return err
}
// if the notification is accepted, we clear it, as this one will replace it
if notification != nil && notification . Accepted {
err = m . persistence . DeleteActivityCenterNotification ( types . FromHex ( defaultID ) )
if err != nil {
return err
}
}
2023-02-21 18:08:11 +00:00
// generate request message
contactRequest , err = m . generateContactRequest (
messageState . CurrentMessageState . Message . Clock ,
messageState . CurrentMessageState . WhisperTimestamp ,
contact ,
"Please add me to your contacts" ,
)
2022-01-18 16:31:34 +00:00
if err != nil {
return err
}
2023-02-21 18:08:11 +00:00
// save this message
2022-01-18 16:31:34 +00:00
messageState . Response . AddMessage ( contactRequest )
err = m . persistence . SaveMessages ( [ ] * common . Message { contactRequest } )
if err != nil {
return err
}
}
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 ) ,
Name : contact . CanonicalName ( ) ,
Message : contactRequest ,
Type : ActivityCenterNotificationTypeContactRequest ,
Author : messageState . CurrentMessageState . Contact . ID ,
Timestamp : messageState . CurrentMessageState . WhisperTimestamp ,
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 ,
2022-01-18 16:31:34 +00:00
}
2022-12-26 04:35:46 +00:00
return m . addActivityCenterNotification ( messageState . Response , notification )
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 )
}
2021-06-08 06:07:45 +00:00
chat , err := m . matchChatEntity ( message )
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-01-10 17:59:32 +00:00
m . updateUnviewedCounts ( chat , message . Mentioned || message . Replied )
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 {
2021-05-26 06:38:25 +00:00
m . createMessageNotification ( chat , state )
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
}
2021-09-03 08:26:05 +00:00
func ( m * Messenger ) HandleSyncInstallationContact ( state * ReceivedMessageState , message protobuf . SyncInstallationContactV2 ) error {
2022-11-17 14:43:58 +00:00
removedOrBlocked := message . Removed || message . Blocked
2021-09-03 08:26:05 +00:00
chat , ok := state . AllChats . Load ( message . Id )
2022-11-17 14:43:58 +00:00
if ! ok && ( message . Added || message . Muted ) && ! removedOrBlocked {
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
}
2021-03-29 15:41:30 +00:00
contact , ok := state . AllContacts . Load ( message . Id )
2020-01-10 18:59:01 +00:00
if ! ok {
2021-09-03 08:26:05 +00:00
if message . Removed {
// 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
contact . processSyncContactRequestState (
ContactRequestState ( message . ContactRequestRemoteState ) ,
uint64 ( message . ContactRequestRemoteClock ) ,
ContactRequestState ( message . ContactRequestLocalState ) ,
uint64 ( message . ContactRequestLocalClock ) )
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
} 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 )
}
if message . HasAddedUs && contact . LastUpdated < message . LastUpdated {
contact . ContactRequestReceived ( message . LastUpdated )
}
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.
if contact . LastUpdated < message . LastUpdated {
if message . DisplayName != "" {
contact . DisplayName = message . DisplayName
}
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
}
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 )
2021-12-06 12:44:40 +00:00
response , err := m . BlockContact ( contact . ID )
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 {
err := m . muteChat ( chat , contact )
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
}
2022-03-24 09:35:56 +00:00
func ( m * Messenger ) HandleSyncProfilePictures ( state * ReceivedMessageState , message protobuf . SyncProfilePictures ) error {
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
}
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) HandleSyncInstallationPublicChat ( state * ReceivedMessageState , message protobuf . SyncInstallationPublicChat ) * Chat {
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 {
chat = CreatePublicChat ( chatID , state . Timesource )
2021-12-17 13:57:54 +00:00
chat . Joined = int64 ( message . Clock )
2021-10-05 17:26:02 +00:00
} else {
2021-12-17 13:57:54 +00:00
existingChat . Joined = int64 ( message . Clock )
2021-10-05 17:26:02 +00:00
}
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 )
return chat
2020-01-15 07:25:09 +00:00
}
2021-10-05 17:26:02 +00:00
func ( m * Messenger ) HandleSyncChatRemoved ( state * ReceivedMessageState , message protobuf . SyncChatRemoved ) error {
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 )
}
2021-10-12 10:33:32 +00:00
func ( m * Messenger ) HandleSyncChatMessagesRead ( state * ReceivedMessageState , message protobuf . SyncChatMessagesRead ) error {
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
}
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) HandlePinMessage ( state * ReceivedMessageState , message protobuf . PinMessage ) error {
2021-05-14 21:22:50 +00:00
logger := m . logger . With ( zap . String ( "site" , "HandlePinMessage" ) )
logger . Info ( "Handling pin message" )
pinMessage := & common . PinMessage {
PinMessage : message ,
// MessageID: message.MessageId,
WhisperTimestamp : state . CurrentMessageState . WhisperTimestamp ,
From : state . CurrentMessageState . Contact . ID ,
SigPubKey : state . CurrentMessageState . PublicKey ,
Identicon : state . CurrentMessageState . Contact . Identicon ,
Alias : state . CurrentMessageState . Contact . Alias ,
}
2021-06-08 06:07:45 +00:00
chat , err := m . matchChatEntity ( pinMessage )
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
}
// Set the LocalChatID for the message
pinMessage . LocalChatID = chat . ID
if c , ok := state . AllChats . Load ( chat . ID ) ; ok {
chat = c
}
// Set the LocalChatID for the message
pinMessage . LocalChatID = chat . ID
if chat . LastClockValue < message . Clock {
chat . LastClockValue = message . Clock
}
state . Response . AddPinMessage ( pinMessage )
// Set in the modified maps chat
state . Response . AddChat ( chat )
state . AllChats . Store ( chat . ID , chat )
return nil
}
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-01-20 14:28:30 +00:00
contact := state . CurrentMessageState . Contact
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
if processingResponse . newContactRequestReceived && contact . mutual ( ) {
// 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
}
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 )
err = m . addActivityCenterNotification ( state . Response , notification )
if err != nil {
return err
}
} else {
err = m . createIncomingContactRequestNotification ( contact , state , request , processingResponse . newContactRequestReceived )
}
2023-01-20 14:28:30 +00:00
if err != nil {
m . logger . Warn ( "could not create contact request notification" , zap . Error ( err ) )
}
}
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
2022-01-18 16:31:34 +00:00
return nil
}
2023-02-21 18:08:11 +00:00
func ( m * Messenger ) HandleAcceptContactRequest ( state * ReceivedMessageState , message protobuf . AcceptContactRequest , senderID string ) error {
// outgoing contact requests are created on the side of a sender
err := m . handleAcceptContactRequestMessage ( state , message . Clock , defaultContactRequestID ( senderID ) , true )
if err != nil {
m . logger . Warn ( "could not accept contact request" , zap . Error ( err ) )
}
// legacy contact requests: the ones that are send with SendContactRequest
err = m . handleAcceptContactRequestMessage ( state , message . Clock , message . Id , false )
if err != nil {
m . logger . Warn ( "could not accept contact request" , zap . Error ( err ) )
}
return nil
}
2023-02-02 18:12:20 +00:00
func ( m * Messenger ) handleRetractContactRequest ( 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-01-20 14:28:30 +00:00
r := contact . ContactRequestRetracted ( message . Clock )
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
// We remove anything that's related to this contact request
2023-01-20 14:28:30 +00:00
err := m . persistence . RemoveAllContactRequestActivityCenterNotifications ( contact . ID )
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
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
}
func ( m * Messenger ) HandleRetractContactRequest ( state * ReceivedMessageState , message protobuf . RetractContactRequest ) error {
contact := state . CurrentMessageState . Contact
err := m . handleRetractContactRequest ( contact , message )
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
}
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) HandleContactUpdate ( state * ReceivedMessageState , message protobuf . ContactUpdate ) error {
2020-01-10 18:59:01 +00:00
logger := m . logger . With ( zap . String ( "site" , "HandleContactUpdate" ) )
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
}
2022-02-17 15:13:10 +00:00
if err = ValidateDisplayName ( & message . DisplayName ) ; err != nil {
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
_ , err = m . sendContactUpdate ( context . Background ( ) , contact . ID , "" , "" , "" , m . dispatchMessage )
if err != nil {
return err
}
}
if result . newContactRequestReceived {
2023-02-21 18:08:11 +00:00
err = m . createIncomingContactRequestNotification ( contact , state , nil , true )
2023-02-02 18:12:20 +00:00
if err != nil {
return err
}
}
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
}
2023-01-20 14:28:30 +00:00
r := contact . ContactRequestReceived ( message . ContactRequestClock )
if r . newContactRequestReceived {
2023-02-21 18:08:11 +00:00
err = m . createIncomingContactRequestNotification ( contact , state , nil , true )
2022-01-18 16:31:34 +00:00
if err != nil {
m . logger . Warn ( "could not create contact request notification" , zap . Error ( err ) )
}
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
}
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) HandlePairInstallation ( state * ReceivedMessageState , message protobuf . PairInstallation ) error {
2020-01-10 18:59:01 +00:00
logger := m . logger . With ( zap . String ( "site" , "HandlePairInstallation" ) )
2020-01-20 16:44:32 +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
}
2020-11-18 09:16:51 +00:00
// HandleCommunityInvitation handles an community invitation
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) HandleCommunityInvitation ( state * ReceivedMessageState , signer * ecdsa . PublicKey , invitation protobuf . CommunityInvitation , rawPayload [ ] byte ) error {
2020-11-18 09:16:51 +00:00
if invitation . PublicKey == nil {
return errors . New ( "invalid pubkey" )
}
pk , err := crypto . DecompressPubkey ( invitation . PublicKey )
if err != nil {
return err
}
if ! common . IsPubKeyEqual ( pk , & m . identity . PublicKey ) {
return errors . New ( "invitation not for us" )
}
2021-01-11 10:32:51 +00:00
communityResponse , err := m . communitiesManager . HandleCommunityInvitation ( signer , & invitation , rawPayload )
2020-11-18 09:16:51 +00:00
if err != nil {
return err
}
2021-01-11 10:32:51 +00:00
community := communityResponse . Community
state . Response . AddCommunity ( community )
state . Response . CommunityChanges = append ( state . Response . CommunityChanges , communityResponse . Changes )
2020-11-18 09:16:51 +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 ) )
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
}
2022-12-09 14:26:12 +00:00
if m . torrentClientReady ( ) && settings != nil && settings . HistoryArchiveSupportEnabled {
2022-04-22 07:42:22 +00:00
signedByOwnedCommunity , err := m . communitiesManager . IsAdminCommunity ( communityPubKey )
if err != nil {
return err
}
joinedCommunity , err := m . communitiesManager . IsJoinedCommunity ( communityPubKey )
if err != nil {
return err
}
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
if ! signedByOwnedCommunity && joinedCommunity && clock >= lastClock {
2022-12-19 08:34:37 +00:00
if lastSeenMagnetlink == magnetlink {
m . communitiesManager . LogStdout ( "already processed this magnetlink" )
return nil
}
2022-04-22 07:42:22 +00:00
m . communitiesManager . UnseedHistoryArchiveTorrent ( id )
2022-12-12 09:22:37 +00:00
currentTask := m . communitiesManager . GetHistoryArchiveDownloadTask ( id . String ( ) )
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
}
2022-12-19 08:34:37 +00:00
m . communitiesManager . 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
m . downloadHistoryArchiveTasksWaitGroup . Add ( 1 )
2022-09-29 11:50:23 +00:00
defer m . downloadHistoryArchiveTasksWaitGroup . 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 { } ) {
downloadTaskInfo , err := m . communitiesManager . DownloadHistoryArchivesByMagnetlink ( id , magnetlink , cancel )
if err != nil {
logMsg := "failed to download history archive data"
if err == communities . ErrTorrentTimedout {
m . communitiesManager . LogStdout ( "torrent has timed out, trying once more..." )
downloadTaskInfo , err = m . communitiesManager . DownloadHistoryArchivesByMagnetlink ( id , magnetlink , cancel )
if err != nil {
m . communitiesManager . LogStdout ( logMsg , zap . Error ( err ) )
return
}
} else {
m . communitiesManager . LogStdout ( logMsg , zap . Error ( err ) )
return
}
}
if downloadTaskInfo . Cancelled {
2022-12-19 08:34:37 +00:00
if downloadTaskInfo . TotalDownloadedArchivesCount > 0 {
m . communitiesManager . LogStdout ( fmt . Sprintf ( "downloaded %d of %d archives so far" , downloadTaskInfo . TotalDownloadedArchivesCount , downloadTaskInfo . TotalArchivesCount ) )
}
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 {
m . communitiesManager . LogStdout ( "couldn't update last seen magnetlink" , zap . Error ( err ) )
}
2023-01-19 13:23:48 +00:00
err = m . importHistoryArchives ( id , cancel )
if err != nil {
m . communitiesManager . LogStdout ( "failed to import history archives" , zap . Error ( err ) )
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-01-13 15:40:39 +00:00
func ( m * Messenger ) handleArchiveMessages ( archiveMessages [ ] * protobuf . WakuMessage , id types . HexBytes ) ( * MessengerResponse , error ) {
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 {
m . communitiesManager . LogStdout ( "failed to handle imported messages" , zap . Error ( err ) )
return nil , err
}
response , err := m . handleRetrievedMessages ( otherMessages , false )
if err != nil {
m . communitiesManager . LogStdout ( "failed to write history archive messages to database" , zap . Error ( err ) )
return nil , err
}
return response , nil
}
2022-10-28 08:41:20 +00:00
func ( m * Messenger ) HandleCommunityCancelRequestToJoin ( state * ReceivedMessageState , signer * ecdsa . PublicKey , cancelRequestToJoinProto protobuf . CommunityCancelRequestToJoin ) error {
if cancelRequestToJoinProto . CommunityId == nil {
return errors . New ( "invalid community id" )
}
requestToJoin , err := m . communitiesManager . HandleCommunityCancelRequestToJoin ( signer , & cancelRequestToJoinProto )
if err != nil {
return err
}
state . Response . RequestsToJoinCommunity = append ( state . Response . RequestsToJoinCommunity , requestToJoin )
return nil
}
2021-01-11 10:32:51 +00:00
// HandleCommunityRequestToJoin handles an community request to join
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) HandleCommunityRequestToJoin ( state * ReceivedMessageState , signer * ecdsa . PublicKey , requestToJoinProto protobuf . CommunityRequestToJoin ) error {
2021-01-11 10:32:51 +00:00
if requestToJoinProto . CommunityId == nil {
return errors . New ( "invalid community id" )
}
requestToJoin , err := m . communitiesManager . HandleCommunityRequestToJoin ( signer , & requestToJoinProto )
if err != nil {
return err
}
2022-07-01 13:54:02 +00:00
if requestToJoin . State == communities . RequestToJoinStateAccepted {
accept := & requests . AcceptRequestToJoinCommunity {
ID : requestToJoin . ID ,
}
_ , err = m . AcceptRequestToJoinCommunity ( accept )
if err != nil {
return err
}
2022-10-25 22:06:20 +00:00
2022-07-01 13:54:02 +00:00
}
2021-01-11 10:32:51 +00:00
2021-03-31 16:23:45 +00:00
community , err := m . communitiesManager . GetByID ( requestToJoinProto . CommunityId )
if err != nil {
return err
}
contactID := contactIDFromPublicKey ( signer )
2021-03-29 15:41:30 +00:00
contact , _ := state . AllContacts . Load ( contactID )
2021-03-31 16:23:45 +00:00
2022-06-22 18:02:44 +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
if requestToJoin . State == communities . RequestToJoinStatePending {
state . Response . RequestsToJoinCommunity = append ( state . Response . RequestsToJoinCommunity , requestToJoin )
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-02-17 10:08:08 +00:00
err = m . addActivityCenterNotification ( state . Response , notification )
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
}
} else {
2023-02-21 18:08:11 +00:00
// Activity Center notification, updating existing for accepted/declined
2022-10-25 22:06:20 +00:00
notification , err := m . persistence . GetActivityCenterNotificationByID ( requestToJoin . ID )
if err != nil {
return err
}
if notification != nil {
if requestToJoin . State == communities . RequestToJoinStateAccepted {
notification . MembershipStatus = ActivityCenterMembershipStatusAccepted
} else {
notification . MembershipStatus = ActivityCenterMembershipStatusDeclined
}
2023-02-17 10:08:08 +00:00
err = m . addActivityCenterNotification ( state . Response , notification )
2023-01-28 09:52:53 +00:00
if err != nil {
2023-02-17 10:08:08 +00:00
m . logger . Error ( "failed to save notification" , zap . Error ( err ) )
2023-01-28 09:52:53 +00:00
return err
2022-10-25 22:06:20 +00:00
}
}
2022-07-01 13:54:02 +00:00
}
return nil
}
func ( m * Messenger ) HandleCommunityRequestToJoinResponse ( state * ReceivedMessageState , signer * ecdsa . PublicKey , requestToJoinResponseProto protobuf . CommunityRequestToJoinResponse ) error {
if requestToJoinResponseProto . CommunityId == nil {
return errors . New ( "invalid community id" )
}
2023-02-03 16:33:16 +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 {
state . Response . RequestsToJoinCommunity = append ( state . Response . RequestsToJoinCommunity , updatedRequest )
}
2022-07-01 13:54:02 +00:00
if requestToJoinResponseProto . Accepted {
response , err := m . JoinCommunity ( context . Background ( ) , requestToJoinResponseProto . CommunityId )
if err != nil {
return err
}
if len ( response . Communities ( ) ) > 0 {
2022-12-09 14:26:12 +00:00
communitySettings := response . CommunitiesSettings ( ) [ 0 ]
community := response . Communities ( ) [ 0 ]
state . Response . AddCommunity ( community )
state . Response . AddCommunitySettings ( communitySettings )
magnetlink := requestToJoinResponseProto . MagnetUri
if m . torrentClientReady ( ) && communitySettings != nil && communitySettings . HistoryArchiveSupportEnabled && magnetlink != "" {
currentTask := m . communitiesManager . GetHistoryArchiveDownloadTask ( community . IDString ( ) )
go func ( currentTask * communities . HistoryArchiveDownloadTask ) {
// Cancel ongoing download/import task
2023-01-23 14:32:35 +00:00
if currentTask != nil && ! currentTask . IsCancelled ( ) {
currentTask . Cancel ( )
2022-12-09 14:26:12 +00:00
currentTask . Waiter . Wait ( )
}
task := & communities . HistoryArchiveDownloadTask {
2023-01-23 14:32:35 +00:00
CancelChan : make ( chan struct { } ) ,
Waiter : * new ( sync . WaitGroup ) ,
Cancelled : false ,
2022-12-09 14:26:12 +00:00
}
m . communitiesManager . AddHistoryArchiveDownloadTask ( community . IDString ( ) , task )
task . Waiter . Add ( 1 )
defer task . Waiter . Done ( )
m . downloadHistoryArchiveTasksWaitGroup . Add ( 1 )
defer m . downloadHistoryArchiveTasksWaitGroup . Done ( )
2023-01-23 14:32:35 +00:00
m . downloadAndImportHistoryArchives ( community . ID ( ) , magnetlink , task . CancelChan )
2022-12-09 14:26:12 +00:00
} ( currentTask )
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
// Activity Center notification
requestID := communities . CalculateRequestID ( common . PubkeyToHex ( & m . identity . PublicKey ) , requestToJoinResponseProto . CommunityId )
notification , err := m . persistence . GetActivityCenterNotificationByID ( requestID )
if err != nil {
return err
}
if notification != nil {
if requestToJoinResponseProto . Accepted {
notification . MembershipStatus = ActivityCenterMembershipStatusAccepted
} else {
notification . MembershipStatus = ActivityCenterMembershipStatusDeclined
}
2023-02-17 10:08:08 +00:00
err = m . addActivityCenterNotification ( state . Response , notification )
2023-01-28 09:52:53 +00:00
if err != nil {
m . logger . Warn ( "failed to update notification" , zap . Error ( err ) )
return err
2022-10-25 22:06:20 +00:00
}
}
2021-01-11 10:32:51 +00:00
return nil
}
2022-08-22 10:10:31 +00:00
func ( m * Messenger ) HandleCommunityRequestToLeave ( state * ReceivedMessageState , signer * ecdsa . PublicKey , requestToLeaveProto protobuf . CommunityRequestToLeave ) error {
if requestToLeaveProto . CommunityId == nil {
return errors . New ( "invalid community id" )
}
err := m . communitiesManager . HandleCommunityRequestToLeave ( signer , & requestToLeaveProto )
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 ] )
}
2022-10-25 22:06:20 +00:00
// Activity Center notification
notification := & ActivityCenterNotification {
ID : types . FromHex ( uuid . NewRandom ( ) . String ( ) ) ,
Type : ActivityCenterNotificationTypeCommunityKicked ,
Timestamp : m . getTimesource ( ) . GetCurrentTime ( ) ,
CommunityID : string ( requestToLeaveProto . CommunityId ) ,
}
2023-02-17 10:08:08 +00:00
err = m . addActivityCenterNotification ( state . Response , notification )
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
}
2022-08-22 10:10:31 +00:00
return nil
}
2021-01-11 10:32:51 +00:00
// handleWrappedCommunityDescriptionMessage handles a wrapped community description
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) handleWrappedCommunityDescriptionMessage ( payload [ ] byte ) ( * communities . CommunityResponse , error ) {
2020-11-18 09:16:51 +00:00
return m . communitiesManager . HandleWrappedCommunityDescriptionMessage ( payload )
}
2023-02-06 10:38:37 +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
// Check if it's already in the response
2023-02-06 10:38:37 +00:00
originalMessage := state . Response . GetMessage ( messageID )
2021-06-07 11:45:06 +00:00
// otherwise pull from database
if originalMessage == nil {
var err error
originalMessage , err = m . persistence . MessageByID ( messageID )
2021-06-08 06:07:45 +00:00
if err != nil && err != common . ErrRecordNotFound {
2021-06-07 11:45:06 +00:00
return err
}
}
// We don't have the original message, save the edited message
if originalMessage == nil {
return m . persistence . SaveEdit ( editMessage )
}
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 {
return m . persistence . SaveEdit ( editMessage )
}
// Update message and return it
err := m . applyEditMessage ( & editMessage . EditMessage , originalMessage )
if err != nil {
return err
}
2021-06-23 10:07:26 +00:00
if chat . LastMessage != nil && chat . LastMessage . ID == originalMessage . ID {
chat . LastMessage = originalMessage
err := m . saveChat ( chat )
if err != nil {
return err
}
}
2023-02-06 10:38:37 +00:00
responseTo , err := m . persistence . MessageByID ( originalMessage . ResponseTo )
if err != nil && err != common . ErrRecordNotFound {
return err
}
err = state . updateExistingActivityCenterNotification ( m . identity . PublicKey , m , originalMessage , responseTo )
if err != nil {
return err
}
state . Response . AddMessage ( originalMessage )
state . Response . AddChat ( chat )
2021-06-07 11:45:06 +00:00
2021-06-07 08:31:27 +00:00
return nil
}
2021-08-19 13:16:45 +00:00
func ( m * Messenger ) HandleDeleteMessage ( state * ReceivedMessageState , deleteMessage DeleteMessage ) error {
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 {
2022-12-02 11:34:02 +00:00
if chat . ChatType == ChatTypeCommunityChat {
fromPublicKey , err := common . HexToPubkey ( deleteMessage . From )
if err != nil {
return err
}
canDeleteMessageForEveryone = m . CanDeleteMessageForEveryone ( chat . CommunityID , fromPublicKey )
if ! canDeleteMessageForEveryone {
return ErrInvalidDeletePermission
}
}
// Check edit is valid
if ! canDeleteMessageForEveryone {
return errors . New ( "invalid delete, not the right author" )
}
2021-07-26 21:06:32 +00:00
}
// Update message and return it
originalMessage . Deleted = true
2023-02-01 00:57:35 +00:00
originalMessage . DeletedBy = deleteMessage . DeleteMessage . DeletedBy
2021-07-26 21:06:32 +00:00
err := m . persistence . SaveMessages ( [ ] * common . Message { originalMessage } )
if err != nil {
return err
}
2021-08-23 14:23:55 +00:00
m . logger . Debug ( "deleting activity center notification for message" , zap . String ( "chatID" , chat . ID ) , zap . String ( "messageID" , deleteMessage . MessageId ) )
err = m . persistence . DeleteActivityCenterNotificationForMessage ( chat . ID , deleteMessage . MessageId )
if err != nil {
m . logger . Warn ( "failed to delete notifications for deleted message" , zap . Error ( err ) )
return err
}
2021-07-26 21:06:32 +00:00
if chat . LastMessage != nil && chat . LastMessage . ID == originalMessage . ID {
2021-08-17 12:40:51 +00:00
if err := m . updateLastMessage ( chat ) ; err != nil {
2021-07-26 21:06:32 +00:00
return err
}
2022-09-12 13:39:07 +00:00
2023-01-13 17:12:46 +00:00
if chat . LastMessage != nil && ! chat . LastMessage . Seen && chat . OneToOne ( ) && ! chat . Active {
2022-09-12 13:39:07 +00:00
m . createMessageNotification ( chat , state )
}
2021-07-26 21:06:32 +00:00
}
2021-08-19 13:16:45 +00:00
2023-02-01 00:57:35 +00:00
state . Response . AddRemovedMessage ( & RemovedMessage { MessageID : messageID , ChatID : chat . ID , DeletedBy : deleteMessage . DeleteMessage . DeletedBy } )
2021-08-19 13:16:45 +00:00
state . Response . AddChat ( chat )
state . Response . AddNotification ( DeletedMessageNotification ( messageID , chat ) )
2021-07-26 21:06:32 +00:00
return nil
}
2022-09-28 11:42:17 +00:00
func ( m * Messenger ) HandleDeleteForMeMessage ( state * ReceivedMessageState , deleteForMeMessage DeleteForMeMessage ) error {
if err := ValidateDeleteForMeMessage ( deleteForMeMessage . DeleteForMeMessage ) ; err != nil {
return err
}
messageID := deleteForMeMessage . MessageId
// Check if it's already in the response
originalMessage := state . Response . GetMessage ( messageID )
// otherwise pull from database
if originalMessage == nil {
var err error
originalMessage , err = m . persistence . MessageByID ( messageID )
if err == common . ErrRecordNotFound {
return m . persistence . SaveDeleteForMe ( deleteForMeMessage )
}
if err != nil {
return err
}
}
chat , ok := m . allChats . Load ( originalMessage . LocalChatID )
if ! ok {
return errors . New ( "chat not found" )
}
// Update message and return it
originalMessage . DeletedForMe = true
err := m . persistence . SaveMessages ( [ ] * common . Message { originalMessage } )
if err != nil {
return err
}
m . logger . Debug ( "deleting activity center notification for message" , zap . String ( "chatID" , chat . ID ) , zap . String ( "messageID" , deleteForMeMessage . MessageId ) )
err = m . persistence . DeleteActivityCenterNotificationForMessage ( chat . ID , deleteForMeMessage . MessageId )
if err != nil {
m . logger . Warn ( "failed to delete notifications for deleted message" , zap . Error ( err ) )
return err
}
2022-11-17 10:11:58 +00:00
if chat . LastMessage != nil && chat . LastMessage . ID == originalMessage . ID {
if err := m . updateLastMessage ( chat ) ; err != nil {
return err
}
}
2022-09-28 11:42:17 +00:00
state . Response . AddMessage ( originalMessage )
state . Response . AddChat ( chat )
return nil
}
2021-08-17 12:40:51 +00:00
func ( m * Messenger ) updateLastMessage ( chat * Chat ) error {
// Get last message that is not hidden
2022-11-17 10:11:58 +00:00
messages , err := m . persistence . LatestMessageByChatID ( chat . ID )
2021-08-17 12:40:51 +00:00
if err != nil {
return err
}
if len ( messages ) > 0 {
chat . LastMessage = messages [ 0 ]
} else {
chat . LastMessage = nil
}
return m . saveChat ( chat )
}
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
}
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) HandleChatMessage ( state * ReceivedMessageState ) error {
2020-01-10 18:59:01 +00:00
logger := m . logger . With ( zap . String ( "site" , "handleChatMessage" ) )
2020-01-20 16:44:32 +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
}
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 )
if 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
}
}
2021-06-08 06:07:45 +00:00
chat , err := m . matchChatEntity ( receivedMessage )
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
}
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
m . updateUnviewedCounts ( chat , receivedMessage . Mentioned || receivedMessage . Replied )
2020-01-10 18:59:01 +00:00
}
2022-01-18 16:31:34 +00:00
contact := state . CurrentMessageState . Contact
2023-02-02 18:12:20 +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 {
2023-02-16 16:01:11 +00:00
if contact . hasAddedUs ( ) && ! contact . mutual ( ) {
receivedMessage . ContactRequestState = common . ContactRequestStatePending
}
2023-02-21 18:08:11 +00:00
err = m . createIncomingContactRequestNotification ( contact , state , receivedMessage , true )
2023-02-02 18:12:20 +00:00
if err != nil {
return err
}
}
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
}
2023-01-20 14:28:30 +00:00
if receivedMessage . ContentType == protobuf . ChatMessage_CONTACT_REQUEST && chat . OneToOne ( ) {
chatContact := contact
if isSyncMessage {
chatContact , err = m . BuildContact ( chat . ID )
if err != nil {
return err
}
2022-01-18 16:31:34 +00:00
}
2023-01-20 14:28:30 +00:00
sendNotification , err := handleContactRequestChatMessage ( receivedMessage , chatContact , isSyncMessage , m . logger )
2022-01-18 16:31:34 +00:00
if err != nil {
2023-01-20 14:28:30 +00:00
m . logger . Error ( "failed to handle contact request message" , zap . Error ( err ) )
2022-01-18 16:31:34 +00:00
return err
}
2023-01-20 14:28:30 +00:00
state . ModifiedContacts . Store ( chatContact . ID , true )
state . AllContacts . Store ( chatContact . ID , chatContact )
2022-01-18 16:31:34 +00:00
2023-01-20 14:28:30 +00:00
if sendNotification {
2023-02-21 18:08:11 +00:00
err = m . createIncomingContactRequestNotification ( chatContact , state , receivedMessage , true )
2023-01-20 14:28:30 +00:00
if err != nil {
return err
}
}
2022-01-18 16:31:34 +00:00
} else if receivedMessage . ContentType == protobuf . ChatMessage_COMMUNITY {
2021-10-21 17:04:56 +00:00
chat . Highlight = true
}
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
}
2022-11-17 10:11:58 +00:00
if ( receivedMessage . Deleted || receivedMessage . DeletedForMe ) && ( chat . LastMessage == nil || chat . LastMessage . ID == receivedMessage . ID ) {
2021-08-13 17:10:32 +00:00
// Get last message that is not hidden
2022-11-17 10:11:58 +00:00
messages , err := m . persistence . LatestMessageByChatID ( receivedMessage . LocalChatID )
2021-08-13 17:10:32 +00:00
if err != nil {
return err
}
2021-09-08 11:10:53 +00:00
if len ( messages ) != 0 {
2021-08-13 17:10:32 +00:00
chat . LastMessage = messages [ 0 ]
2021-09-08 11:10:53 +00:00
} else {
chat . LastMessage = nil
2021-08-13 17:10:32 +00:00
}
} else {
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 )
}
2020-11-18 09:16:51 +00:00
if receivedMessage . ContentType == protobuf . ChatMessage_COMMUNITY {
m . logger . Debug ( "Handling community content type" )
2021-01-11 10:32:51 +00:00
communityResponse , err := m . handleWrappedCommunityDescriptionMessage ( receivedMessage . GetCommunity ( ) )
2020-11-18 09:16:51 +00:00
if err != nil {
return err
}
2021-01-11 10:32:51 +00:00
community := communityResponse . Community
2020-11-18 09:16:51 +00:00
receivedMessage . CommunityID = community . IDString ( )
2021-01-11 10:32:51 +00:00
state . Response . AddCommunity ( community )
state . Response . CommunityChanges = append ( state . Response . CommunityChanges , communityResponse . Changes )
2020-11-18 09:16:51 +00:00
}
2021-04-07 12:57:14 +00:00
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
}
2022-12-26 04:35:46 +00:00
func ( m * Messenger ) addActivityCenterNotification ( response * MessengerResponse , notification * ActivityCenterNotification ) error {
2021-04-07 12:57:14 +00:00
err := m . persistence . SaveActivityCenterNotification ( notification )
if err != nil {
2023-01-28 09:52:53 +00:00
m . logger . Error ( "failed to save notification" , zap . Error ( err ) )
2021-04-07 12:57:14 +00:00
return err
}
2023-02-17 10:08:08 +00:00
state , err := m . persistence . GetActivityCenterState ( )
if err != nil {
m . logger . Error ( "failed to obtain activity center state" , zap . Error ( err ) )
return err
}
2022-12-26 04:35:46 +00:00
response . AddActivityCenterNotification ( notification )
2023-02-17 10:08:08 +00:00
response . SetActivityCenterState ( state )
2021-04-07 12:57:14 +00:00
return nil
}
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) HandleRequestAddressForTransaction ( messageState * ReceivedMessageState , command protobuf . RequestAddressForTransaction ) error {
2020-01-20 16:44:32 +00:00
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 {
2020-01-10 18:59:01 +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 )
}
2022-12-29 06:16:19 +00:00
func ( m * Messenger ) handleSyncSetting ( messageState * ReceivedMessageState , message * protobuf . SyncSetting ) 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-01-12 03:00:24 +00:00
if message . GetType ( ) == protobuf . SyncSetting_DISPLAY_NAME && settingField != nil {
2023-02-17 09:53:54 +00:00
if message . GetValueString ( ) != "" && m . account . Name != message . GetValueString ( ) {
2023-01-12 03:00:24 +00:00
m . account . Name = message . GetValueString ( )
err = m . multiAccounts . SaveAccount ( * m . account )
if err != nil {
return err
}
}
2022-12-29 06:16:19 +00:00
}
2023-01-31 13:37:41 +00:00
if settingField != nil {
messageState . Response . AddSetting ( settingField )
}
2022-12-29 06:16:19 +00:00
return nil
}
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) HandleRequestTransaction ( messageState * ReceivedMessageState , command protobuf . RequestTransaction ) error {
2020-01-20 16:44:32 +00:00
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 {
2020-01-10 18:59:01 +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 )
}
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) HandleAcceptRequestAddressForTransaction ( messageState * ReceivedMessageState , command protobuf . AcceptRequestAddressForTransaction ) error {
2020-01-20 16:44:32 +00:00
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 )
}
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) HandleSendTransaction ( messageState * ReceivedMessageState , command protobuf . SendTransaction ) error {
2020-01-20 16:44:32 +00:00
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 )
}
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) HandleDeclineRequestAddressForTransaction ( messageState * ReceivedMessageState , command protobuf . DeclineRequestAddressForTransaction ) error {
2020-01-20 16:44:32 +00:00
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 )
}
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) HandleDeclineRequestTransaction ( messageState * ReceivedMessageState , command protobuf . DeclineRequestTransaction ) error {
2020-01-20 16:44:32 +00:00
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 )
}
2021-06-08 06:07:45 +00:00
func ( m * Messenger ) matchChatEntity ( chatEntity common . ChatEntity ) ( * 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" )
}
2020-12-22 16:20:12 +00:00
var emojiReaction bool
2022-05-10 14:21:38 +00:00
var pinMessage bool
2020-12-22 16:20:12 +00:00
// We allow emoji reactions from anyone
switch chatEntity . ( type ) {
case * EmojiReaction :
emojiReaction = true
2022-05-10 14:21:38 +00:00
case * common . PinMessage :
pinMessage = true
2020-12-22 16:20:12 +00:00
}
2020-11-18 09:16:51 +00:00
canPost , err := m . communitiesManager . CanPost ( chatEntity . GetSigPubKey ( ) , chat . CommunityID , chat . CommunityChatID ( ) , chatEntity . GetGrant ( ) )
if err != nil {
return nil , err
}
2022-05-10 14:21:38 +00:00
community , err := m . communitiesManager . GetByIDString ( chat . CommunityID )
if err != nil {
return nil , err
}
isMemberAdmin := community . IsMemberAdmin ( chatEntity . GetSigPubKey ( ) )
pinMessageAllowed := community . AllowsAllMembersToPinMessage ( )
if ( pinMessage && ! isMemberAdmin && ! pinMessageAllowed ) || ( ! emojiReaction && ! canPost ) {
2020-11-18 09:16:51 +00:00
return nil , errors . New ( "user can't post" )
}
2020-01-10 18:59:01 +00:00
return chat , nil
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
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) HandleEmojiReaction ( state * ReceivedMessageState , pbEmojiR protobuf . EmojiReaction ) error {
2020-07-24 13:47:58 +00:00
logger := m . logger . With ( zap . String ( "site" , "HandleEmojiReaction" ) )
2020-07-28 07:53:32 +00:00
if err := ValidateReceivedEmojiReaction ( & pbEmojiR , state . Timesource . GetCurrentTime ( ) ) ; err != nil {
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
}
2021-06-08 06:07:45 +00:00
chat , err := m . matchChatEntity ( emojiReaction )
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
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) HandleGroupChatInvitation ( state * ReceivedMessageState , pbGHInvitations protobuf . GroupChatInvitation ) 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" ) )
if err := ValidateReceivedGroupChatInvitation ( & pbGHInvitations ) ; err != nil {
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
// HandleChatIdentity handles an incoming protobuf.ChatIdentity
// extracts contact information stored in the protobuf and adds it to the user's contact for update.
2021-06-07 12:38:13 +00:00
func ( m * Messenger ) HandleChatIdentity ( state * ReceivedMessageState , ci protobuf . ChatIdentity ) 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 )
}
}
2022-08-02 12:56:26 +00:00
clockChanged , imagesChanged , err := m . persistence . SaveContactChatIdentity ( 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
}
2022-08-02 12:56:26 +00:00
contactModified = true
}
if clockChanged {
if err = ValidateDisplayName ( & ci . DisplayName ) ; err != nil {
return err
}
if contact . DisplayName != ci . DisplayName && len ( ci . DisplayName ) != 0 {
contact . DisplayName = ci . DisplayName
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
}
2022-08-10 13:09:15 +00:00
socialLinks := identity . NewSocialLinks ( ci . SocialLinks )
if err = ValidateSocialLinks ( socialLinks ) ; err != nil {
return err
}
if ! contact . SocialLinks . Equals ( * socialLinks ) {
contact . SocialLinks = * socialLinks
2022-08-02 12:56:26 +00:00
contactModified = true
}
}
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
2021-09-01 12:02:18 +00:00
func ( m * Messenger ) HandleAnonymousMetricBatch ( amb protobuf . AnonymousMetricBatch ) error {
// 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
err := m . applyEditMessage ( & e . EditMessage , message )
if err != nil {
return err
}
return nil
}
}
2021-06-07 08:31:27 +00:00
return nil
}
2021-07-26 21:06:32 +00:00
func ( m * Messenger ) checkForDeletes ( message * common . Message ) error {
// Check for any pending deletes
// If any pending deletes are available and valid, apply them
messageDeletes , err := m . persistence . GetDeletes ( message . ID , message . From )
if err != nil {
return err
}
if len ( messageDeletes ) == 0 {
return nil
}
return m . applyDeleteMessage ( messageDeletes , message )
}
2022-09-28 11:42:17 +00:00
func ( m * Messenger ) checkForDeleteForMes ( message * common . Message ) error {
// Check for any pending delete for mes
// If any pending deletes are available and valid, apply them
messageDeleteForMes , err := m . persistence . GetDeleteForMes ( message . ID , message . From )
if err != nil {
return err
}
if len ( messageDeleteForMes ) == 0 {
return nil
}
return m . applyDeleteForMeMessage ( messageDeleteForMes , message )
}
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
}
// If the chat is active, we allow it
if chat != nil && chat . Active {
return true , nil
}
// If the chat is public, we allow it
if chat != nil && chat . Public ( ) {
return true , nil
}
2021-06-08 06:07:45 +00:00
contact , ok := m . allContacts . Load ( publicKey )
2021-04-07 12:57:14 +00:00
if ! ok {
// 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-01-10 17:59:32 +00:00
func ( m * Messenger ) updateUnviewedCounts ( chat * Chat , mentionedOrReplied bool ) {
2021-05-26 06:38:25 +00:00
chat . UnviewedMessagesCount ++
2023-01-10 17:59:32 +00:00
if mentionedOrReplied {
2021-05-26 06:38:25 +00:00
chat . UnviewedMentionsCount ++
}
}
2022-05-18 10:42:51 +00:00
func ( m * Messenger ) HandleSyncWalletAccount ( state * ReceivedMessageState , message protobuf . SyncWalletAccounts ) error {
dbAccounts , err := m . settings . GetAccounts ( )
if err != nil {
return err
}
dbAccountMap := make ( map [ types . Address ] * accounts . Account )
for _ , acc := range dbAccounts {
dbAccountMap [ acc . Address ] = acc
}
var accs [ ] * accounts . Account
for _ , message := range message . Accounts {
dbAcc := dbAccountMap [ types . BytesToAddress ( message . Address ) ]
if dbAcc != nil && message . Clock <= dbAcc . Clock {
continue
}
var acc * accounts . Account
if dbAcc != nil && message . Removed {
acc = & accounts . Account {
Address : types . BytesToAddress ( message . Address ) ,
Removed : true ,
}
} else if ! message . Removed {
acc = & accounts . Account {
Address : types . BytesToAddress ( message . Address ) ,
Wallet : message . Wallet ,
Chat : message . Chat ,
2022-08-25 15:09:08 +00:00
Type : accounts . AccountType ( message . Type ) ,
2022-05-18 10:42:51 +00:00
Storage : message . Storage ,
PublicKey : types . HexBytes ( message . PublicKey ) ,
Path : message . Path ,
Color : message . Color ,
Hidden : message . Hidden ,
Name : message . Name ,
Clock : message . Clock ,
}
}
accs = append ( accs , acc )
}
if len ( accs ) == 0 {
return nil
}
err = m . settings . SaveAccounts ( accs )
if err != nil {
return err
}
2022-07-06 16:12:49 +00:00
latestDerivedPath , err := m . settings . GetLatestDerivedPath ( )
if err != nil {
return err
}
newPath := latestDerivedPath + uint ( len ( accs ) )
err = m . settings . SaveSettingField ( settings . LatestDerivedPath , newPath )
if err != nil {
return err
}
2022-05-18 10:42:51 +00:00
if err == nil {
state . Response . Accounts = accs
2022-08-03 09:22:50 +00:00
if state . Response . Settings == nil {
state . Response . Settings = [ ] * settings . SyncSettingField { }
}
state . Response . Settings = append (
state . Response . Settings ,
& settings . SyncSettingField {
SettingField : settings . LatestDerivedPath ,
Value : newPath ,
} )
2022-05-18 10:42:51 +00:00
}
return err
}
2022-08-07 14:25:03 +00:00
func ( m * Messenger ) HandleSyncContactRequestDecision ( state * ReceivedMessageState , message protobuf . SyncContactRequestDecision ) error {
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 {
response , err = m . updateAcceptedContactRequest ( nil , message . RequestId )
} else {
2023-02-21 18:08:11 +00:00
response , err = m . declineContactRequest ( message . RequestId , true )
2022-08-07 14:25:03 +00:00
}
if err != nil {
return err
}
state . Response = response
return nil
}