github.com/status-im/status-go@v1.1.0/protocol/messenger_messages.go (about)

     1  package protocol
     2  
     3  import (
     4  	"context"
     5  	"crypto/ecdsa"
     6  	"errors"
     7  
     8  	"github.com/golang/protobuf/proto"
     9  
    10  	"github.com/status-im/status-go/protocol/common"
    11  	"github.com/status-im/status-go/protocol/protobuf"
    12  	"github.com/status-im/status-go/protocol/requests"
    13  
    14  	"go.uber.org/zap"
    15  )
    16  
    17  var ErrInvalidEditOrDeleteAuthor = errors.New("sender is not the author of the message")
    18  var ErrInvalidDeleteTypeAuthor = errors.New("message type cannot be deleted")
    19  var ErrInvalidEditContentType = errors.New("only text or emoji messages can be replaced")
    20  var ErrInvalidDeletePermission = errors.New("don't have enough permission to delete")
    21  
    22  func (m *Messenger) EditMessage(ctx context.Context, request *requests.EditMessage) (*MessengerResponse, error) {
    23  	err := request.Validate()
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  	message, err := m.persistence.MessageByID(request.ID.String())
    28  	if err != nil {
    29  		return nil, err
    30  	}
    31  
    32  	if message.From != common.PubkeyToHex(&m.identity.PublicKey) {
    33  		return nil, ErrInvalidEditOrDeleteAuthor
    34  	}
    35  
    36  	if message.ContentType != protobuf.ChatMessage_TEXT_PLAIN && message.ContentType != protobuf.ChatMessage_EMOJI && message.ContentType != protobuf.ChatMessage_IMAGE && message.ContentType != protobuf.ChatMessage_BRIDGE_MESSAGE {
    37  		return nil, ErrInvalidEditContentType
    38  	}
    39  
    40  	// A valid added chat is required.
    41  	chat, ok := m.allChats.Load(message.ChatId)
    42  	if !ok {
    43  		return nil, ErrChatNotFound
    44  	}
    45  
    46  	messages, err := m.getOtherMessagesInAlbum(message, message.LocalChatID)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  
    51  	response := &MessengerResponse{}
    52  
    53  	for _, message := range messages {
    54  		//Add LinkPreviews only to first message
    55  		if len(request.LinkPreviews) > 0 {
    56  			message.LinkPreviews = request.LinkPreviews
    57  		}
    58  		if len(request.StatusLinkPreviews) > 0 {
    59  			message.StatusLinkPreviews = request.StatusLinkPreviews
    60  		}
    61  
    62  		clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
    63  
    64  		editMessage := NewEditMessage()
    65  
    66  		replacedText, err := m.mentionsManager.ReplaceWithPublicKey(message.ChatId, request.Text)
    67  		if err != nil {
    68  			m.logger.Error("failed to replace text with public key", zap.String("chatID", message.ChatId), zap.String("text", request.Text))
    69  			// use original text as fallback
    70  			replacedText = request.Text
    71  		}
    72  		editMessage.Text = replacedText
    73  		editMessage.ContentType = request.ContentType
    74  		editMessage.ChatId = message.ChatId
    75  		editMessage.MessageId = message.ID
    76  		editMessage.Clock = clock
    77  
    78  		// We consider link previews non-critical data, so we do not want to block
    79  		// messages from being sent.
    80  
    81  		unfurledLinks, err := message.ConvertLinkPreviewsToProto()
    82  		if err != nil {
    83  			m.logger.Error("failed to convert link previews", zap.Error(err))
    84  		} else {
    85  			editMessage.UnfurledLinks = unfurledLinks
    86  		}
    87  
    88  		unfurledStatusLinks, err := message.ConvertStatusLinkPreviewsToProto()
    89  		if err != nil {
    90  			m.logger.Error("failed to convert status link previews", zap.Error(err))
    91  		} else {
    92  			editMessage.UnfurledStatusLinks = unfurledStatusLinks
    93  		}
    94  
    95  		err = m.applyEditMessage(editMessage.EditMessage, message)
    96  		if err != nil {
    97  			return nil, err
    98  		}
    99  
   100  		encodedMessage, err := m.encodeChatEntity(chat, editMessage)
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  
   105  		rawMessage := common.RawMessage{
   106  			LocalChatID:          chat.ID,
   107  			Payload:              encodedMessage,
   108  			MessageType:          protobuf.ApplicationMetadataMessage_EDIT_MESSAGE,
   109  			SkipGroupMessageWrap: true,
   110  			ResendType:           chat.DefaultResendType(),
   111  		}
   112  		_, err = m.dispatchMessage(ctx, rawMessage)
   113  		if err != nil {
   114  			return nil, err
   115  		}
   116  
   117  		if chat.LastMessage != nil && chat.LastMessage.ID == message.ID {
   118  			chat.LastMessage = message
   119  			err := m.saveChat(chat)
   120  			if err != nil {
   121  				return nil, err
   122  			}
   123  		}
   124  
   125  		response.AddMessage(message)
   126  	}
   127  
   128  	// pull updated messages
   129  	updatedMessages, err := m.persistence.MessagesByResponseTo(request.ID.String())
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	response.AddMessages(updatedMessages)
   134  	err = m.prepareMessages(response.messages)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  	response.AddChat(chat)
   139  
   140  	return response, nil
   141  }
   142  
   143  func (m *Messenger) CanDeleteMessageForEveryoneInCommunity(communityID string, publicKey *ecdsa.PublicKey) bool {
   144  	if communityID != "" {
   145  		community, err := m.communitiesManager.GetByIDString(communityID)
   146  		if err != nil {
   147  			m.logger.Error("failed to find community", zap.String("communityID", communityID), zap.Error(err))
   148  			return false
   149  		}
   150  		return community.CanDeleteMessageForEveryone(publicKey)
   151  	}
   152  	return false
   153  }
   154  
   155  func (m *Messenger) CanDeleteMessageForEveryoneInPrivateGroupChat(chat *Chat, publicKey *ecdsa.PublicKey) bool {
   156  	group, err := newProtocolGroupFromChat(chat)
   157  	if err != nil {
   158  		m.logger.Error("failed to find group", zap.String("chatID", chat.ID), zap.Error(err))
   159  		return false
   160  	}
   161  	admins := group.Admins()
   162  	return stringSliceContains(admins, common.PubkeyToHex(publicKey))
   163  }
   164  
   165  func (m *Messenger) DeleteMessageAndSend(ctx context.Context, messageID string) (*MessengerResponse, error) {
   166  	message, err := m.persistence.MessageByID(messageID)
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  
   171  	// A valid added chat is required.
   172  	chat, ok := m.allChats.Load(message.ChatId)
   173  	if !ok {
   174  		return nil, ErrChatNotFound
   175  	}
   176  
   177  	var canDeleteMessageForEveryone = false
   178  	var deletedBy string
   179  	if message.From != common.PubkeyToHex(&m.identity.PublicKey) {
   180  		if message.MessageType == protobuf.MessageType_COMMUNITY_CHAT {
   181  			communityID := chat.CommunityID
   182  			canDeleteMessageForEveryone = m.CanDeleteMessageForEveryoneInCommunity(communityID, &m.identity.PublicKey)
   183  			if !canDeleteMessageForEveryone {
   184  				return nil, ErrInvalidDeletePermission
   185  			}
   186  		} else if message.MessageType == protobuf.MessageType_PRIVATE_GROUP {
   187  			canDeleteMessageForEveryone = m.CanDeleteMessageForEveryoneInPrivateGroupChat(chat, &m.identity.PublicKey)
   188  			if !canDeleteMessageForEveryone {
   189  				return nil, ErrInvalidDeletePermission
   190  			}
   191  		}
   192  
   193  		// only add DeletedBy when not deleted by message.From
   194  		deletedBy = contactIDFromPublicKey(m.IdentityPublicKey())
   195  
   196  		if !canDeleteMessageForEveryone {
   197  			return nil, ErrInvalidEditOrDeleteAuthor
   198  		}
   199  	}
   200  
   201  	// Only certain types of messages can be deleted
   202  	if message.ContentType != protobuf.ChatMessage_TEXT_PLAIN &&
   203  		message.ContentType != protobuf.ChatMessage_BRIDGE_MESSAGE &&
   204  		message.ContentType != protobuf.ChatMessage_STICKER &&
   205  		message.ContentType != protobuf.ChatMessage_EMOJI &&
   206  		message.ContentType != protobuf.ChatMessage_IMAGE &&
   207  		message.ContentType != protobuf.ChatMessage_AUDIO {
   208  		return nil, ErrInvalidDeleteTypeAuthor
   209  	}
   210  
   211  	messagesToDelete, err := m.getOtherMessagesInAlbum(message, message.ChatId)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  
   216  	clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
   217  
   218  	deleteMessage := NewDeleteMessage()
   219  	deleteMessage.ChatId = message.ChatId
   220  	deleteMessage.MessageId = messageID
   221  	deleteMessage.Clock = clock
   222  	deleteMessage.DeletedBy = deletedBy
   223  
   224  	encodedMessage, err := m.encodeChatEntity(chat, deleteMessage)
   225  
   226  	if err != nil {
   227  		return nil, err
   228  	}
   229  
   230  	rawMessage := common.RawMessage{
   231  		LocalChatID:          chat.ID,
   232  		Payload:              encodedMessage,
   233  		MessageType:          protobuf.ApplicationMetadataMessage_DELETE_MESSAGE,
   234  		SkipGroupMessageWrap: true,
   235  		ResendType:           chat.DefaultResendType(),
   236  	}
   237  
   238  	_, err = m.dispatchMessage(ctx, rawMessage)
   239  	if err != nil {
   240  		return nil, err
   241  	}
   242  
   243  	response := &MessengerResponse{}
   244  	for _, messageToDelete := range messagesToDelete {
   245  		messageToDelete.Deleted = true
   246  		messageToDelete.DeletedBy = deletedBy
   247  		err = m.persistence.SaveMessages([]*common.Message{messageToDelete})
   248  		if err != nil {
   249  			return nil, err
   250  		}
   251  		response.AddMessage(messageToDelete)
   252  		response.AddRemovedMessage(&RemovedMessage{MessageID: messageToDelete.ID, ChatID: chat.ID, DeletedBy: deletedBy})
   253  
   254  		if chat.LastMessage != nil && chat.LastMessage.ID == messageToDelete.ID {
   255  			chat.LastMessage = messageToDelete
   256  
   257  			err = m.saveChat(chat)
   258  			if err != nil {
   259  				return nil, err
   260  			}
   261  		}
   262  		// pull updated messages
   263  		updatedMessages, err := m.persistence.MessagesByResponseTo(messageToDelete.ID)
   264  		if err != nil {
   265  			return nil, err
   266  		}
   267  
   268  		response.AddMessages(updatedMessages)
   269  		err = m.prepareMessages(response.messages)
   270  		if err != nil {
   271  			return nil, err
   272  		}
   273  	}
   274  
   275  	response.AddChat(chat)
   276  
   277  	return response, nil
   278  }
   279  
   280  func (m *Messenger) DeleteMessageForMeAndSync(ctx context.Context, localChatID string, messageID string) (*MessengerResponse, error) {
   281  	message, err := m.persistence.MessageByID(messageID)
   282  	if err != nil {
   283  		return nil, err
   284  	}
   285  
   286  	// A valid added chat is required.
   287  	chat, ok := m.allChats.Load(localChatID)
   288  	if !ok {
   289  		return nil, ErrChatNotFound
   290  	}
   291  
   292  	// Only certain types of messages can be deleted
   293  	if message.ContentType != protobuf.ChatMessage_TEXT_PLAIN &&
   294  		message.ContentType != protobuf.ChatMessage_STICKER &&
   295  		message.ContentType != protobuf.ChatMessage_EMOJI &&
   296  		message.ContentType != protobuf.ChatMessage_IMAGE &&
   297  		message.ContentType != protobuf.ChatMessage_AUDIO {
   298  		return nil, ErrInvalidDeleteTypeAuthor
   299  	}
   300  
   301  	messagesToDelete, err := m.getOtherMessagesInAlbum(message, localChatID)
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  
   306  	response := &MessengerResponse{}
   307  	for _, messageToDelete := range messagesToDelete {
   308  		messageToDelete.DeletedForMe = true
   309  		err = m.persistence.SaveMessages([]*common.Message{messageToDelete})
   310  		if err != nil {
   311  			return nil, err
   312  		}
   313  
   314  		if chat.LastMessage != nil && chat.LastMessage.ID == messageToDelete.ID {
   315  			chat.LastMessage = messageToDelete
   316  			err = m.saveChat(chat)
   317  			if err != nil {
   318  				return nil, err
   319  			}
   320  		}
   321  
   322  		response.AddMessage(messageToDelete)
   323  
   324  		// pull updated messages
   325  		updatedMessages, err := m.persistence.MessagesByResponseTo(messageToDelete.ID)
   326  		if err != nil {
   327  			return nil, err
   328  		}
   329  
   330  		response.AddMessages(updatedMessages)
   331  
   332  		err = m.prepareMessages(response.messages)
   333  		if err != nil {
   334  			return nil, err
   335  		}
   336  
   337  	}
   338  	response.AddChat(chat)
   339  
   340  	err = m.withChatClock(func(chatID string, clock uint64) error {
   341  		deletedForMeMessage := &protobuf.SyncDeleteForMeMessage{
   342  			MessageId: messageID,
   343  			Clock:     clock,
   344  		}
   345  
   346  		if m.hasPairedDevices() {
   347  			encodedMessage, err2 := proto.Marshal(deletedForMeMessage)
   348  
   349  			if err2 != nil {
   350  				return err2
   351  			}
   352  
   353  			rawMessage := common.RawMessage{
   354  				LocalChatID: chatID,
   355  				Payload:     encodedMessage,
   356  				MessageType: protobuf.ApplicationMetadataMessage_SYNC_DELETE_FOR_ME_MESSAGE,
   357  				ResendType:  common.ResendTypeDataSync,
   358  			}
   359  			_, err2 = m.dispatchMessage(ctx, rawMessage)
   360  			return err2
   361  		}
   362  
   363  		return m.persistence.SaveOrUpdateDeleteForMeMessage(deletedForMeMessage)
   364  	})
   365  
   366  	if err != nil {
   367  		return response, err
   368  	}
   369  
   370  	return response, nil
   371  }
   372  
   373  func (m *Messenger) applyEditMessage(editMessage *protobuf.EditMessage, message *common.Message) error {
   374  	if err := ValidateText(editMessage.Text); err != nil {
   375  		return err
   376  	}
   377  
   378  	if editMessage.ContentType != protobuf.ChatMessage_BRIDGE_MESSAGE {
   379  		message.Text = editMessage.Text
   380  	} else {
   381  		message.GetBridgeMessage().Content = editMessage.Text
   382  	}
   383  
   384  	message.EditedAt = editMessage.Clock
   385  	message.UnfurledLinks = editMessage.UnfurledLinks
   386  	message.UnfurledStatusLinks = editMessage.UnfurledStatusLinks
   387  	if editMessage.ContentType != protobuf.ChatMessage_UNKNOWN_CONTENT_TYPE {
   388  		message.ContentType = editMessage.ContentType
   389  	}
   390  
   391  	// Save original message as edit so we can retrieve history
   392  	if message.EditedAt == 0 {
   393  		originalEdit := NewEditMessage()
   394  		originalEdit.Clock = message.Clock
   395  		originalEdit.LocalChatID = message.LocalChatID
   396  		originalEdit.MessageId = message.ID
   397  		originalEdit.Text = message.Text
   398  		originalEdit.ContentType = message.ContentType
   399  		originalEdit.From = message.From
   400  		originalEdit.UnfurledLinks = message.UnfurledLinks
   401  		originalEdit.UnfurledStatusLinks = message.UnfurledStatusLinks
   402  		err := m.persistence.SaveEdit(originalEdit)
   403  		if err != nil {
   404  			return err
   405  		}
   406  	}
   407  
   408  	err := message.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
   409  	if err != nil {
   410  		return err
   411  	}
   412  
   413  	return m.persistence.SaveMessages([]*common.Message{message})
   414  }
   415  
   416  func (m *Messenger) applyDeleteMessage(messageDeletes []*DeleteMessage, message *common.Message) error {
   417  	if messageDeletes[0].From != message.From {
   418  		return ErrInvalidEditOrDeleteAuthor
   419  	}
   420  
   421  	message.Deleted = true
   422  	message.DeletedBy = messageDeletes[0].DeletedBy
   423  
   424  	err := message.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
   425  	if err != nil {
   426  		return err
   427  	}
   428  
   429  	err = m.persistence.SaveMessages([]*common.Message{message})
   430  	if err != nil {
   431  		return err
   432  	}
   433  
   434  	return nil
   435  }
   436  
   437  func (m *Messenger) applyDeleteForMeMessage(message *common.Message) error {
   438  	message.DeletedForMe = true
   439  
   440  	err := message.PrepareContent(m.myHexIdentity())
   441  	if err != nil {
   442  		return err
   443  	}
   444  
   445  	err = m.persistence.SaveMessages([]*common.Message{message})
   446  	if err != nil {
   447  		return err
   448  	}
   449  
   450  	return nil
   451  }
   452  
   453  func (m *Messenger) addContactRequestPropagatedState(message *common.Message) error {
   454  	chat, ok := m.allChats.Load(message.LocalChatID)
   455  	if !ok {
   456  		return ErrChatNotFound
   457  	}
   458  	if !chat.OneToOne() {
   459  		return nil
   460  	}
   461  
   462  	contact, err := m.BuildContact(&requests.BuildContact{PublicKey: chat.ID})
   463  	if err != nil {
   464  		return err
   465  	}
   466  
   467  	message.ContactRequestPropagatedState = contact.ContactRequestPropagatedState()
   468  	return nil
   469  }
   470  
   471  func (m *Messenger) SendOneToOneMessage(request *requests.SendOneToOneMessage) (*MessengerResponse, error) {
   472  	if err := request.Validate(); err != nil {
   473  		return nil, err
   474  	}
   475  
   476  	chatID, err := request.HexID()
   477  	if err != nil {
   478  		return nil, err
   479  	}
   480  
   481  	_, ok := m.allChats.Load(chatID)
   482  	if !ok {
   483  		// Only one to one chan be muted when it's not in the database
   484  		publicKey, err := common.HexToPubkey(chatID)
   485  		if err != nil {
   486  			return nil, err
   487  		}
   488  
   489  		// Create a one to one chat
   490  		chat := CreateOneToOneChat(chatID, publicKey, m.getTimesource())
   491  		err = m.initChatSyncFields(chat)
   492  		if err != nil {
   493  			return nil, err
   494  		}
   495  		err = m.saveChat(chat)
   496  		if err != nil {
   497  			return nil, err
   498  		}
   499  	}
   500  
   501  	message := common.NewMessage()
   502  	message.Text = request.Message
   503  	message.ChatId = chatID
   504  	message.ContentType = protobuf.ChatMessage_TEXT_PLAIN
   505  
   506  	return m.sendChatMessage(context.Background(), message)
   507  }
   508  
   509  func (m *Messenger) SendGroupChatMessage(request *requests.SendGroupChatMessage) (*MessengerResponse, error) {
   510  	if err := request.Validate(); err != nil {
   511  		return nil, err
   512  	}
   513  
   514  	chatID := request.ID
   515  
   516  	_, ok := m.allChats.Load(chatID)
   517  	if !ok {
   518  		return nil, ErrChatNotFound
   519  	}
   520  
   521  	message := common.NewMessage()
   522  	message.Text = request.Message
   523  	message.ChatId = chatID
   524  	message.ContentType = protobuf.ChatMessage_TEXT_PLAIN
   525  
   526  	return m.sendChatMessage(context.Background(), message)
   527  }
   528  
   529  func (m *Messenger) deleteCommunityMemberMessages(member string, communityID string, deleteMessages []*protobuf.DeleteCommunityMemberMessage) (*MessengerResponse, error) {
   530  	messagesToDelete := deleteMessages
   531  	var err error
   532  	if len(deleteMessages) == 0 {
   533  		messagesToDelete, err = m.persistence.GetCommunityMemberMessagesToDelete(member, communityID)
   534  		if err != nil {
   535  			return nil, err
   536  		}
   537  	}
   538  
   539  	response := &MessengerResponse{}
   540  
   541  	if len(messagesToDelete) == 0 {
   542  		return response, nil
   543  	}
   544  
   545  	for _, messageToDelete := range messagesToDelete {
   546  		updatedMessages, err := m.persistence.MessagesByResponseTo(messageToDelete.Id)
   547  		if err != nil {
   548  			return nil, err
   549  		}
   550  		response.AddMessages(updatedMessages)
   551  	}
   552  
   553  	response.AddDeletedMessages(messagesToDelete)
   554  
   555  	messageIDs := make([]string, 0, len(messagesToDelete))
   556  
   557  	for _, rm := range messagesToDelete {
   558  		messageIDs = append(messageIDs, rm.Id)
   559  	}
   560  
   561  	if err = m.persistence.DeleteMessages(messageIDs); err != nil {
   562  		return nil, err
   563  	}
   564  
   565  	return response, nil
   566  }