2019-11-21 16:19:22 +00:00
package protocol
2019-10-14 14:10:48 +00:00
import (
2021-07-27 12:43:46 +00:00
"context"
2020-01-10 18:59:01 +00:00
"crypto/ecdsa"
2023-01-10 17:59:32 +00:00
"database/sql"
2020-01-10 18:59:01 +00:00
"encoding/hex"
2020-02-10 11:22:37 +00:00
"fmt"
2022-12-12 09:22:37 +00:00
"sync"
2023-04-16 15:06:00 +00:00
"time"
2021-03-05 14:38:32 +00:00
2023-04-06 14:06:24 +00:00
"github.com/status-im/status-go/signal"
2019-10-14 14:10:48 +00:00
"github.com/pkg/errors"
2021-05-14 21:22:50 +00:00
"go.uber.org/zap"
2023-06-08 12:00:19 +00:00
"github.com/google/uuid"
2020-01-10 18:59:01 +00:00
"github.com/status-im/status-go/eth-node/crypto"
2020-08-07 13:49:37 +00:00
"github.com/status-im/status-go/eth-node/types"
2020-11-25 00:34:32 +00:00
"github.com/status-im/status-go/images"
2022-05-18 10:42:51 +00:00
"github.com/status-im/status-go/multiaccounts/accounts"
2023-06-05 07:12:15 +00:00
multiaccountscommon "github.com/status-im/status-go/multiaccounts/common"
2022-03-23 18:47:00 +00:00
"github.com/status-im/status-go/multiaccounts/settings"
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"
)
2023-04-19 14:44:57 +00:00
var (
ErrMessageNotAllowed = errors . New ( "message from a non-contact" )
ErrMessageForWrongChatType = errors . New ( "message for the wrong chat type" )
2023-06-28 19:45:36 +00:00
ErrNotWatchOnlyAccount = errors . New ( "an account is not a watch only account" )
2023-04-19 14:44:57 +00:00
ErrWalletAccountNotSupportedForMobileApp = errors . New ( "handling account is not supported for mobile app" )
2023-07-16 11:11:48 +00:00
ErrTryingToApplyOldWalletAccountsOrder = errors . New ( "trying to apply old wallet accounts order" )
2023-04-19 14:44:57 +00:00
ErrTryingToStoreOldWalletAccount = errors . New ( "trying to store an old wallet account" )
2023-06-28 19:45:36 +00:00
ErrTryingToStoreOldKeypair = errors . New ( "trying to store an old keypair" )
2023-04-19 14:44:57 +00:00
ErrSomeFieldsMissingForWalletAccount = errors . New ( "some fields are missing for wallet account" )
2023-05-16 10:48:00 +00:00
ErrUnknownKeypairForWalletAccount = errors . New ( "keypair is not known for the wallet account" )
2023-07-10 14:11:37 +00:00
ErrInvalidCommunityID = errors . New ( "invalid community id" )
2023-04-19 14:44:57 +00:00
)
2021-04-07 12:57:14 +00:00
2019-10-14 14:10:48 +00:00
// HandleMembershipUpdate updates a Chat instance according to the membership updates.
// It retrieves chat, if exists, and merges membership updates from the message.
// Finally, the Chat is updated with the new group events.
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
// Reactivate deleted group chat on re-invite from contact
2023-05-30 13:49:46 +00:00
chat . Active = chat . Active || ( isActive && group . IsMember ( ourKey ) )
wasUserAdded = ! existingGroup . IsMember ( ourKey ) && group . IsMember ( ourKey )
2021-11-19 14:32:04 +00:00
// Show push notifications when our key is added to members list and chat is Active
showPushNotification = showPushNotification && wasUserAdded
2020-01-10 18:59:01 +00:00
}
2023-01-10 17:47:10 +00:00
maxClockVal := uint64 ( 0 )
for _ , event := range group . Events ( ) {
if event . ClockValue > maxClockVal {
maxClockVal = event . ClockValue
}
}
if chat . LastClockValue < maxClockVal {
chat . LastClockValue = maxClockVal
}
2020-01-10 18:59:01 +00:00
2021-11-19 14:32:04 +00:00
// Only create a message notification when the user is added, not when removed
if ! chat . Active && wasUserAdded {
chat . Highlight = true
2023-05-08 17:02:54 +00:00
m . createMessageNotification ( chat , messageState , chat . LastMessage )
2021-04-07 12:57:14 +00:00
}
2020-04-22 12:58:28 +00:00
2021-11-19 14:32:04 +00:00
profilePicturesVisibility , err := m . settings . GetProfilePicturesVisibility ( )
if err != nil {
return errors . Wrap ( err , "failed to get profilePicturesVisibility setting" )
}
if showPushNotification {
// chat is highlighted for new group invites or group re-invites
chat . Highlight = true
messageState . Response . AddNotification ( NewPrivateGroupInviteNotification ( chat . ID , chat , messageState . CurrentMessageState . Contact , profilePicturesVisibility ) )
}
2020-01-10 18:59:01 +00:00
systemMessages := buildSystemMessages ( message . Events , translations )
for _ , message := range systemMessages {
messageID := message . ID
exists , err := m . messageExists ( messageID , messageState . ExistingMessagesMap )
if err != nil {
m . logger . Warn ( "failed to check message exists" , zap . Error ( err ) )
}
if exists {
continue
}
2021-06-03 13:11:55 +00:00
messageState . Response . AddMessage ( message )
2020-01-10 18:59:01 +00:00
}
2021-04-07 12:57:14 +00:00
messageState . Response . AddChat ( chat )
2020-01-10 18:59:01 +00:00
// Store in chats map as it might be a new one
2021-03-29 15:41:30 +00:00
messageState . AllChats . Store ( chat . ID , chat )
2020-01-10 18:59:01 +00:00
2022-05-20 10:53:28 +00:00
// explicit join has been removed, mimic auto-join for backward compatibility
// no all cases are covered, e.g. if added to a group by non-contact
autoJoin := chat . Active && wasUserAdded
if autoJoin || waitingForApproval {
2021-07-27 12:43:46 +00:00
_ , err = m . ConfirmJoiningGroup ( context . Background ( ) , chat . ID )
if err != nil {
return err
}
}
2020-07-26 21:37:04 +00:00
if message . Message != nil {
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
}
2023-05-08 17:02:54 +00:00
func ( m * Messenger ) createMessageNotification ( chat * Chat , messageState * ReceivedMessageState , message * common . Message ) {
2021-05-26 06:38:25 +00:00
var notificationType ActivityCenterType
if chat . OneToOne ( ) {
notificationType = ActivityCenterNotificationTypeNewOneToOne
} else {
notificationType = ActivityCenterNotificationTypeNewPrivateGroupChat
}
notification := & ActivityCenterNotification {
ID : types . FromHex ( chat . ID ) ,
Name : chat . Name ,
2023-05-08 17:02:54 +00:00
LastMessage : message ,
2021-05-26 06:38:25 +00:00
Type : notificationType ,
2021-06-17 15:08:28 +00:00
Author : messageState . CurrentMessageState . Contact . ID ,
2021-05-26 06:38:25 +00:00
Timestamp : messageState . CurrentMessageState . WhisperTimestamp ,
ChatID : chat . ID ,
2022-10-25 22:06:20 +00:00
CommunityID : chat . CommunityID ,
2023-06-10 02:00:17 +00:00
UpdatedAt : m . getCurrentTimeInMillis ( ) ,
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-04-25 11:27:15 +00:00
func ( m * Messenger ) createContactRequestForContactUpdate ( contact * Contact , messageState * ReceivedMessageState ) ( * common . Message , error ) {
contactRequest , err := m . generateContactRequest (
messageState . CurrentMessageState . Message . Clock ,
messageState . CurrentMessageState . WhisperTimestamp ,
contact ,
2023-05-22 12:22:33 +00:00
defaultContactRequestText ( ) ,
2023-04-25 11:27:15 +00:00
false ,
)
if err != nil {
return nil , err
}
contactRequest . ID = defaultContactRequestID ( contact . ID )
// save this message
messageState . Response . AddMessage ( contactRequest )
err = m . persistence . SaveMessages ( [ ] * common . Message { contactRequest } )
if err != nil {
return nil , err
}
return contactRequest , nil
}
2023-02-21 18:08:11 +00:00
func ( m * Messenger ) createIncomingContactRequestNotification ( contact * Contact , messageState * ReceivedMessageState , contactRequest * common . Message , createNewNotification bool ) error {
2023-04-25 11:27:15 +00:00
if contactRequest . ContactRequestState == common . ContactRequestStateAccepted {
2023-02-21 18:08:11 +00:00
// Pull one from the db if there
notification , err := m . persistence . GetActivityCenterNotificationByID ( types . FromHex ( contactRequest . ID ) )
if err != nil {
return err
}
if notification != nil {
2023-04-25 11:27:15 +00:00
notification . Name = contact . PrimaryName ( )
2023-02-21 18:08:11 +00:00
notification . Message = contactRequest
notification . Read = true
notification . Accepted = true
notification . Dismissed = false
2023-06-10 02:00:17 +00:00
notification . UpdatedAt = m . getCurrentTimeInMillis ( )
_ , err = m . persistence . SaveActivityCenterNotification ( notification , true )
2023-02-21 18:08:11 +00:00
if err != nil {
return err
}
messageState . Response . AddMessage ( contactRequest )
messageState . Response . AddActivityCenterNotification ( notification )
2023-06-10 02:00:17 +00:00
err = m . syncActivityCenterNotifications ( [ ] * ActivityCenterNotification { notification } )
if err != nil {
m . logger . Error ( "createIncomingContactRequestNotification, failed to sync activity center notifications" , zap . Error ( err ) )
return err
}
2023-02-21 18:08:11 +00:00
}
return nil
}
2022-01-18 16:31:34 +00:00
2023-02-13 20:09:20 +00:00
if ! createNewNotification {
return nil
}
2022-01-18 16:31:34 +00:00
notification := & ActivityCenterNotification {
ID : types . FromHex ( contactRequest . ID ) ,
2023-02-22 16:57:33 +00:00
Name : contact . PrimaryName ( ) ,
2022-01-18 16:31:34 +00:00
Message : contactRequest ,
Type : ActivityCenterNotificationTypeContactRequest ,
2023-05-22 12:22:33 +00:00
Author : contactRequest . From ,
Timestamp : contactRequest . WhisperTimestamp ,
2022-01-18 16:31:34 +00:00
ChatID : contact . ID ,
2023-01-28 09:52:53 +00:00
Read : contactRequest . ContactRequestState == common . ContactRequestStateAccepted || contactRequest . ContactRequestState == common . ContactRequestStateDismissed ,
Accepted : contactRequest . ContactRequestState == common . ContactRequestStateAccepted ,
Dismissed : contactRequest . ContactRequestState == common . ContactRequestStateDismissed ,
2023-06-10 02:00:17 +00:00
UpdatedAt : m . getCurrentTimeInMillis ( ) ,
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 {
2023-05-08 17:02:54 +00:00
m . createMessageNotification ( chat , state , chat . LastMessage )
2021-04-07 12:57:14 +00:00
}
2020-01-10 18:59:01 +00:00
// Add to response
2021-04-07 12:57:14 +00:00
state . Response . AddChat ( chat )
2020-01-10 18:59:01 +00:00
if message != nil {
2021-06-25 08:30:18 +00:00
message . New = true
2021-06-03 13:11:55 +00:00
state . Response . AddMessage ( message )
2020-01-10 18:59:01 +00:00
}
2021-04-07 12:57:14 +00:00
// Set in the modified maps chat
state . AllChats . Store ( chat . ID , chat )
2020-01-10 18:59:01 +00:00
return nil
}
2023-05-22 12:22:33 +00:00
func ( m * Messenger ) syncContactRequestForInstallationContact ( contact * Contact , state * ReceivedMessageState , chat * Chat , outgoing bool ) error {
2023-07-12 09:46:56 +00:00
2023-05-22 12:22:33 +00:00
if chat == nil {
return fmt . Errorf ( "no chat restored during the contact synchronisation, contact.ID = %s" , contact . ID )
}
contactRequestID , err := m . persistence . LatestPendingContactRequestIDForContact ( contact . ID )
if err != nil {
return err
}
if contactRequestID != "" {
2023-07-12 09:46:56 +00:00
m . logger . Warn ( "syncContactRequestForInstallationContact: skipping as contact request found" , zap . String ( "contactRequestID" , contactRequestID ) )
2023-05-22 12:22:33 +00:00
return nil
}
clock , timestamp := chat . NextClockAndTimestamp ( m . transport )
contactRequest , err := m . generateContactRequest ( clock , timestamp , contact , defaultContactRequestText ( ) , outgoing )
if err != nil {
return err
}
contactRequest . ID = defaultContactRequestID ( contact . ID )
state . Response . AddMessage ( contactRequest )
err = m . persistence . SaveMessages ( [ ] * common . Message { contactRequest } )
if err != nil {
return err
}
if outgoing {
notification := m . generateOutgoingContactRequestNotification ( contact , contactRequest )
err = m . addActivityCenterNotification ( state . Response , notification )
if err != nil {
return err
}
} else {
2023-07-12 15:12:58 +00:00
err = m . createIncomingContactRequestNotification ( contact , state , contactRequest , true )
2023-05-22 12:22:33 +00:00
if err != nil {
return err
}
}
return nil
}
2021-09-03 08:26:05 +00:00
func ( m * Messenger ) HandleSyncInstallationContact ( state * ReceivedMessageState , message protobuf . SyncInstallationContactV2 ) error {
2023-05-22 12:22:33 +00:00
// Ignore own contact installation
2023-07-12 09:46:56 +00:00
2023-05-22 12:22:33 +00:00
if message . Id == m . myHexIdentity ( ) {
2023-07-12 09:46:56 +00:00
m . logger . Warn ( "HandleSyncInstallationContact: skipping own contact" )
2023-05-22 12:22:33 +00:00
return nil
}
2023-08-04 10:41:24 +00:00
removed := message . Removed && ! message . Blocked
2021-09-03 08:26:05 +00:00
chat , ok := state . AllChats . Load ( message . Id )
2023-08-04 10:41:24 +00:00
if ! ok && ( message . Added || message . HasAddedUs || message . Muted ) && ! removed {
2021-09-03 08:26:05 +00:00
pubKey , err := common . HexToPubkey ( message . Id )
if err != nil {
2021-11-05 15:11:10 +00:00
return err
2021-09-03 08:26:05 +00:00
}
chat = OneToOneFromPublicKey ( pubKey , state . Timesource )
2020-01-10 18:59:01 +00:00
// We don't want to show the chat to the user
chat . Active = false
}
2023-08-04 10:41:24 +00:00
contact , contactFound := state . AllContacts . Load ( message . Id )
if ! contactFound {
if message . Removed && ! message . Blocked {
2021-09-03 08:26:05 +00:00
// Nothing to do in case if contact doesn't exist
return nil
}
2020-12-22 10:49:25 +00:00
var err error
contact , err = buildContactFromPkString ( message . Id )
2020-01-10 18:59:01 +00:00
if err != nil {
return err
}
}
2023-01-20 14:28:30 +00:00
if message . ContactRequestRemoteClock != 0 || message . ContactRequestLocalClock != 0 {
// Some local action about contact requests were performed,
// process them
2023-02-17 10:02:03 +00:00
contact . ProcessSyncContactRequestState (
2023-01-20 14:28:30 +00:00
ContactRequestState ( message . ContactRequestRemoteState ) ,
uint64 ( message . ContactRequestRemoteClock ) ,
ContactRequestState ( message . ContactRequestLocalState ) ,
uint64 ( message . ContactRequestLocalClock ) )
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
2023-05-22 12:22:33 +00:00
err := m . syncContactRequestForInstallationContact ( contact , state , chat , contact . ContactRequestLocalState == ContactRequestStateSent )
if err != nil {
return err
}
2023-01-20 14:28:30 +00:00
} else if message . Added || message . HasAddedUs {
// NOTE(cammellos): this is for handling backward compatibility, old clients
// won't propagate ContactRequestRemoteClock or ContactRequestLocalClock
if message . Added && contact . LastUpdatedLocally < message . LastUpdatedLocally {
contact . ContactRequestSent ( message . LastUpdatedLocally )
2023-05-22 12:22:33 +00:00
err := m . syncContactRequestForInstallationContact ( contact , state , chat , true )
if err != nil {
return err
}
2023-01-20 14:28:30 +00:00
}
if message . HasAddedUs && contact . LastUpdated < message . LastUpdated {
contact . ContactRequestReceived ( message . LastUpdated )
2023-05-22 12:22:33 +00:00
err := m . syncContactRequestForInstallationContact ( contact , state , chat , false )
if err != nil {
return err
}
2023-01-20 14:28:30 +00:00
}
if message . Removed && contact . LastUpdatedLocally < message . LastUpdatedLocally {
err := m . removeContact ( context . Background ( ) , state . Response , contact . ID , false )
if err != nil {
return err
}
}
2021-10-22 14:20:42 +00:00
}
2023-02-14 17:44:00 +00:00
// Sync last updated field
// We don't set `LastUpdated`, since that would cause some issues
// as `LastUpdated` tracks both display name & picture.
// The case where it would break is as follow:
// 1) User A pairs A1 with device A2.
// 2) User B publishes display name and picture with LastUpdated = 3.
// 3) Device A1 receives message from step 2.
// 4) Device A1 syncs with A2 (which has not received message from step 3).
// 5) Device A2 saves Display name and sets LastUpdated = 3,
// note that picture has not been set as it's not synced.
// 6) Device A2 receives the message from 2. because LastUpdated is 3
// it will be discarded, A2 will not have B's picture.
// The correct solution is to either sync profile image (expensive)
// or split the clock for image/display name, so they can be synced
// independently.
2023-08-04 10:41:24 +00:00
if ! contactFound || ( contact . LastUpdated < message . LastUpdated ) {
2023-02-14 17:44:00 +00:00
if message . DisplayName != "" {
contact . DisplayName = message . DisplayName
}
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 )
2023-08-04 10:41:24 +00:00
response , err := m . BlockContact ( contact . ID , true )
2021-12-06 12:44:40 +00:00
if err != nil {
return err
}
err = state . Response . Merge ( response )
2021-09-03 08:26:05 +00:00
if err != nil {
return err
}
} else {
2023-01-20 14:28:30 +00:00
contact . Unblock ( message . LastUpdatedLocally )
2021-09-03 08:26:05 +00:00
}
}
if chat != nil && message . Muted != chat . Muted {
if message . Muted {
2023-04-16 15:06:00 +00:00
_ , err := m . muteChat ( chat , contact , time . Time { } )
2021-09-03 08:26:05 +00:00
if err != nil {
return err
}
} else {
err := m . unmuteChat ( chat , contact )
if err != nil {
return err
}
}
state . Response . AddChat ( chat )
}
2021-03-29 15:41:30 +00:00
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
2020-01-10 18:59:01 +00:00
}
2021-09-03 08:26:05 +00:00
if chat != nil {
state . AllChats . Store ( chat . ID , chat )
}
2020-01-10 18:59:01 +00:00
return nil
}
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
}
2023-04-17 14:44:48 +00:00
func ( m * Messenger ) handlePinMessage ( pinner * Contact , whisperTimestamp uint64 , response * MessengerResponse , 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" )
2023-04-17 14:44:48 +00:00
publicKey , err := pinner . PublicKey ( )
if err != nil {
return err
}
2021-05-14 21:22:50 +00:00
pinMessage := & common . PinMessage {
PinMessage : message ,
// MessageID: message.MessageId,
2023-04-17 14:44:48 +00:00
WhisperTimestamp : whisperTimestamp ,
From : pinner . ID ,
SigPubKey : publicKey ,
Identicon : pinner . Identicon ,
Alias : pinner . Alias ,
2021-05-14 21:22:50 +00:00
}
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
2023-04-17 14:44:48 +00:00
if c , ok := m . allChats . Load ( chat . ID ) ; ok {
2021-05-14 21:22:50 +00:00
chat = c
}
// Set the LocalChatID for the message
pinMessage . LocalChatID = chat . ID
2023-04-17 14:44:48 +00:00
inserted , err := m . persistence . SavePinMessage ( pinMessage )
if err != nil {
return err
}
// Nothing to do, returning
if ! inserted {
m . logger . Info ( "pin message already processed" )
return nil
}
if message . Pinned {
id , err := generatePinMessageNotificationID ( & m . identity . PublicKey , pinMessage , chat )
if err != nil {
return err
}
message := & common . Message {
ChatMessage : protobuf . ChatMessage {
Clock : message . Clock ,
Timestamp : whisperTimestamp ,
ChatId : chat . ID ,
MessageType : message . MessageType ,
ResponseTo : message . MessageId ,
ContentType : protobuf . ChatMessage_SYSTEM_MESSAGE_PINNED_MESSAGE ,
} ,
WhisperTimestamp : whisperTimestamp ,
ID : id ,
LocalChatID : chat . ID ,
From : pinner . ID ,
}
response . AddMessage ( message )
2023-05-05 15:26:19 +00:00
chat . UnviewedMessagesCount ++
2023-04-17 14:44:48 +00:00
}
2021-05-14 21:22:50 +00:00
if chat . LastClockValue < message . Clock {
chat . LastClockValue = message . Clock
}
2023-04-17 14:44:48 +00:00
response . AddPinMessage ( pinMessage )
2021-05-14 21:22:50 +00:00
// Set in the modified maps chat
2023-04-17 14:44:48 +00:00
response . AddChat ( chat )
m . allChats . Store ( chat . ID , chat )
2021-05-14 21:22:50 +00:00
return nil
}
2023-04-17 14:44:48 +00:00
func ( m * Messenger ) HandlePinMessage ( state * ReceivedMessageState , message protobuf . PinMessage ) error {
return m . handlePinMessage ( state . CurrentMessageState . Contact , state . CurrentMessageState . WhisperTimestamp , state . Response , message )
}
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
2023-04-25 11:27:15 +00:00
if contact . mutual ( ) {
2023-01-20 14:28:30 +00:00
// We set the chat as active, this is currently the expected behavior
// for mobile, it might change as we implement further the activity
// center
chat , _ , err := m . getOneToOneAndNextClock ( contact )
if err != nil {
return err
}
2022-01-18 16:31:34 +00:00
2023-02-21 18:08:11 +00:00
if chat . LastClockValue < clock {
chat . LastClockValue = clock
2023-01-20 14:28:30 +00:00
}
// NOTE(cammellos): This will re-enable the chat if it was deleted, and only
// after we became contact, currently seems safe, but that needs
// discussing with UX.
2023-02-21 18:08:11 +00:00
if chat . DeletedAtClockValue < clock {
2023-01-20 14:28:30 +00:00
chat . Active = true
}
2023-07-12 15:12:58 +00:00
// Add mutual state update message for incoming contact request
clock , timestamp := chat . NextClockAndTimestamp ( m . transport )
updateMessage , err := m . prepareMutualStateUpdateMessage ( contact . ID , MutualStateUpdateTypeAdded , clock , timestamp , false )
if err != nil {
return err
}
m . prepareMessage ( updateMessage , m . httpServer )
err = m . persistence . SaveMessages ( [ ] * common . Message { updateMessage } )
if err != nil {
return err
}
state . Response . AddMessage ( updateMessage )
2023-01-20 14:28:30 +00:00
state . Response . AddChat ( chat )
state . AllChats . Store ( chat . ID , chat )
}
2023-02-21 18:08:11 +00:00
if request != nil {
if isOutgoing {
notification := m . generateOutgoingContactRequestNotification ( contact , request )
err = m . addActivityCenterNotification ( state . Response , notification )
if err != nil {
return err
}
} else {
2023-07-12 15:12:58 +00:00
err = m . createIncomingContactRequestNotification ( contact , state , request , processingResponse . newContactRequestReceived )
2023-06-08 12:00:19 +00:00
if err != nil {
return err
}
2023-02-21 18:08:11 +00:00
}
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 {
2023-04-25 11:27:15 +00:00
err := m . handleAcceptContactRequestMessage ( state , message . Clock , message . Id , false )
2023-02-21 18:08:11 +00:00
if err != nil {
m . logger . Warn ( "could not accept contact request" , zap . Error ( err ) )
}
return nil
}
2023-06-08 12:00:19 +00:00
func ( m * Messenger ) handleRetractContactRequest ( response * MessengerResponse , contact * Contact , message protobuf . RetractContactRequest ) error {
2023-01-20 14:28:30 +00:00
if contact . ID == m . myHexIdentity ( ) {
m . logger . Debug ( "retraction coming from us, ignoring" )
2022-01-18 16:31:34 +00:00
return nil
}
2023-02-02 18:12:20 +00:00
m . logger . Debug ( "handling retracted contact request" , zap . Uint64 ( "clock" , message . Clock ) )
2023-02-17 10:02:03 +00:00
r := contact . ContactRequestRetracted ( message . Clock , false )
2023-01-20 14:28:30 +00:00
if ! r . processed {
2023-02-08 14:58:10 +00:00
m . logger . Debug ( "not handling retract since clock lower" )
2023-01-20 14:28:30 +00:00
return nil
2022-06-08 16:18:56 +00:00
}
2022-06-13 10:57:51 +00:00
2023-06-08 12:00:19 +00:00
// System message for mutual state update
chat , clock , err := m . getOneToOneAndNextClock ( contact )
if err != nil {
return err
}
timestamp := m . getTimesource ( ) . GetCurrentTime ( )
updateMessage , err := m . prepareMutualStateUpdateMessage ( contact . ID , MutualStateUpdateTypeRemoved , clock , timestamp , false )
2022-06-13 10:57:51 +00:00
if err != nil {
return err
2022-06-08 16:18:56 +00:00
}
2022-06-13 10:57:51 +00:00
2023-06-08 12:00:19 +00:00
m . prepareMessage ( updateMessage , m . httpServer )
err = m . persistence . SaveMessages ( [ ] * common . Message { updateMessage } )
if err != nil {
return err
}
response . AddMessage ( updateMessage )
response . AddChat ( chat )
notification := & ActivityCenterNotification {
ID : types . FromHex ( uuid . New ( ) . String ( ) ) ,
Type : ActivityCenterNotificationTypeContactRemoved ,
Name : contact . PrimaryName ( ) ,
Author : contact . ID ,
Timestamp : m . getTimesource ( ) . GetCurrentTime ( ) ,
ChatID : contact . ID ,
Read : false ,
2023-06-10 02:00:17 +00:00
UpdatedAt : m . getCurrentTimeInMillis ( ) ,
2023-06-08 12:00:19 +00:00
}
err = m . addActivityCenterNotification ( response , notification )
if err != nil {
m . logger . Warn ( "failed to create activity center notification" , zap . Error ( err ) )
return err
}
2023-02-02 18:12:20 +00:00
m . allContacts . Store ( contact . ID , contact )
2022-01-18 16:31:34 +00:00
2023-02-02 18:12:20 +00:00
return nil
}
func ( m * Messenger ) HandleRetractContactRequest ( state * ReceivedMessageState , message protobuf . RetractContactRequest ) error {
contact := state . CurrentMessageState . Contact
2023-06-08 12:00:19 +00:00
err := m . handleRetractContactRequest ( state . Response , contact , message )
2023-02-02 18:12:20 +00:00
if err != nil {
return err
}
2023-02-08 14:58:10 +00:00
if contact . ID != m . myHexIdentity ( ) {
state . ModifiedContacts . Store ( contact . ID , true )
}
2022-01-18 16:31:34 +00:00
return nil
}
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-04-25 11:27:15 +00:00
contactRequest , err := m . createContactRequestForContactUpdate ( contact , state )
2023-02-02 18:12:20 +00:00
if err != nil {
return err
}
2023-07-12 15:12:58 +00:00
err = m . createIncomingContactRequestNotification ( contact , state , contactRequest , true )
2023-04-25 11:27:15 +00:00
if err != nil {
return err
}
2023-02-02 18:12:20 +00:00
}
2023-04-25 11:27:15 +00:00
2023-02-08 14:58:10 +00:00
logger . Debug ( "handled propagated state" , zap . Any ( "state after update" , contact . ContactRequestPropagatedState ( ) ) )
2023-02-02 18:12:20 +00:00
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
}
2020-01-10 18:59:01 +00:00
if contact . LastUpdated < message . Clock {
2022-02-17 15:13:10 +00:00
if contact . EnsName != message . EnsName {
contact . EnsName = message . EnsName
2020-01-10 18:59:01 +00:00
contact . ENSVerified = false
}
2022-02-17 15:13:10 +00:00
if len ( message . DisplayName ) != 0 {
contact . DisplayName = message . DisplayName
}
2023-01-20 14:28:30 +00:00
r := contact . ContactRequestReceived ( message . ContactRequestClock )
if r . newContactRequestReceived {
2023-07-12 15:12:58 +00:00
err = m . createIncomingContactRequestNotification ( contact , state , nil , true )
2022-01-18 16:31:34 +00:00
if err != nil {
2023-06-08 12:00:19 +00:00
return err
2022-01-18 16:31:34 +00:00
}
2022-06-13 10:57:51 +00:00
}
2023-01-20 14:28:30 +00:00
contact . LastUpdated = message . Clock
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
2020-01-10 18:59:01 +00:00
}
if chat . LastClockValue < message . Clock {
chat . LastClockValue = message . Clock
}
2023-01-20 14:28:30 +00:00
if contact . mutual ( ) && chat . DeletedAtClockValue < message . Clock {
2022-08-26 16:50:41 +00:00
chat . Active = true
}
2021-01-11 10:32:51 +00:00
state . Response . AddChat ( chat )
2021-03-29 15:41:30 +00:00
// TODO(samyoul) remove storing of an updated reference pointer?
state . AllChats . Store ( chat . ID , chat )
2020-01-10 18:59:01 +00:00
return nil
}
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
}
2022-04-22 07:42:22 +00:00
func ( m * Messenger ) HandleHistoryArchiveMagnetlinkMessage ( state * ReceivedMessageState , communityPubKey * ecdsa . PublicKey , magnetlink string , clock uint64 ) error {
id := types . HexBytes ( crypto . CompressPubkey ( communityPubKey ) )
2023-07-06 17:44:31 +00:00
community , err := m . communitiesManager . GetByID ( id )
if err != nil {
m . logger . Debug ( "Couldn't get community for community with id: " , zap . Any ( "id" , id ) )
return err
}
if community == nil {
return nil
}
2022-04-22 07:42:22 +00:00
settings , err := m . communitiesManager . GetCommunitySettingsByID ( id )
if err != nil {
m . logger . Debug ( "Couldn't get community settings for community with id: " , zap . Any ( "id" , id ) )
return err
}
2023-07-06 17:44:31 +00:00
if settings == nil {
return nil
}
2022-04-22 07:42:22 +00:00
2023-07-06 17:44:31 +00:00
if m . torrentClientReady ( ) && settings . HistoryArchiveSupportEnabled {
2022-04-22 07:42:22 +00:00
lastClock , err := m . communitiesManager . GetMagnetlinkMessageClock ( id )
if err != nil {
return err
}
2022-12-19 08:34:37 +00:00
lastSeenMagnetlink , err := m . communitiesManager . GetLastSeenMagnetlink ( id )
if err != nil {
return err
}
2022-04-22 07:42:22 +00:00
// We are only interested in a community archive magnet link
// if it originates from a community that the current account is
// part of and doesn't own the private key at the same time
2023-07-06 17:44:31 +00:00
if ! community . IsControlNode ( ) && community . Joined ( ) && clock >= lastClock {
2022-12-19 08:34:37 +00:00
if lastSeenMagnetlink == magnetlink {
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-07-07 08:39:52 +00:00
func ( m * Messenger ) handleArchiveMessages ( archiveMessages [ ] * protobuf . WakuMessage ) ( * MessengerResponse , error ) {
2023-01-13 15:40:39 +00:00
messagesToHandle := make ( map [ transport . Filter ] [ ] * types . Message )
for _ , message := range archiveMessages {
filter := m . transport . FilterByTopic ( message . Topic )
if filter != nil {
shhMessage := & types . Message {
Sig : message . Sig ,
Timestamp : uint32 ( message . Timestamp ) ,
Topic : types . BytesToTopic ( message . Topic ) ,
Payload : message . Payload ,
Padding : message . Padding ,
Hash : message . Hash ,
ThirdPartyID : message . ThirdPartyId ,
}
messagesToHandle [ * filter ] = append ( messagesToHandle [ * filter ] , shhMessage )
}
}
importedMessages := make ( map [ transport . Filter ] [ ] * types . Message , 0 )
otherMessages := make ( map [ transport . Filter ] [ ] * types . Message , 0 )
for filter , messages := range messagesToHandle {
for _ , message := range messages {
if message . ThirdPartyID != "" {
importedMessages [ filter ] = append ( importedMessages [ filter ] , message )
} else {
otherMessages [ filter ] = append ( otherMessages [ filter ] , message )
}
}
}
err := m . handleImportedMessages ( importedMessages )
if err != nil {
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 {
2023-07-10 14:11:37 +00:00
return ErrInvalidCommunityID
2022-10-28 08:41:20 +00:00
}
requestToJoin , err := m . communitiesManager . HandleCommunityCancelRequestToJoin ( signer , & cancelRequestToJoinProto )
if err != nil {
return err
}
state . Response . RequestsToJoinCommunity = append ( state . Response . RequestsToJoinCommunity , requestToJoin )
2023-04-06 14:06:24 +00:00
// delete activity center notification
notification , err := m . persistence . GetActivityCenterNotificationByID ( requestToJoin . ID )
if err != nil {
return err
}
if notification != nil {
2023-06-10 02:00:17 +00:00
notification . UpdatedAt = m . getCurrentTimeInMillis ( )
err = m . persistence . DeleteActivityCenterNotificationByID ( types . FromHex ( requestToJoin . ID . String ( ) ) , notification . UpdatedAt )
2023-04-06 14:06:24 +00:00
if err != nil {
m . logger . Error ( "failed to delete notification from Activity Center" , zap . Error ( err ) )
return err
}
// sending signal to client to remove the activity center notification from UI
response := & MessengerResponse { }
notification . Deleted = true
2023-06-10 02:00:17 +00:00
err = m . syncActivityCenterNotifications ( [ ] * ActivityCenterNotification { notification } )
if err != nil {
m . logger . Error ( "HandleCommunityCancelRequestToJoin, failed to sync activity center notifications" , zap . Error ( err ) )
return err
}
2023-04-06 14:06:24 +00:00
response . AddActivityCenterNotification ( notification )
signal . SendNewMessages ( response )
}
2022-10-28 08:41:20 +00:00
return nil
}
2021-01-11 10:32:51 +00:00
// HandleCommunityRequestToJoin handles an community request to join
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 {
2023-07-10 14:11:37 +00:00
return ErrInvalidCommunityID
2021-01-11 10:32:51 +00:00
}
2023-04-21 09:18:47 +00:00
timeNow := uint64 ( time . Now ( ) . Unix ( ) )
requestTimeOutClock , err := communities . AddTimeoutToRequestToJoinClock ( requestToJoinProto . Clock )
if err != nil {
return err
}
if timeNow >= requestTimeOutClock {
return errors . New ( "request is expired" )
}
2021-01-11 10:32:51 +00:00
requestToJoin , err := m . communitiesManager . HandleCommunityRequestToJoin ( signer , & requestToJoinProto )
if err != nil {
return err
}
refactor: EventSenders forward RequestToJoin decision to control node
This is a bigger change in how community membership requests are handled
among admins, token masters, owners, and control nodes.
Prior to this commit, all privileged users, also known as
`EventSenders`, were able to accept and reject community membership
requests and those changes would be applied by all users.
This commit changes this behaviour such that:
1. EventSenders can make a decision (accept, reject), but merely forward
their decision to the control node, which ultimately has to confirm
it
2. EventSenders are no longer removing or adding members to and from
communities
3. When an eventsender signaled a decision, the membership request will
enter a pending state (acceptedPending or rejectedPending)
4. Once a decision was made by one eventsender, no other eventsender can
override that decision
This implementation is covered with a bunch of tests:
- Ensure that decision made by event sender is shared with other event
senders
- `testAcceptMemberRequestToJoinResponseSharedWithOtherEventSenders()`
- `testRejectMemberRequestToJoinResponseSharedWithOtherEventSenders()`
- Ensure memebrship request stays pending, until control node has
confirmed decision by event senders
- `testAcceptMemberRequestToJoinNotConfirmedByControlNode()`
- `testRejectMemberRequestToJoinNotConfirmedByControlNode()`
- Ensure that decision made by event sender cannot be overriden by other
event senders
- `testEventSenderCannotOverrideRequestToJoinState()`
These test cases live in three test suites for different event sender
types respectively
- `OwnerWithoutCommunityKeyCommunityEventsSuite`
- `TokenMasterCommunityEventsSuite`
- `AdminCommunityEventsSuite`
In addition to the changes mentioned above, there's also a smaller
changes that ensures membership requests to *not* attached revealed wallet
addresses when the requests are sent to event senders (in addition to
control nodes).
Requests send to a control node will still include revealed addresses as
the control node needs them to verify token permissions.
This commit does not yet handle the case of event senders attempting to
kick and ban members.
Similar to accepting and rejecting membership requests, kicking and
banning need a new pending state. However, we don't track such state in
local databases yet so those two cases will be handled in future commit
to not have this commit grow larger.
2023-08-02 12:04:47 +00:00
if requestToJoin . State == communities . RequestToJoinStateAccepted || requestToJoin . State == communities . RequestToJoinStateAcceptedPending {
2022-07-01 13:54:02 +00:00
accept := & requests . AcceptRequestToJoinCommunity {
ID : requestToJoin . ID ,
}
_ , err = m . AcceptRequestToJoinCommunity ( accept )
if err != nil {
2023-07-20 20:33:47 +00:00
if err == communities . ErrNoPermissionToJoin {
refactor: EventSenders forward RequestToJoin decision to control node
This is a bigger change in how community membership requests are handled
among admins, token masters, owners, and control nodes.
Prior to this commit, all privileged users, also known as
`EventSenders`, were able to accept and reject community membership
requests and those changes would be applied by all users.
This commit changes this behaviour such that:
1. EventSenders can make a decision (accept, reject), but merely forward
their decision to the control node, which ultimately has to confirm
it
2. EventSenders are no longer removing or adding members to and from
communities
3. When an eventsender signaled a decision, the membership request will
enter a pending state (acceptedPending or rejectedPending)
4. Once a decision was made by one eventsender, no other eventsender can
override that decision
This implementation is covered with a bunch of tests:
- Ensure that decision made by event sender is shared with other event
senders
- `testAcceptMemberRequestToJoinResponseSharedWithOtherEventSenders()`
- `testRejectMemberRequestToJoinResponseSharedWithOtherEventSenders()`
- Ensure memebrship request stays pending, until control node has
confirmed decision by event senders
- `testAcceptMemberRequestToJoinNotConfirmedByControlNode()`
- `testRejectMemberRequestToJoinNotConfirmedByControlNode()`
- Ensure that decision made by event sender cannot be overriden by other
event senders
- `testEventSenderCannotOverrideRequestToJoinState()`
These test cases live in three test suites for different event sender
types respectively
- `OwnerWithoutCommunityKeyCommunityEventsSuite`
- `TokenMasterCommunityEventsSuite`
- `AdminCommunityEventsSuite`
In addition to the changes mentioned above, there's also a smaller
changes that ensures membership requests to *not* attached revealed wallet
addresses when the requests are sent to event senders (in addition to
control nodes).
Requests send to a control node will still include revealed addresses as
the control node needs them to verify token permissions.
This commit does not yet handle the case of event senders attempting to
kick and ban members.
Similar to accepting and rejecting membership requests, kicking and
banning need a new pending state. However, we don't track such state in
local databases yet so those two cases will be handled in future commit
to not have this commit grow larger.
2023-08-02 12:04:47 +00:00
// only control node will end up here as it's the only one that
// performed token permission checks
2023-07-20 20:33:47 +00:00
requestToJoin . State = communities . RequestToJoinStateDeclined
} else {
return err
}
2022-07-01 13:54:02 +00:00
}
feat: add verified wallet accounts to community requests
This commit extends the `CommunityRequestToJoin` with `RevealedAddresses` which represent wallet addresses and signatures provided by the sender, to proof a community owner ownership of those wallet addresses.
**Note: This only works with keystore files maanged by status-go**
At high level, the follwing happens:
1. User instructs Status to send a request to join to a community. By adding a password hash to the instruction, Status will try to unlock the users keystore and verify each wallet account.
2. For every verified wallet account, a signature is created for the following payload, using each wallet's private key
``` keccak256(chatkey + communityID + requestToJoinID) ``` A map of walletAddress->signature is then attached to the community request to join, which will be sent to the community owner
3. The owner node receives the request, and if the community requires users to hold tokens to become a member, it will check and verify whether the given wallet addresses are indeed owned by the sender. If any signature provided by the request cannot be recovered, the request is immediately declined by the owner.
4. The verified addresses are then added to the owner node's database such that, once the request should be accepted, the addresses can be used to check on chain whether they own the necessary funds to fulfill the community's permissions
The checking of required funds is **not** part of this commit. It will be added in a follow-up commit.
2023-03-17 09:19:40 +00:00
}
2022-10-25 22:06:20 +00:00
refactor: EventSenders forward RequestToJoin decision to control node
This is a bigger change in how community membership requests are handled
among admins, token masters, owners, and control nodes.
Prior to this commit, all privileged users, also known as
`EventSenders`, were able to accept and reject community membership
requests and those changes would be applied by all users.
This commit changes this behaviour such that:
1. EventSenders can make a decision (accept, reject), but merely forward
their decision to the control node, which ultimately has to confirm
it
2. EventSenders are no longer removing or adding members to and from
communities
3. When an eventsender signaled a decision, the membership request will
enter a pending state (acceptedPending or rejectedPending)
4. Once a decision was made by one eventsender, no other eventsender can
override that decision
This implementation is covered with a bunch of tests:
- Ensure that decision made by event sender is shared with other event
senders
- `testAcceptMemberRequestToJoinResponseSharedWithOtherEventSenders()`
- `testRejectMemberRequestToJoinResponseSharedWithOtherEventSenders()`
- Ensure memebrship request stays pending, until control node has
confirmed decision by event senders
- `testAcceptMemberRequestToJoinNotConfirmedByControlNode()`
- `testRejectMemberRequestToJoinNotConfirmedByControlNode()`
- Ensure that decision made by event sender cannot be overriden by other
event senders
- `testEventSenderCannotOverrideRequestToJoinState()`
These test cases live in three test suites for different event sender
types respectively
- `OwnerWithoutCommunityKeyCommunityEventsSuite`
- `TokenMasterCommunityEventsSuite`
- `AdminCommunityEventsSuite`
In addition to the changes mentioned above, there's also a smaller
changes that ensures membership requests to *not* attached revealed wallet
addresses when the requests are sent to event senders (in addition to
control nodes).
Requests send to a control node will still include revealed addresses as
the control node needs them to verify token permissions.
This commit does not yet handle the case of event senders attempting to
kick and ban members.
Similar to accepting and rejecting membership requests, kicking and
banning need a new pending state. However, we don't track such state in
local databases yet so those two cases will be handled in future commit
to not have this commit grow larger.
2023-08-02 12:04:47 +00:00
declinedOrDeclinedPending := requestToJoin . State == communities . RequestToJoinStateDeclined || requestToJoin . State == communities . RequestToJoinStateDeclinedPending
if declinedOrDeclinedPending {
feat: add verified wallet accounts to community requests
This commit extends the `CommunityRequestToJoin` with `RevealedAddresses` which represent wallet addresses and signatures provided by the sender, to proof a community owner ownership of those wallet addresses.
**Note: This only works with keystore files maanged by status-go**
At high level, the follwing happens:
1. User instructs Status to send a request to join to a community. By adding a password hash to the instruction, Status will try to unlock the users keystore and verify each wallet account.
2. For every verified wallet account, a signature is created for the following payload, using each wallet's private key
``` keccak256(chatkey + communityID + requestToJoinID) ``` A map of walletAddress->signature is then attached to the community request to join, which will be sent to the community owner
3. The owner node receives the request, and if the community requires users to hold tokens to become a member, it will check and verify whether the given wallet addresses are indeed owned by the sender. If any signature provided by the request cannot be recovered, the request is immediately declined by the owner.
4. The verified addresses are then added to the owner node's database such that, once the request should be accepted, the addresses can be used to check on chain whether they own the necessary funds to fulfill the community's permissions
The checking of required funds is **not** part of this commit. It will be added in a follow-up commit.
2023-03-17 09:19:40 +00:00
cancel := & requests . DeclineRequestToJoinCommunity {
ID : requestToJoin . ID ,
}
_ , err = m . DeclineRequestToJoinCommunity ( cancel )
if err != nil {
return err
}
return nil
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 {
2023-06-14 14:15:46 +00:00
if state . Response . RequestsToJoinCommunity == nil {
state . Response . RequestsToJoinCommunity = make ( [ ] * communities . RequestToJoin , 0 )
}
2022-07-01 13:54:02 +00:00
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-04-21 09:18:47 +00:00
Deleted : false ,
2023-06-10 02:00:17 +00:00
UpdatedAt : m . getCurrentTimeInMillis ( ) ,
2022-10-25 22:06:20 +00:00
}
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-06-10 02:00:17 +00:00
notification . UpdatedAt = m . getCurrentTimeInMillis ( )
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
}
2023-07-10 14:11:37 +00:00
// HandleCommunityEditSharedAddresses handles an edit a user has made to their shared addresses
func ( m * Messenger ) HandleCommunityEditSharedAddresses ( state * ReceivedMessageState , signer * ecdsa . PublicKey , editRevealedAddressesProto protobuf . CommunityEditRevealedAccounts ) error {
if editRevealedAddressesProto . CommunityId == nil {
return ErrInvalidCommunityID
}
err := m . communitiesManager . HandleCommunityEditSharedAddresses ( signer , & editRevealedAddressesProto )
if err != nil {
return err
}
community , err := m . communitiesManager . GetByIDString ( string ( editRevealedAddressesProto . GetCommunityId ( ) ) )
if err != nil {
return err
}
state . Response . AddCommunity ( community )
return nil
}
2022-07-01 13:54:02 +00:00
func ( m * Messenger ) HandleCommunityRequestToJoinResponse ( state * ReceivedMessageState , signer * ecdsa . PublicKey , requestToJoinResponseProto protobuf . CommunityRequestToJoinResponse ) error {
if requestToJoinResponseProto . CommunityId == nil {
2023-07-10 14:11:37 +00:00
return ErrInvalidCommunityID
2022-07-01 13:54:02 +00:00
}
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 {
2023-05-29 17:57:05 +00:00
response , err := m . JoinCommunity ( context . Background ( ) , requestToJoinResponseProto . CommunityId , false )
2022-07-01 13:54:02 +00:00
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
2023-04-21 09:18:47 +00:00
notification . Read = false
notification . Deleted = false
2022-10-25 22:06:20 +00:00
} else {
notification . MembershipStatus = ActivityCenterMembershipStatusDeclined
}
2023-06-10 02:00:17 +00:00
notification . UpdatedAt = m . getCurrentTimeInMillis ( )
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 {
2023-07-10 14:11:37 +00:00
return ErrInvalidCommunityID
2022-08-22 10:10:31 +00:00
}
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 ] )
}
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
2023-04-17 14:44:48 +00:00
originalMessage , err := m . getMessageFromResponseOrDatabase ( state . Response , messageID )
2021-06-07 11:45:06 +00:00
2023-04-17 14:44:48 +00:00
if err == common . ErrRecordNotFound {
2021-06-07 11:45:06 +00:00
return m . persistence . SaveEdit ( editMessage )
2023-04-17 14:44:48 +00:00
} else if err != nil {
return err
2021-06-07 11:45:06 +00:00
}
2023-03-30 19:37:41 +00:00
originalMessageMentioned := originalMessage . Mentioned
2021-06-07 11:45:06 +00:00
chat , ok := m . allChats . Load ( originalMessage . LocalChatID )
if ! ok {
return errors . New ( "chat not found" )
}
// Check edit is valid
if originalMessage . From != editMessage . From {
return errors . New ( "invalid edit, not the right author" )
}
// Check that edit should be applied
if originalMessage . EditedAt >= editMessage . Clock {
return m . persistence . SaveEdit ( editMessage )
}
2023-04-27 14:22:26 +00:00
// applyEditMessage modifies the message. Changing the variable name to make it clearer
editedMessage := originalMessage
2021-06-07 11:45:06 +00:00
// Update message and return it
2023-04-27 14:22:26 +00:00
err = m . applyEditMessage ( & editMessage . EditMessage , editedMessage )
2021-06-07 11:45:06 +00:00
if err != nil {
return err
}
2023-05-29 17:57:05 +00:00
needToSaveChat := false
2023-04-27 14:22:26 +00:00
if chat . LastMessage != nil && chat . LastMessage . ID == editedMessage . ID {
chat . LastMessage = editedMessage
2023-05-29 17:57:05 +00:00
needToSaveChat = true
2021-06-23 10:07:26 +00:00
}
2023-04-27 14:22:26 +00:00
responseTo , err := m . persistence . MessageByID ( editedMessage . ResponseTo )
2023-02-06 10:38:37 +00:00
if err != nil && err != common . ErrRecordNotFound {
return err
}
2023-04-27 14:22:26 +00:00
err = state . updateExistingActivityCenterNotification ( m . identity . PublicKey , m , editedMessage , responseTo )
2023-02-06 10:38:37 +00:00
if err != nil {
return err
}
2023-03-30 19:37:41 +00:00
2023-04-27 14:22:26 +00:00
editedMessageHasMentions := editedMessage . Mentioned
2023-03-30 19:37:41 +00:00
2023-04-27 14:22:26 +00:00
if editedMessageHasMentions && ! originalMessageMentioned && ! editedMessage . Seen {
2023-03-30 19:37:41 +00:00
// Increase unviewed count when the edited message has a mention and didn't have one before
2023-04-27 14:22:26 +00:00
chat . UnviewedMentionsCount ++
needToSaveChat = true
} else if ! editedMessageHasMentions && originalMessageMentioned && ! editedMessage . Seen {
// Opposite of above, the message had a mention, but no longer does, so we reduce the count
chat . UnviewedMentionsCount --
needToSaveChat = true
}
if needToSaveChat {
err := m . saveChat ( chat )
if err != nil {
return err
}
2023-03-30 19:37:41 +00:00
}
2023-04-27 14:22:26 +00:00
state . Response . AddMessage ( editedMessage )
2023-04-17 14:44:48 +00:00
// pull updated messages
updatedMessages , err := m . persistence . MessagesByResponseTo ( messageID )
if err != nil {
return err
}
state . Response . AddMessages ( updatedMessages )
2023-02-06 10:38:37 +00:00
state . Response . AddChat ( chat )
2021-06-07 11:45:06 +00:00
2021-06-07 08:31:27 +00:00
return nil
}
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 {
2023-03-07 14:38:09 +00:00
fromPublicKey , err := common . HexToPubkey ( deleteMessage . From )
if err != nil {
return err
}
2022-12-02 11:34:02 +00:00
if chat . ChatType == ChatTypeCommunityChat {
2023-03-07 14:38:09 +00:00
canDeleteMessageForEveryone = m . CanDeleteMessageForEveryoneInCommunity ( chat . CommunityID , fromPublicKey )
if ! canDeleteMessageForEveryone {
return ErrInvalidDeletePermission
2022-12-02 11:34:02 +00:00
}
2023-03-07 14:38:09 +00:00
} else if chat . ChatType == ChatTypePrivateGroupChat {
canDeleteMessageForEveryone = m . CanDeleteMessageForEveryoneInPrivateGroupChat ( chat , fromPublicKey )
2022-12-02 11:34:02 +00:00
if ! canDeleteMessageForEveryone {
return ErrInvalidDeletePermission
}
}
2023-03-07 14:38:09 +00:00
2022-12-02 11:34:02 +00:00
// Check edit is valid
if ! canDeleteMessageForEveryone {
return errors . New ( "invalid delete, not the right author" )
}
2021-07-26 21:06:32 +00:00
}
2023-05-08 17:02:54 +00:00
messagesToDelete , err := m . getConnectedMessages ( originalMessage , originalMessage . LocalChatID )
2021-07-26 21:06:32 +00:00
if err != nil {
return err
}
2023-04-27 14:22:26 +00:00
unreadCountDecreased := false
2023-04-14 17:17:56 +00:00
for _ , messageToDelete := range messagesToDelete {
messageToDelete . Deleted = true
messageToDelete . DeletedBy = deleteMessage . DeleteMessage . DeletedBy
err := m . persistence . SaveMessages ( [ ] * common . Message { messageToDelete } )
if err != nil {
return err
}
2021-08-23 14:23:55 +00:00
2023-04-14 17:17:56 +00:00
m . logger . Debug ( "deleting activity center notification for message" , zap . String ( "chatID" , chat . ID ) , zap . String ( "messageID" , messageToDelete . ID ) )
2023-06-10 02:00:17 +00:00
notifications , err := m . persistence . DeleteActivityCenterNotificationForMessage ( chat . ID , messageToDelete . ID , m . getCurrentTimeInMillis ( ) )
2021-08-23 14:23:55 +00:00
2023-04-14 17:17:56 +00:00
if err != nil {
m . logger . Warn ( "failed to delete notifications for deleted message" , zap . Error ( err ) )
2021-07-26 21:06:32 +00:00
return err
}
2022-09-12 13:39:07 +00:00
2023-04-27 14:22:26 +00:00
// Reduce chat mention count and unread count if unread
if ! messageToDelete . Seen && ! unreadCountDecreased {
unreadCountDecreased = true
2023-05-16 16:11:52 +00:00
if chat . UnviewedMessagesCount > 0 {
chat . UnviewedMessagesCount --
}
if chat . UnviewedMentionsCount > 0 && ( messageToDelete . Mentioned || messageToDelete . Replied ) {
2023-04-27 14:22:26 +00:00
chat . UnviewedMentionsCount --
}
err := m . saveChat ( chat )
if err != nil {
return err
}
}
2023-06-10 02:00:17 +00:00
err = m . syncActivityCenterNotifications ( notifications )
if err != nil {
m . logger . Error ( "HandleDeleteMessage, failed to sync activity center notifications" , zap . Error ( err ) )
return err
}
2023-04-14 17:17:56 +00:00
state . Response . AddRemovedMessage ( & RemovedMessage { MessageID : messageToDelete . ID , ChatID : chat . ID , DeletedBy : deleteMessage . DeleteMessage . DeletedBy } )
state . Response . AddNotification ( DeletedMessageNotification ( messageToDelete . ID , chat ) )
state . Response . AddActivityCenterNotification ( & ActivityCenterNotification {
ID : types . FromHex ( messageToDelete . ID ) ,
Deleted : true ,
} )
2023-05-08 17:02:54 +00:00
if chat . LastMessage != nil && chat . LastMessage . ID == messageToDelete . ID {
chat . LastMessage = messageToDelete
err = m . saveChat ( chat )
if err != nil {
return nil
2023-04-14 17:17:56 +00:00
}
2023-05-08 17:02:54 +00:00
}
2023-04-14 17:17:56 +00:00
2023-05-08 17:02:54 +00:00
messages , err := m . persistence . LatestMessageByChatID ( chat . ID )
if err != nil {
return err
}
if len ( messages ) > 0 {
previousNotDeletedMessage := messages [ 0 ]
if previousNotDeletedMessage != nil && ! previousNotDeletedMessage . Seen && chat . OneToOne ( ) && ! chat . Active {
m . createMessageNotification ( chat , state , previousNotDeletedMessage )
2023-04-14 17:17:56 +00:00
}
2022-09-12 13:39:07 +00:00
}
2023-04-17 14:44:48 +00:00
// pull updated messages
updatedMessages , err := m . persistence . MessagesByResponseTo ( messageToDelete . ID )
if err != nil {
return err
}
state . Response . AddMessages ( updatedMessages )
2021-07-26 21:06:32 +00:00
}
2021-08-19 13:16:45 +00:00
state . Response . AddChat ( chat )
2021-07-26 21:06:32 +00:00
return nil
}
2023-04-17 14:44:48 +00:00
func ( m * Messenger ) getMessageFromResponseOrDatabase ( response * MessengerResponse , messageID string ) ( * common . Message , error ) {
originalMessage := response . GetMessage ( messageID )
// otherwise pull from database
if originalMessage != nil {
return originalMessage , nil
}
return m . persistence . MessageByID ( messageID )
}
2023-05-12 08:31:34 +00:00
func ( m * Messenger ) HandleDeleteForMeMessage ( state * ReceivedMessageState , deleteForMeMessage protobuf . DeleteForMeMessage ) error {
if err := ValidateDeleteForMeMessage ( deleteForMeMessage ) ; err != nil {
2022-09-28 11:42:17 +00:00
return err
}
messageID := deleteForMeMessage . MessageId
// Check if it's already in the response
2023-04-17 14:44:48 +00:00
originalMessage , err := m . getMessageFromResponseOrDatabase ( state . Response , messageID )
2022-09-28 11:42:17 +00:00
2023-04-17 14:44:48 +00:00
if err == common . ErrRecordNotFound {
2023-05-12 08:31:34 +00:00
return m . persistence . SaveOrUpdateDeleteForMeMessage ( & deleteForMeMessage )
2023-04-17 14:44:48 +00:00
} else if err != nil {
return err
2022-09-28 11:42:17 +00:00
}
chat , ok := m . allChats . Load ( originalMessage . LocalChatID )
if ! ok {
return errors . New ( "chat not found" )
}
2023-05-12 08:31:34 +00:00
messagesToDelete , err := m . getConnectedMessages ( originalMessage , originalMessage . LocalChatID )
2022-09-28 11:42:17 +00:00
if err != nil {
return err
}
2023-04-14 17:17:56 +00:00
for _ , messageToDelete := range messagesToDelete {
messageToDelete . DeletedForMe = true
2022-09-28 11:42:17 +00:00
2023-04-14 17:17:56 +00:00
err := m . persistence . SaveMessages ( [ ] * common . Message { messageToDelete } )
if err != nil {
return err
}
2022-09-28 11:42:17 +00:00
2023-04-14 17:17:56 +00:00
m . logger . Debug ( "deleting activity center notification for message" , zap . String ( "chatID" , chat . ID ) , zap . String ( "messageID" , messageToDelete . ID ) )
2023-06-10 02:00:17 +00:00
notifications , err := m . persistence . DeleteActivityCenterNotificationForMessage ( chat . ID , messageToDelete . ID , m . getCurrentTimeInMillis ( ) )
2023-04-14 17:17:56 +00:00
if err != nil {
m . logger . Warn ( "failed to delete notifications for deleted message" , zap . Error ( err ) )
2022-11-17 10:11:58 +00:00
return err
}
2023-04-14 17:17:56 +00:00
if chat . LastMessage != nil && chat . LastMessage . ID == messageToDelete . ID {
2023-05-08 17:02:54 +00:00
chat . LastMessage = messageToDelete
err = m . saveChat ( chat )
if err != nil {
return nil
2023-04-14 17:17:56 +00:00
}
}
2023-06-10 02:00:17 +00:00
err = m . syncActivityCenterNotifications ( notifications )
if err != nil {
m . logger . Error ( "HandleDeleteForMeMessage, failed to sync activity center notifications" , zap . Error ( err ) )
return err
}
2023-04-14 17:17:56 +00:00
state . Response . AddMessage ( messageToDelete )
}
2022-09-28 11:42:17 +00:00
state . Response . AddChat ( chat )
return nil
}
2023-01-20 14:28:30 +00:00
func handleContactRequestChatMessage ( receivedMessage * common . Message , contact * Contact , outgoing bool , logger * zap . Logger ) ( bool , error ) {
receivedMessage . ContactRequestState = common . ContactRequestStatePending
var response ContactRequestProcessingResponse
if outgoing {
response = contact . ContactRequestSent ( receivedMessage . Clock )
} else {
response = contact . ContactRequestReceived ( receivedMessage . Clock )
}
if ! response . processed {
logger . Info ( "not handling contact message since clock lower" )
return false , nil
}
if contact . mutual ( ) {
receivedMessage . ContactRequestState = common . ContactRequestStateAccepted
}
return response . newContactRequestReceived , nil
}
2023-05-10 16:26:31 +00:00
func ( m * Messenger ) handleChatMessage ( state * ReceivedMessageState , forceSeen bool ) error {
2020-01-10 18:59:01 +00:00
logger := m . logger . With ( zap . String ( "site" , "handleChatMessage" ) )
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 )
2023-05-10 16:26:31 +00:00
if forceSeen || isSyncMessage {
2021-09-23 11:52:15 +00:00
receivedMessage . Seen = true
}
2023-01-20 14:28:30 +00:00
err := receivedMessage . PrepareContent ( m . myHexIdentity ( ) )
2020-02-10 11:22:37 +00:00
if err != nil {
return fmt . Errorf ( "failed to prepare message content: %v" , err )
}
2023-01-10 17:59:32 +00:00
// If the message is a reply, we check if it's a reply to one of own own messages
if receivedMessage . ResponseTo != "" {
repliedTo , err := m . persistence . MessageByID ( receivedMessage . ResponseTo )
2023-01-11 17:43:04 +00:00
if err != nil && ( err == sql . ErrNoRows || err == common . ErrRecordNotFound ) {
2023-01-10 17:59:32 +00:00
logger . Error ( "failed to get quoted message" , zap . Error ( err ) )
} else if err != nil {
return err
2023-01-20 14:28:30 +00:00
} else if repliedTo . From == m . myHexIdentity ( ) {
2023-01-10 17:59:32 +00:00
receivedMessage . Replied = true
}
}
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
2023-03-31 09:15:06 +00:00
skipUpdateUnviewedCountForAlbums := false
if receivedMessage . ContentType == protobuf . ChatMessage_IMAGE {
image := receivedMessage . GetImage ( )
if image != nil && image . AlbumId != "" {
// Skip unviewed counts increasing for other messages from album if we have it in memory
for _ , message := range state . Response . Messages ( ) {
if receivedMessage . ContentType == protobuf . ChatMessage_IMAGE {
img := message . GetImage ( )
if img != nil && img . AlbumId != "" && img . AlbumId == image . AlbumId {
skipUpdateUnviewedCountForAlbums = true
break
}
}
}
if ! skipUpdateUnviewedCountForAlbums {
messages , err := m . persistence . AlbumMessages ( chat . ID , image . AlbumId )
if err != nil {
return err
}
// Skip unviewed counts increasing for other messages from album if we have it in db
skipUpdateUnviewedCountForAlbums = len ( messages ) > 0
}
}
}
if ! skipUpdateUnviewedCountForAlbums {
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-07-12 15:12:58 +00:00
// Add mutual state update message for outgoing contact request
clock := receivedMessage . Clock - 1
updateMessage , err := m . prepareMutualStateUpdateMessage ( contact . ID , MutualStateUpdateTypeSent , clock , receivedMessage . Timestamp , false )
if err != nil {
return err
}
m . prepareMessage ( updateMessage , m . httpServer )
err = m . persistence . SaveMessages ( [ ] * common . Message { updateMessage } )
if err != nil {
return err
}
state . Response . AddMessage ( updateMessage )
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 {
2023-03-14 15:34:35 +00:00
chatContact , err = m . BuildContact ( & requests . BuildContact { PublicKey : chat . ID } )
2023-01-20 14:28:30 +00:00
if err != nil {
return err
}
2022-01-18 16:31:34 +00:00
}
2023-01-20 14:28:30 +00:00
2023-07-14 13:37:38 +00:00
if chatContact . mutual ( ) || chatContact . dismissed ( ) {
m . logger . Info ( "ignoring contact request message for a mutual or dismissed contact" )
return nil
}
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-07-12 15:12:58 +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
}
2023-05-08 17:02:54 +00:00
if ! receivedMessage . Deleted && ! receivedMessage . DeletedForMe {
2021-08-13 17:10:32 +00:00
err = chat . UpdateFromMessage ( receivedMessage , m . getTimesource ( ) )
if err != nil {
return err
}
2020-01-10 18:59:01 +00:00
}
// Set in the modified maps chat
2021-01-11 10:32:51 +00:00
state . Response . AddChat ( chat )
2021-03-29 15:41:30 +00:00
// TODO(samyoul) remove storing of an updated reference pointer?
2021-06-08 06:07:45 +00:00
m . allChats . Store ( chat . ID , chat )
2020-01-10 18:59:01 +00:00
2023-01-20 14:28:30 +00:00
if ! isSyncMessage && receivedMessage . EnsName != "" {
2021-01-11 10:32:51 +00:00
oldRecord , err := m . ensVerifier . Add ( contact . ID , receivedMessage . EnsName , receivedMessage . Clock )
if err != nil {
m . logger . Warn ( "failed to verify ENS name" , zap . Error ( err ) )
} else if oldRecord == nil {
// If oldRecord is nil, a new verification process will take place
// so we reset the record
contact . ENSVerified = false
2021-03-29 15:41:30 +00:00
state . ModifiedContacts . Store ( contact . ID , true )
state . AllContacts . Store ( contact . ID , contact )
2021-01-11 10:32:51 +00:00
}
2020-02-05 10:09:33 +00:00
}
2023-01-20 14:28:30 +00:00
if ! isSyncMessage && contact . DisplayName != receivedMessage . DisplayName && len ( receivedMessage . DisplayName ) != 0 {
2022-02-17 15:13:10 +00:00
contact . DisplayName = receivedMessage . DisplayName
state . ModifiedContacts . Store ( contact . ID , true )
}
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
}
2023-05-10 16:26:31 +00:00
func ( m * Messenger ) HandleChatMessage ( state * ReceivedMessageState ) error {
return m . handleChatMessage ( state , false )
}
func ( m * Messenger ) HandleImportedChatMessage ( state * ReceivedMessageState ) error {
return m . handleChatMessage ( state , true )
}
2022-12-26 04:35:46 +00:00
func ( m * Messenger ) addActivityCenterNotification ( response * MessengerResponse , notification * ActivityCenterNotification ) error {
2023-06-10 02:00:17 +00:00
_ , err := m . persistence . SaveActivityCenterNotification ( notification , true )
2021-04-07 12:57:14 +00:00
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
2023-06-10 02:00:17 +00:00
err = m . syncActivityCenterNotifications ( [ ] * ActivityCenterNotification { notification } )
if err != nil {
m . logger . Error ( "addActivityCenterNotification, failed to sync activity center notifications" , zap . Error ( err ) )
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 )
2023-06-10 02:00:17 +00:00
if ! notification . Read {
return m . syncActivityCenterNotificationState ( 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-04-13 00:45:25 +00:00
if settingField == nil {
return nil
}
switch message . GetType ( ) {
case protobuf . SyncSetting_DISPLAY_NAME :
if newName := message . GetValueString ( ) ; newName != "" && m . account . Name != newName {
m . account . Name = newName
if err := m . multiAccounts . SaveAccount ( * m . account ) ; err != nil {
2023-01-12 03:00:24 +00:00
return err
}
}
2023-04-13 00:45:25 +00:00
case protobuf . SyncSetting_MNEMONIC_REMOVED :
if message . GetValueBool ( ) {
if err := m . settings . DeleteMnemonic ( ) ; err != nil {
return err
}
messageState . Response . AddSetting ( & settings . SyncSettingField { SettingField : settings . Mnemonic } )
}
return nil
2022-12-29 06:16:19 +00:00
}
2023-04-13 00:45:25 +00:00
messageState . Response . AddSetting ( settingField )
2022-12-29 06:16:19 +00:00
return nil
}
2023-07-18 13:35:06 +00:00
func ( m * Messenger ) handleSyncAccountCustomizationColor ( state * ReceivedMessageState , message protobuf . SyncAccountCustomizationColor ) error {
err := m . multiAccounts . UpdateAccountCustomizationColor ( message . GetKeyUid ( ) , message . GetCustomizationColor ( ) , message . GetUpdatedAt ( ) )
if err != nil {
return err
}
state . Response . CustomizationColor = message . GetCustomizationColor ( )
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
}
2023-07-28 18:18:27 +00:00
hasPermission := community . IsPrivilegedMember ( chatEntity . GetSigPubKey ( ) )
2022-05-10 14:21:38 +00:00
pinMessageAllowed := community . AllowsAllMembersToPinMessage ( )
2023-07-28 18:18:27 +00:00
if ( pinMessage && ! hasPermission && ! 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
}
2023-06-06 18:06:11 +00:00
if err = m . updateContactImagesURL ( contact ) ; err != nil {
return err
}
2022-08-02 12:56:26 +00:00
contactModified = true
}
if clockChanged {
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
}
2023-06-05 11:10:26 +00:00
if ! contact . SocialLinks . Equal ( 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
}
2023-04-14 17:17:56 +00:00
func ( m * Messenger ) getMessagesToCheckForDelete ( message * common . Message ) ( [ ] * common . Message , error ) {
var messagesToCheck [ ] * common . Message
if message . ContentType == protobuf . ChatMessage_IMAGE {
image := message . GetImage ( )
2023-05-16 15:00:49 +00:00
if image != nil && image . AlbumId != "" {
messagesInTheAlbum , err := m . persistence . albumMessages ( message . ChatId , image . GetAlbumId ( ) )
if err != nil {
return nil , err
}
messagesToCheck = append ( messagesToCheck , messagesInTheAlbum ... )
2023-04-14 17:17:56 +00:00
}
}
messagesToCheck = append ( messagesToCheck , message )
return messagesToCheck , nil
}
2021-07-26 21:06:32 +00:00
func ( m * Messenger ) checkForDeletes ( message * common . Message ) error {
2023-05-16 15:00:49 +00:00
// Get all messages part of the album
2023-04-14 17:17:56 +00:00
messagesToCheck , err := m . getMessagesToCheckForDelete ( message )
2021-07-26 21:06:32 +00:00
if err != nil {
return err
}
2023-04-14 17:17:56 +00:00
var messageDeletes [ ] * DeleteMessage
applyDelete := false
2023-05-16 15:00:49 +00:00
// Loop all messages part of the album, if one of them is marked as deleted, we delete them all
2023-04-14 17:17:56 +00:00
for _ , messageToCheck := range messagesToCheck {
2023-05-16 15:00:49 +00:00
// Check for any pending deletes
// If any pending deletes are available and valid, apply them
messageDeletes , err = m . persistence . GetDeletes ( messageToCheck . ID , messageToCheck . From )
if err != nil {
return err
}
2023-04-14 17:17:56 +00:00
2023-05-16 15:00:49 +00:00
if len ( messageDeletes ) == 0 {
continue
2023-04-14 17:17:56 +00:00
}
// Once one messageDelete has been found, we apply it to all the images in the album
applyDelete = true
2023-05-16 15:00:49 +00:00
break
}
if applyDelete {
for _ , messageToCheck := range messagesToCheck {
err := m . applyDeleteMessage ( messageDeletes , messageToCheck )
if err != nil {
return err
}
2023-04-14 17:17:56 +00:00
}
}
return nil
2021-07-26 21:06:32 +00:00
}
2022-09-28 11:42:17 +00:00
func ( m * Messenger ) checkForDeleteForMes ( message * common . Message ) error {
2023-04-14 17:17:56 +00:00
messagesToCheck , err := m . getMessagesToCheckForDelete ( message )
2022-09-28 11:42:17 +00:00
if err != nil {
return err
}
2023-05-12 08:31:34 +00:00
var messageDeleteForMes [ ] * protobuf . DeleteForMeMessage
2023-04-14 17:17:56 +00:00
applyDelete := false
for _ , messageToCheck := range messagesToCheck {
if ! applyDelete {
// Check for any pending delete for mes
// If any pending deletes are available and valid, apply them
2023-05-12 08:31:34 +00:00
messageDeleteForMes , err = m . persistence . GetDeleteForMeMessagesByMessageID ( messageToCheck . ID )
2023-04-14 17:17:56 +00:00
if err != nil {
return err
}
2022-09-28 11:42:17 +00:00
2023-04-14 17:17:56 +00:00
if len ( messageDeleteForMes ) == 0 {
continue
}
}
// Once one messageDeleteForMes has been found, we apply it to all the images in the album
applyDelete = true
2023-05-12 08:31:34 +00:00
err := m . applyDeleteForMeMessage ( messageToCheck )
2023-04-14 17:17:56 +00:00
if err != nil {
return err
}
}
return nil
2022-09-28 11:42:17 +00:00
}
2021-06-08 06:07:45 +00:00
func ( m * Messenger ) isMessageAllowedFrom ( publicKey string , chat * Chat ) ( bool , error ) {
2021-04-07 12:57:14 +00:00
onlyFromContacts , err := m . settings . GetMessagesFromContactsOnly ( )
if err != nil {
return false , err
}
if ! onlyFromContacts {
return true , nil
}
// if it's from us, it's allowed
2023-01-20 14:28:30 +00:00
if m . myHexIdentity ( ) == publicKey {
2021-04-07 12:57:14 +00:00
return true , nil
}
// 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 ++
}
}
2023-06-28 19:45:36 +00:00
func mapSyncAccountToAccount ( message * protobuf . SyncAccount , accountOperability accounts . AccountOperable , accType accounts . AccountType ) * accounts . Account {
2023-05-16 10:48:00 +00:00
return & accounts . Account {
2023-07-20 13:30:23 +00:00
Address : types . BytesToAddress ( message . Address ) ,
KeyUID : message . KeyUid ,
PublicKey : types . HexBytes ( message . PublicKey ) ,
Type : accType ,
Path : message . Path ,
Name : message . Name ,
ColorID : multiaccountscommon . CustomizationColor ( message . ColorId ) ,
Emoji : message . Emoji ,
Wallet : message . Wallet ,
Chat : message . Chat ,
Hidden : message . Hidden ,
Clock : message . Clock ,
Operable : accountOperability ,
Removed : message . Removed ,
Position : message . Position ,
ProdPreferredChainIDs : message . ProdPreferredChainIDs ,
TestPreferredChainIDs : message . TestPreferredChainIDs ,
2023-05-16 10:48:00 +00:00
}
}
2023-06-28 19:45:36 +00:00
func ( m * Messenger ) resolveAccountOperability ( syncAcc * protobuf . SyncAccount , syncKpMigratedToKeycard bool , accountReceivedFromLocalPairing bool ) ( accounts . AccountOperable , error ) {
2023-08-09 10:36:05 +00:00
if accountReceivedFromLocalPairing {
return accounts . AccountOperable ( syncAcc . Operable ) , nil
}
2023-06-28 19:45:36 +00:00
accountsOperability := accounts . AccountNonOperable
dbAccount , err := m . settings . GetAccountByAddress ( types . BytesToAddress ( syncAcc . Address ) )
if err != nil && err != accounts . ErrDbAccountNotFound {
return accountsOperability , err
}
if dbAccount != nil {
return dbAccount . Operable , nil
}
if ! syncKpMigratedToKeycard {
// We're here when we receive a keypair from the paired device which is either:
// 1. regular keypair or
// 2. was just converted from keycard to a regular keypair.
2023-07-05 12:41:58 +00:00
dbKeycardsForKeyUID , err := m . settings . GetKeycardsWithSameKeyUID ( syncAcc . KeyUid )
if err != nil {
2023-06-28 19:45:36 +00:00
return accounts . AccountNonOperable , err
}
if len ( dbKeycardsForKeyUID ) > 0 {
2023-07-05 12:41:58 +00:00
// We're here in case 2. from above and in this case we need to mark all accounts for this keypair non operable
2023-05-16 10:48:00 +00:00
return accounts . AccountNonOperable , nil
}
}
2023-08-09 10:36:05 +00:00
if syncKpMigratedToKeycard || syncAcc . Chat || syncAcc . Wallet {
2023-05-16 10:48:00 +00:00
accountsOperability = accounts . AccountFullyOperable
} else {
2023-06-28 19:45:36 +00:00
partiallyOrFullyOperable , err := m . settings . IsAnyAccountPartiallyOrFullyOperableForKeyUID ( syncAcc . KeyUid )
2023-05-16 10:48:00 +00:00
if err != nil {
if err == accounts . ErrDbKeypairNotFound {
return accounts . AccountNonOperable , nil
}
return accounts . AccountNonOperable , err
}
if partiallyOrFullyOperable {
accountsOperability = accounts . AccountPartiallyOperable
}
}
return accountsOperability , nil
}
2022-05-18 10:42:51 +00:00
2023-07-27 13:20:40 +00:00
func ( m * Messenger ) handleSyncWatchOnlyAccount ( message * protobuf . SyncAccount , fromBackup bool ) ( * accounts . Account , error ) {
2023-06-28 19:45:36 +00:00
if message . KeyUid != "" {
return nil , ErrNotWatchOnlyAccount
2022-05-18 10:42:51 +00:00
}
2023-05-16 10:48:00 +00:00
accountOperability := accounts . AccountFullyOperable
2023-04-19 14:44:57 +00:00
accAddress := types . BytesToAddress ( message . Address )
dbAccount , err := m . settings . GetAccountByAddress ( accAddress )
2023-05-16 10:48:00 +00:00
if err != nil && err != accounts . ErrDbAccountNotFound {
2023-04-19 14:44:57 +00:00
return nil , err
2022-05-18 10:42:51 +00:00
}
2023-05-16 10:48:00 +00:00
if dbAccount != nil {
if message . Clock <= dbAccount . Clock {
return nil , ErrTryingToStoreOldWalletAccount
}
2023-04-19 14:44:57 +00:00
2023-05-16 10:48:00 +00:00
if message . Removed {
2023-07-20 15:00:39 +00:00
err = m . settings . RemoveAccount ( accAddress , message . Clock )
2023-07-19 11:50:16 +00:00
if err != nil {
return nil , err
}
2023-07-27 13:20:40 +00:00
// if keypair is retrieved from backed up data, no need for resolving accounts positions
if ! fromBackup {
err = m . settings . ResolveAccountsPositions ( message . Clock )
if err != nil {
return nil , err
}
}
2023-05-16 10:48:00 +00:00
dbAccount . Removed = true
2023-07-27 13:20:40 +00:00
return dbAccount , nil
2023-04-19 14:44:57 +00:00
}
}
2023-06-28 19:45:36 +00:00
acc := mapSyncAccountToAccount ( message , accountOperability , accounts . AccountTypeWatch )
2023-05-16 10:48:00 +00:00
2023-06-28 19:45:36 +00:00
err = m . settings . SaveOrUpdateAccounts ( [ ] * accounts . Account { acc } , false )
2023-04-19 14:44:57 +00:00
if err != nil {
return nil , err
}
return acc , nil
}
2023-07-16 11:11:48 +00:00
func ( m * Messenger ) handleSyncAccountsPositions ( message * protobuf . SyncAccountsPositions ) ( [ ] * accounts . Account , error ) {
if len ( message . Accounts ) == 0 {
return nil , nil
}
dbLastUpdate , err := m . settings . GetClockOfLastAccountsPositionChange ( )
if err != nil {
return nil , err
}
2023-07-19 11:50:16 +00:00
// Since adding new account updates `ClockOfLastAccountsPositionChange` we should handle account order changes
// even they are with the same clock, that ensures the correct order in case of syncing devices.
if message . Clock < dbLastUpdate {
2023-07-16 11:11:48 +00:00
return nil , ErrTryingToApplyOldWalletAccountsOrder
}
var accs [ ] * accounts . Account
for _ , sAcc := range message . Accounts {
acc := & accounts . Account {
Address : types . BytesToAddress ( sAcc . Address ) ,
KeyUID : sAcc . KeyUid ,
Position : sAcc . Position ,
}
accs = append ( accs , acc )
}
err = m . settings . SetWalletAccountsPositions ( accs , message . Clock )
if err != nil {
return nil , err
}
return accs , nil
}
2023-08-09 11:14:17 +00:00
func ( m * Messenger ) handleSyncKeypair ( message * protobuf . SyncKeypair , fromLocalPairing bool ) ( * accounts . Keypair , error ) {
2023-05-26 16:37:40 +00:00
if message == nil {
return nil , errors . New ( "handleSyncKeypair receive a nil message" )
}
2023-05-16 10:48:00 +00:00
dbKeypair , err := m . settings . GetKeypairByKeyUID ( message . KeyUid )
if err != nil && err != accounts . ErrDbKeypairNotFound {
return nil , err
}
kp := & accounts . Keypair {
KeyUID : message . KeyUid ,
Name : message . Name ,
Type : accounts . KeypairType ( message . Type ) ,
DerivedFrom : message . DerivedFrom ,
LastUsedDerivationIndex : message . LastUsedDerivationIndex ,
SyncedFrom : message . SyncedFrom ,
Clock : message . Clock ,
2023-06-28 19:45:36 +00:00
Removed : message . Removed ,
2023-05-16 10:48:00 +00:00
}
if dbKeypair != nil {
2023-06-28 19:45:36 +00:00
if dbKeypair . Clock >= kp . Clock {
return nil , ErrTryingToStoreOldKeypair
}
2023-05-16 10:48:00 +00:00
// in case of keypair update, we need to keep `synced_from` field as it was when keypair was introduced to this device for the first time
2023-08-09 11:14:17 +00:00
// but in case if keypair on this device came from the backup (e.g. device A recovered from waku, then device B paired with the device A
// via local pairing, before device A made its keypairs fully operable) we need to update syncedFrom when user on this device when that
// keypair becomes operable on any of other paired devices
if dbKeypair . SyncedFrom != accounts . SyncedFromBackup {
kp . SyncedFrom = dbKeypair . SyncedFrom
}
2023-05-16 10:48:00 +00:00
}
2023-06-28 19:45:36 +00:00
for _ , sAcc := range message . Accounts {
syncKpMigratedToKeycard := len ( message . Keycards ) > 0
2023-07-27 13:20:40 +00:00
if message . SyncedFrom == accounts . SyncedFromBackup && kp . Type == accounts . KeypairTypeProfile {
// if a profile keypair is coming from backup, we're handling within this block the case when a recovering
// was inititiated via keycard, while backed up profile keypair data refers to a regular profile
multiAcc , err := m . multiAccounts . GetAccount ( kp . KeyUID )
if err != nil {
return nil , err
}
syncKpMigratedToKeycard = multiAcc != nil && multiAcc . KeycardPairing != ""
}
2023-08-09 11:14:17 +00:00
accountOperability , err := m . resolveAccountOperability ( sAcc , syncKpMigratedToKeycard , fromLocalPairing )
2023-06-28 19:45:36 +00:00
if err != nil {
return nil , err
2023-05-16 10:48:00 +00:00
}
2023-06-28 19:45:36 +00:00
acc := mapSyncAccountToAccount ( sAcc , accountOperability , accounts . GetAccountTypeForKeypairType ( kp . Type ) )
2023-05-16 10:48:00 +00:00
2023-06-28 19:45:36 +00:00
kp . Accounts = append ( kp . Accounts , acc )
}
if kp . Removed {
// delete all keystore files
2023-07-28 13:06:50 +00:00
err = m . deleteKeystoreFilesForKeypair ( dbKeypair )
if err != nil {
return nil , err
2023-05-16 10:48:00 +00:00
}
2023-08-09 11:14:17 +00:00
} else if ! fromLocalPairing && dbKeypair != nil {
2023-06-28 19:45:36 +00:00
for _ , dbAcc := range dbKeypair . Accounts {
found := false
for _ , acc := range kp . Accounts {
if dbAcc . Address == acc . Address {
found = true
break
}
}
if ! found {
err = m . deleteKeystoreFileForAddress ( dbAcc . Address )
if err != nil {
return nil , err
}
2023-04-19 14:44:57 +00:00
}
}
2023-06-28 19:45:36 +00:00
}
2023-03-20 07:33:08 +00:00
2023-07-20 15:00:39 +00:00
// deleting keypair will delete related keycards as well
err = m . settings . RemoveKeypair ( message . KeyUid , message . Clock )
2023-06-28 19:45:36 +00:00
if err != nil && err != accounts . ErrDbKeypairNotFound {
return nil , err
2023-05-16 10:48:00 +00:00
}
2023-07-28 13:06:50 +00:00
// if entire keypair was removed and keypair is already in db, there is no point to continue
if kp . Removed && dbKeypair != nil {
2023-07-27 13:20:40 +00:00
// if keypair is retrieved from backed up data, no need for resolving accounts positions
if message . SyncedFrom != accounts . SyncedFromBackup {
err = m . settings . ResolveAccountsPositions ( message . Clock )
if err != nil {
return nil , err
}
2023-07-19 11:50:16 +00:00
}
2023-06-28 19:45:36 +00:00
return kp , nil
}
2023-05-16 10:48:00 +00:00
2023-07-19 11:50:16 +00:00
// save keypair first
2023-06-28 19:45:36 +00:00
err = m . settings . SaveOrUpdateKeypair ( kp )
2023-05-16 10:48:00 +00:00
if err != nil {
2023-06-28 19:45:36 +00:00
return nil , err
2023-05-16 10:48:00 +00:00
}
2023-07-27 13:20:40 +00:00
// if keypair is retrieved from backed up data, no need for resolving accounts positions
if message . SyncedFrom != accounts . SyncedFromBackup {
// then resolve accounts positions, cause some accounts might be removed
err = m . settings . ResolveAccountsPositions ( message . Clock )
if err != nil {
return nil , err
}
2023-07-19 11:50:16 +00:00
}
2023-05-16 10:48:00 +00:00
for _ , sKc := range message . Keycards {
kc := accounts . Keycard { }
kc . FromSyncKeycard ( sKc )
2023-07-05 12:41:58 +00:00
err = m . settings . SaveOrUpdateKeycard ( kc , message . Clock , false )
if err != nil {
return nil , err
}
2023-06-28 19:45:36 +00:00
kp . Keycards = append ( kp . Keycards , & kc )
2023-05-16 10:48:00 +00:00
}
2023-07-19 11:50:16 +00:00
// getting keypair form the db, cause keypair related accounts positions might be changed
dbKeypair , err = m . settings . GetKeypairByKeyUID ( message . KeyUid )
if err != nil {
return nil , err
}
return dbKeypair , nil
2023-05-16 10:48:00 +00:00
}
2023-07-16 11:11:48 +00:00
func ( m * Messenger ) HandleSyncAccountsPositions ( state * ReceivedMessageState , message protobuf . SyncAccountsPositions ) error {
accs , err := m . handleSyncAccountsPositions ( & message )
if err != nil {
if err == ErrTryingToApplyOldWalletAccountsOrder ||
err == accounts . ErrAccountWrongPosition ||
err == accounts . ErrNotTheSameNumberOdAccountsToApplyReordering ||
err == accounts . ErrNotTheSameAccountsToApplyReordering {
m . logger . Warn ( "syncing accounts order issue" , zap . Error ( err ) )
return nil
}
return err
}
state . Response . AccountsPositions = append ( state . Response . AccountsPositions , accs ... )
return nil
}
2023-06-28 19:45:36 +00:00
func ( m * Messenger ) HandleSyncWatchOnlyAccount ( state * ReceivedMessageState , message protobuf . SyncAccount ) error {
2023-07-27 13:20:40 +00:00
acc , err := m . handleSyncWatchOnlyAccount ( & message , false )
2023-05-16 10:48:00 +00:00
if err != nil {
2023-06-28 19:45:36 +00:00
if err == ErrTryingToStoreOldWalletAccount {
return nil
}
2023-05-16 10:48:00 +00:00
return err
}
2023-06-28 19:45:36 +00:00
state . Response . WatchOnlyAccounts = append ( state . Response . WatchOnlyAccounts , acc )
2023-05-16 10:48:00 +00:00
return nil
}
2023-08-09 11:14:17 +00:00
func ( m * Messenger ) HandleSyncKeypair ( state * ReceivedMessageState , message protobuf . SyncKeypair , fromLocalPairing bool ) error {
kp , err := m . handleSyncKeypair ( & message , fromLocalPairing )
2023-05-16 10:48:00 +00:00
if err != nil {
2023-06-28 19:45:36 +00:00
if err == ErrTryingToStoreOldKeypair {
return nil
}
2023-05-16 10:48:00 +00:00
return err
}
state . Response . Keypairs = append ( state . Response . Keypairs , kp )
return nil
}
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
}