203 lines
5.1 KiB
Go
203 lines
5.1 KiB
Go
package protocol
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/ecdsa"
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
|
|
gethcommon "github.com/ethereum/go-ethereum/common"
|
|
"github.com/status-im/status-go/eth-node/crypto"
|
|
"github.com/status-im/status-go/protocol/common"
|
|
"github.com/status-im/status-go/protocol/protobuf"
|
|
)
|
|
|
|
// SendPinMessage sends the PinMessage to the corresponding chat
|
|
func (m *Messenger) SendPinMessage(ctx context.Context, message *common.PinMessage) (*MessengerResponse, error) {
|
|
m.mutex.Lock()
|
|
defer m.mutex.Unlock()
|
|
return m.sendPinMessage(ctx, message)
|
|
}
|
|
|
|
func (m *Messenger) sendPinMessage(ctx context.Context, message *common.PinMessage) (*MessengerResponse, error) {
|
|
var response MessengerResponse
|
|
|
|
// A valid added chat is required.
|
|
chat, ok := m.allChats.Load(message.ChatId)
|
|
if !ok {
|
|
return nil, errors.New("chat not found")
|
|
}
|
|
|
|
if chat.CommunityChat() {
|
|
community, err := m.communitiesManager.GetByIDString(chat.CommunityID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
hasPermission := community.IsPrivilegedMember(&m.identity.PublicKey)
|
|
pinMessageAllowed := community.AllowsAllMembersToPinMessage()
|
|
canPost, err := community.CanPost(&m.identity.PublicKey, chat.CommunityChatID(), nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !canPost && !pinMessageAllowed && !hasPermission {
|
|
return nil, errors.New("can't pin message")
|
|
}
|
|
}
|
|
|
|
err := m.handleStandaloneChatIdentity(chat)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = extendPinMessageFromChat(message, chat, &m.identity.PublicKey, m.getTimesource())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
message.ID, err = generatePinMessageID(&m.identity.PublicKey, message, chat)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
encodedMessage, err := m.encodeChatEntity(chat, message)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rawMessage := common.RawMessage{
|
|
LocalChatID: chat.ID,
|
|
Payload: encodedMessage,
|
|
MessageType: protobuf.ApplicationMetadataMessage_PIN_MESSAGE,
|
|
SkipGroupMessageWrap: true,
|
|
ResendAutomatically: true,
|
|
}
|
|
_, err = m.dispatchMessage(ctx, rawMessage)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = m.persistence.SavePinMessages([]*common.PinMessage{message})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if message.Pinned {
|
|
id, err := generatePinMessageNotificationID(&m.identity.PublicKey, message, chat)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
chatMessage := &common.Message{
|
|
ChatMessage: &protobuf.ChatMessage{
|
|
Clock: message.Clock,
|
|
Timestamp: m.getTimesource().GetCurrentTime(),
|
|
ChatId: chat.ID,
|
|
MessageType: message.MessageType,
|
|
ResponseTo: message.MessageId,
|
|
ContentType: protobuf.ChatMessage_SYSTEM_MESSAGE_PINNED_MESSAGE,
|
|
},
|
|
WhisperTimestamp: m.getTimesource().GetCurrentTime(),
|
|
ID: id,
|
|
LocalChatID: chat.ID,
|
|
From: m.myHexIdentity(),
|
|
}
|
|
|
|
msg := []*common.Message{chatMessage}
|
|
err = m.persistence.SaveMessages(msg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
msg, err = m.pullMessagesAndResponsesFromDB(msg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
response.SetMessages(msg)
|
|
m.prepareMessages(response.messages)
|
|
}
|
|
|
|
response.AddPinMessage(message)
|
|
response.AddChat(chat)
|
|
return &response, m.saveChat(chat)
|
|
}
|
|
|
|
func (m *Messenger) PinnedMessageByChatID(chatID, cursor string, limit int) ([]*common.PinnedMessage, string, error) {
|
|
pinnedMsgs, cursor, err := m.persistence.PinnedMessageByChatID(chatID, cursor, limit)
|
|
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
|
|
if m.httpServer != nil {
|
|
for idx := range pinnedMsgs {
|
|
msg := pinnedMsgs[idx].Message
|
|
m.prepareMessage(msg, m.httpServer)
|
|
pinnedMsgs[idx].Message = msg
|
|
}
|
|
}
|
|
return pinnedMsgs, cursor, nil
|
|
}
|
|
|
|
func (m *Messenger) SavePinMessages(messages []*common.PinMessage) error {
|
|
return m.persistence.SavePinMessages(messages)
|
|
}
|
|
|
|
func generatePinMessageID(pubKey *ecdsa.PublicKey, pm *common.PinMessage, chat *Chat) (string, error) {
|
|
data, err := pinMessageBaseID(pubKey, pm, chat)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
id := sha256.Sum256(data)
|
|
idString := fmt.Sprintf("%x", id)
|
|
|
|
return idString, nil
|
|
}
|
|
|
|
func pinMessageBaseID(pubKey *ecdsa.PublicKey, pm *common.PinMessage, chat *Chat) ([]byte, error) {
|
|
data := gethcommon.FromHex(pm.MessageId)
|
|
|
|
switch {
|
|
case chat.ChatType == ChatTypeOneToOne:
|
|
ourPubKey := crypto.FromECDSAPub(pubKey)
|
|
tmpPubKey, err := chat.PublicKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
theirPubKey := crypto.FromECDSAPub(tmpPubKey)
|
|
|
|
if bytes.Compare(ourPubKey, theirPubKey) < 0 {
|
|
data = append(data, ourPubKey...) // our key
|
|
data = append(data, theirPubKey...) // their key
|
|
} else {
|
|
data = append(data, theirPubKey...) // their key
|
|
data = append(data, ourPubKey...) // our key
|
|
}
|
|
default:
|
|
data = append(data, []byte(chat.ID)...)
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
func generatePinMessageNotificationID(pubKey *ecdsa.PublicKey, pm *common.PinMessage, chat *Chat) (string, error) {
|
|
data, err := pinMessageBaseID(pubKey, pm, chat)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
clockBytes := make([]byte, 8)
|
|
binary.LittleEndian.PutUint64(clockBytes, pm.Clock)
|
|
data = append(data, clockBytes...)
|
|
|
|
id := sha256.Sum256(data)
|
|
idString := fmt.Sprintf("%x", id)
|
|
|
|
return idString, nil
|
|
}
|