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 }