mirror of
https://github.com/status-im/status-go.git
synced 2025-01-10 06:36:32 +00:00
7650f3003e
There were a couple of issues on how we handle pinned messages: 1) Clock of the message was only checked when saving, meaning that the client would receive potentially updates that were not to be processed. 2) We relied on the client to generate a notification for a pinned message by sending a normal message through the wire. This PR changes the behavior so that the notification is generated locally, either on response to a network event or client event. 3) When deleting a message, we pull all the replies/pinned notifications and send them over to the client so they know that those messages needs updating.
345 lines
8.7 KiB
Go
345 lines
8.7 KiB
Go
package protocol
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/status-im/status-go/protocol/protobuf"
|
|
"github.com/status-im/status-go/protocol/v1"
|
|
)
|
|
|
|
const maxChatMessageTextLength = 4096
|
|
const maxStatusMessageText = 128
|
|
|
|
// maxWhisperDrift is how many milliseconds we allow the clock value to differ
|
|
// from whisperTimestamp
|
|
const maxWhisperFutureDriftMs uint64 = 120000
|
|
|
|
func validateClockValue(clock uint64, whisperTimestamp uint64) error {
|
|
if clock == 0 {
|
|
return errors.New("clock can't be 0")
|
|
}
|
|
|
|
if clock > whisperTimestamp && clock-whisperTimestamp > maxWhisperFutureDriftMs {
|
|
return errors.New("clock value too high")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ValidateMembershipUpdateMessage(message *protocol.MembershipUpdateMessage, timeNowMs uint64) error {
|
|
|
|
for _, e := range message.Events {
|
|
if err := validateClockValue(e.ClockValue, timeNowMs); err != nil {
|
|
return err
|
|
}
|
|
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ValidateStatusUpdate(message protobuf.StatusUpdate) error {
|
|
if message.Clock == 0 {
|
|
return errors.New("clock can't be 0")
|
|
}
|
|
|
|
if message.StatusType == protobuf.StatusUpdate_UNKNOWN_STATUS_TYPE {
|
|
return errors.New("unknown status type")
|
|
}
|
|
|
|
if len([]rune(message.CustomText)) > maxStatusMessageText {
|
|
return fmt.Errorf("custom text shouldn't be longer than %d", maxStatusMessageText)
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func ValidateEditMessage(message protobuf.EditMessage) error {
|
|
if message.Clock == 0 {
|
|
return errors.New("clock can't be 0")
|
|
}
|
|
if len(message.ChatId) == 0 {
|
|
return errors.New("chat-id can't be empty")
|
|
}
|
|
if len(message.MessageId) == 0 {
|
|
return errors.New("message-id can't be empty")
|
|
}
|
|
|
|
if message.MessageType == protobuf.MessageType_UNKNOWN_MESSAGE_TYPE || message.MessageType == protobuf.MessageType_SYSTEM_MESSAGE_PRIVATE_GROUP {
|
|
return errors.New("unknown message type")
|
|
}
|
|
|
|
return ValidateText(message.Text)
|
|
}
|
|
|
|
func ValidateDeleteMessage(message protobuf.DeleteMessage) error {
|
|
if len(message.ChatId) == 0 {
|
|
return errors.New("chat-id can't be empty")
|
|
}
|
|
if len(message.MessageId) == 0 {
|
|
return errors.New("message-id can't be empty")
|
|
}
|
|
|
|
if message.MessageType == protobuf.MessageType_UNKNOWN_MESSAGE_TYPE || message.MessageType == protobuf.MessageType_SYSTEM_MESSAGE_PRIVATE_GROUP {
|
|
return errors.New("unknown message type")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ValidateDeleteForMeMessage(message protobuf.DeleteForMeMessage) error {
|
|
if len(message.MessageId) == 0 {
|
|
return errors.New("message-id can't be empty")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ValidateReceivedPairInstallation(message *protobuf.PairInstallation, whisperTimestamp uint64) error {
|
|
if err := validateClockValue(message.Clock, whisperTimestamp); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(strings.TrimSpace(message.Name)) == 0 {
|
|
return errors.New("name can't be empty")
|
|
}
|
|
|
|
if len(strings.TrimSpace(message.DeviceType)) == 0 {
|
|
return errors.New("device type can't be empty")
|
|
}
|
|
|
|
if len(strings.TrimSpace(message.InstallationId)) == 0 {
|
|
return errors.New("installationId can't be empty")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ValidateReceivedSendTransaction(message *protobuf.SendTransaction, whisperTimestamp uint64) error {
|
|
if err := validateClockValue(message.Clock, whisperTimestamp); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(strings.TrimSpace(message.TransactionHash)) == 0 {
|
|
return errors.New("transaction hash can't be empty")
|
|
}
|
|
|
|
if message.Signature == nil {
|
|
return errors.New("signature can't be nil")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ValidateReceivedRequestAddressForTransaction(message *protobuf.RequestAddressForTransaction, whisperTimestamp uint64) error {
|
|
if err := validateClockValue(message.Clock, whisperTimestamp); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(strings.TrimSpace(message.Value)) == 0 {
|
|
return errors.New("value can't be empty")
|
|
}
|
|
|
|
_, err := strconv.ParseFloat(message.Value, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ValidateReceivedRequestTransaction(message *protobuf.RequestTransaction, whisperTimestamp uint64) error {
|
|
if err := validateClockValue(message.Clock, whisperTimestamp); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(strings.TrimSpace(message.Value)) == 0 {
|
|
return errors.New("value can't be empty")
|
|
}
|
|
|
|
if len(strings.TrimSpace(message.Address)) == 0 {
|
|
return errors.New("address can't be empty")
|
|
}
|
|
|
|
_, err := strconv.ParseFloat(message.Value, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ValidateReceivedAcceptRequestAddressForTransaction(message *protobuf.AcceptRequestAddressForTransaction, whisperTimestamp uint64) error {
|
|
if err := validateClockValue(message.Clock, whisperTimestamp); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(message.Id) == 0 {
|
|
return errors.New("messageID can't be empty")
|
|
}
|
|
|
|
if len(strings.TrimSpace(message.Address)) == 0 {
|
|
return errors.New("address can't be empty")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ValidateReceivedDeclineRequestAddressForTransaction(message *protobuf.DeclineRequestAddressForTransaction, whisperTimestamp uint64) error {
|
|
if err := validateClockValue(message.Clock, whisperTimestamp); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(message.Id) == 0 {
|
|
return errors.New("messageID can't be empty")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ValidateReceivedDeclineRequestTransaction(message *protobuf.DeclineRequestTransaction, whisperTimestamp uint64) error {
|
|
if err := validateClockValue(message.Clock, whisperTimestamp); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(message.Id) == 0 {
|
|
return errors.New("messageID can't be empty")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ValidateText(text string) error {
|
|
if len(strings.TrimSpace(text)) == 0 {
|
|
return errors.New("text can't be empty")
|
|
}
|
|
|
|
if len([]rune(text)) > maxChatMessageTextLength {
|
|
return fmt.Errorf("text shouldn't be longer than %d", maxChatMessageTextLength)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ValidateReceivedChatMessage(message *protobuf.ChatMessage, whisperTimestamp uint64) error {
|
|
if err := validateClockValue(message.Clock, whisperTimestamp); err != nil {
|
|
return err
|
|
}
|
|
|
|
if message.Timestamp == 0 {
|
|
return errors.New("timestamp can't be 0")
|
|
}
|
|
|
|
if message.ContentType != protobuf.ChatMessage_DISCORD_MESSAGE && (message.ContentType != protobuf.ChatMessage_IMAGE || message.Text != "") {
|
|
if err := ValidateText(message.Text); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if len(message.ChatId) == 0 {
|
|
return errors.New("chatId can't be empty")
|
|
}
|
|
|
|
if message.MessageType == protobuf.MessageType_UNKNOWN_MESSAGE_TYPE || message.MessageType == protobuf.MessageType_SYSTEM_MESSAGE_PRIVATE_GROUP {
|
|
return errors.New("unknown message type")
|
|
}
|
|
|
|
switch message.ContentType {
|
|
case protobuf.ChatMessage_UNKNOWN_CONTENT_TYPE:
|
|
return errors.New("unknown content type")
|
|
|
|
case protobuf.ChatMessage_TRANSACTION_COMMAND:
|
|
return errors.New("can't receive request address for transaction from others")
|
|
|
|
case protobuf.ChatMessage_STICKER:
|
|
if message.Payload == nil {
|
|
return errors.New("no sticker content")
|
|
}
|
|
sticker := message.GetSticker()
|
|
if sticker == nil {
|
|
return errors.New("no sticker content")
|
|
}
|
|
if len(sticker.Hash) == 0 {
|
|
return errors.New("sticker hash not set")
|
|
}
|
|
|
|
case protobuf.ChatMessage_IMAGE:
|
|
if message.Payload == nil {
|
|
return errors.New("no image content")
|
|
}
|
|
image := message.GetImage()
|
|
if image == nil {
|
|
return errors.New("no image content")
|
|
}
|
|
if len(image.Payload) == 0 {
|
|
return errors.New("image payload empty")
|
|
}
|
|
if image.Type == protobuf.ImageType_UNKNOWN_IMAGE_TYPE {
|
|
return errors.New("image type unknown")
|
|
}
|
|
}
|
|
|
|
if message.ContentType == protobuf.ChatMessage_AUDIO {
|
|
if message.Payload == nil {
|
|
return errors.New("no audio content")
|
|
}
|
|
audio := message.GetAudio()
|
|
if audio == nil {
|
|
return errors.New("no audio content")
|
|
}
|
|
if len(audio.Payload) == 0 {
|
|
return errors.New("audio payload empty")
|
|
}
|
|
|
|
if audio.Type == protobuf.AudioMessage_UNKNOWN_AUDIO_TYPE {
|
|
return errors.New("audio type unknown")
|
|
}
|
|
}
|
|
|
|
if message.ContentType == protobuf.ChatMessage_SYSTEM_MESSAGE_CONTENT_PRIVATE_GROUP {
|
|
return errors.New("system message content type not allowed")
|
|
}
|
|
|
|
if err := ValidateDisplayName(&message.DisplayName); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ValidateReceivedEmojiReaction(emoji *protobuf.EmojiReaction, whisperTimestamp uint64) error {
|
|
if err := validateClockValue(emoji.Clock, whisperTimestamp); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(emoji.MessageId) == 0 {
|
|
return errors.New("message-id can't be empty")
|
|
}
|
|
|
|
if len(emoji.ChatId) == 0 {
|
|
return errors.New("chat-id can't be empty")
|
|
}
|
|
|
|
if emoji.Type == protobuf.EmojiReaction_UNKNOWN_EMOJI_REACTION_TYPE {
|
|
return errors.New("unknown emoji reaction type")
|
|
}
|
|
|
|
if emoji.MessageType == protobuf.MessageType_UNKNOWN_MESSAGE_TYPE {
|
|
return errors.New("unknown message type")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ValidateReceivedGroupChatInvitation(invitation *protobuf.GroupChatInvitation) error {
|
|
|
|
if len(invitation.ChatId) == 0 {
|
|
return errors.New("chat-id can't be empty")
|
|
}
|
|
|
|
return nil
|
|
}
|