From 2c373db8fcf7fca1b6bd86afe1fb26f3759d420c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 13 Jun 2024 20:46:18 +0300 Subject: [PATCH] Only set force RCS flag if chat has had e2ee tombstone --- database/portal.go | 17 +- database/upgrades/00-latest-revision.sql | 4 +- database/upgrades/10-send-mode.sql | 3 + libgm/gmproto/client.pb.go | 6 +- libgm/gmproto/client.pb.raw | Bin 6318 -> 6324 bytes libgm/gmproto/client.proto | 2 +- libgm/gmproto/conversations.pb.go | 249 ++++++++++++++--------- libgm/gmproto/conversations.pb.raw | Bin 9574 -> 9727 bytes libgm/gmproto/conversations.proto | 7 + portal.go | 38 +++- 10 files changed, 217 insertions(+), 109 deletions(-) create mode 100644 database/upgrades/10-send-mode.sql diff --git a/database/portal.go b/database/portal.go index 48a6a36..20af3b7 100644 --- a/database/portal.go +++ b/database/portal.go @@ -37,18 +37,18 @@ func newPortal(qh *dbutil.QueryHelper[*Portal]) *Portal { } const ( - getAllPortalsQuery = "SELECT id, receiver, self_user, other_user, type, mxid, name, name_set, encrypted, in_space FROM portal" + getAllPortalsQuery = "SELECT id, receiver, self_user, other_user, type, send_mode, force_rcs, mxid, name, name_set, encrypted, in_space FROM portal" getAllPortalsForUserQuery = getAllPortalsQuery + " WHERE receiver=$1" getPortalByKeyQuery = getAllPortalsQuery + " WHERE id=$1 AND receiver=$2" getPortalByOtherUserQuery = getAllPortalsQuery + " WHERE other_user=$1 AND receiver=$2" getPortalByMXIDQuery = getAllPortalsQuery + " WHERE mxid=$1" insertPortalQuery = ` - INSERT INTO portal (id, receiver, self_user, other_user, type, mxid, name, name_set, encrypted, in_space) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) + INSERT INTO portal (id, receiver, self_user, other_user, type, send_mode, force_rcs, mxid, name, name_set, encrypted, in_space) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) ` updatePortalQuery = ` UPDATE portal - SET self_user=$3, other_user=$4, type=$5, mxid=$6, name=$7, name_set=$8, encrypted=$9, in_space=$10 + SET self_user=$3, other_user=$4, type=$5, send_mode=$6, force_rcs=$7, mxid=$8, name=$9, name_set=$10, encrypted=$11, in_space=$12 WHERE id=$1 AND receiver=$2 ` deletePortalQuery = "DELETE FROM portal WHERE id=$1 AND receiver=$2" @@ -96,6 +96,8 @@ type Portal struct { MXID id.RoomID Type gmproto.ConversationType + SendMode gmproto.ConversationSendMode + ForceRCS bool Name string NameSet bool Encrypted bool @@ -104,15 +106,16 @@ type Portal struct { func (portal *Portal) Scan(row dbutil.Scannable) (*Portal, error) { var mxid, selfUserID, otherUserID sql.NullString - var convType int + var convType, sendMode int err := row.Scan( - &portal.ID, &portal.Receiver, &selfUserID, &otherUserID, &convType, &mxid, + &portal.ID, &portal.Receiver, &selfUserID, &otherUserID, &convType, &sendMode, &portal.ForceRCS, &mxid, &portal.Name, &portal.NameSet, &portal.Encrypted, &portal.InSpace, ) if err != nil { return nil, err } portal.Type = gmproto.ConversationType(convType) + portal.SendMode = gmproto.ConversationSendMode(sendMode) portal.MXID = id.RoomID(mxid.String) portal.OutgoingID = selfUserID.String portal.OtherUserID = otherUserID.String @@ -122,7 +125,7 @@ func (portal *Portal) Scan(row dbutil.Scannable) (*Portal, error) { func (portal *Portal) sqlVariables() []any { return []any{ portal.ID, portal.Receiver, dbutil.StrPtr(portal.OutgoingID), dbutil.StrPtr(portal.OtherUserID), - int(portal.Type), dbutil.StrPtr(portal.MXID), + int(portal.Type), int(portal.SendMode), portal.ForceRCS, dbutil.StrPtr(portal.MXID), portal.Name, portal.NameSet, portal.Encrypted, portal.InSpace, } } diff --git a/database/upgrades/00-latest-revision.sql b/database/upgrades/00-latest-revision.sql index 029a75d..1613b65 100644 --- a/database/upgrades/00-latest-revision.sql +++ b/database/upgrades/00-latest-revision.sql @@ -1,4 +1,4 @@ --- v0 -> v9: Latest revision +-- v0 -> v10 (compatible with v9+): Latest revision CREATE TABLE "user" ( -- only: postgres @@ -44,6 +44,8 @@ CREATE TABLE portal ( self_user TEXT, other_user TEXT, type INTEGER NOT NULL, + send_mode INTEGER NOT NULL, + force_rcs BOOLEAN NOT NULL DEFAULT false, mxid TEXT UNIQUE, name TEXT NOT NULL, name_set BOOLEAN NOT NULL DEFAULT false, diff --git a/database/upgrades/10-send-mode.sql b/database/upgrades/10-send-mode.sql new file mode 100644 index 0000000..47a1789 --- /dev/null +++ b/database/upgrades/10-send-mode.sql @@ -0,0 +1,3 @@ +-- v10 (compatible with v9+): Store send mode for portals +ALTER TABLE portal ADD COLUMN send_mode INTEGER NOT NULL DEFAULT 0; +ALTER TABLE portal ADD COLUMN force_rcs BOOLEAN NOT NULL DEFAULT false; diff --git a/libgm/gmproto/client.pb.go b/libgm/gmproto/client.pb.go index b2bd6f4..1204351 100644 --- a/libgm/gmproto/client.pb.go +++ b/libgm/gmproto/client.pb.go @@ -2383,7 +2383,7 @@ type SendMessageRequest struct { MessagePayload *MessagePayload `protobuf:"bytes,3,opt,name=messagePayload,proto3" json:"messagePayload,omitempty"` SIMPayload *SIMPayload `protobuf:"bytes,4,opt,name=SIMPayload,proto3" json:"SIMPayload,omitempty"` TmpID string `protobuf:"bytes,5,opt,name=tmpID,proto3" json:"tmpID,omitempty"` - IsRCS bool `protobuf:"varint,6,opt,name=isRCS,proto3" json:"isRCS,omitempty"` // not sure + ForceRCS bool `protobuf:"varint,6,opt,name=forceRCS,proto3" json:"forceRCS,omitempty"` Reply *ReplyPayload `protobuf:"bytes,8,opt,name=reply,proto3" json:"reply,omitempty"` } @@ -2447,9 +2447,9 @@ func (x *SendMessageRequest) GetTmpID() string { return "" } -func (x *SendMessageRequest) GetIsRCS() bool { +func (x *SendMessageRequest) GetForceRCS() bool { if x != nil { - return x.IsRCS + return x.ForceRCS } return false } diff --git a/libgm/gmproto/client.pb.raw b/libgm/gmproto/client.pb.raw index 01ccd2406ef906b1aac6385be7c42b9da6743051..2c85f2470ca3259c6a754035778c3022c2d5b15d 100644 GIT binary patch delta 43 ucmZ2yxW#aTnh@iJ$?8I conversations.ContactNumber - 20, // 1: conversations.Message.msgType:type_name -> conversations.MsgType - 21, // 2: conversations.Message.messageStatus:type_name -> conversations.MessageStatus - 16, // 3: conversations.Message.messageInfo:type_name -> conversations.MessageInfo - 9, // 4: conversations.Message.reactions:type_name -> conversations.ReactionEntry - 14, // 5: conversations.Message.replyMessage:type_name -> conversations.ReplyMessage - 11, // 6: conversations.ReactionEntry.data:type_name -> conversations.ReactionData - 28, // 7: conversations.CustomEmojiData.innerData:type_name -> conversations.CustomEmojiData.Inner + 8, // 0: conversations.Contact.number:type_name -> conversations.ContactNumber + 21, // 1: conversations.Message.msgType:type_name -> conversations.MsgType + 22, // 2: conversations.Message.messageStatus:type_name -> conversations.MessageStatus + 17, // 3: conversations.Message.messageInfo:type_name -> conversations.MessageInfo + 10, // 4: conversations.Message.reactions:type_name -> conversations.ReactionEntry + 15, // 5: conversations.Message.replyMessage:type_name -> conversations.ReplyMessage + 12, // 6: conversations.ReactionEntry.data:type_name -> conversations.ReactionData + 29, // 7: conversations.CustomEmojiData.innerData:type_name -> conversations.CustomEmojiData.Inner 0, // 8: conversations.ReactionData.type:type_name -> conversations.EmojiType - 10, // 9: conversations.ReactionData.customEmoji:type_name -> conversations.CustomEmojiData - 13, // 10: conversations.EmojiMeta.emojiMetaData:type_name -> conversations.EmojiMetaData - 15, // 11: conversations.ReplyMessage.replyMessageData:type_name -> conversations.ReplyMessageData - 19, // 12: conversations.MessageInfo.messageContent:type_name -> conversations.MessageContent - 17, // 13: conversations.MessageInfo.mediaContent:type_name -> conversations.MediaContent - 5, // 14: conversations.MediaContent.format:type_name -> conversations.MediaFormats - 18, // 15: conversations.MediaContent.dimensions:type_name -> conversations.Dimensions - 3, // 16: conversations.MessageStatus.status:type_name -> conversations.MessageStatusType - 25, // 17: conversations.Conversation.latestMessage:type_name -> conversations.LatestMessage - 4, // 18: conversations.Conversation.status:type_name -> conversations.ConversationStatus - 23, // 19: conversations.Conversation.participants:type_name -> conversations.Participant - 2, // 20: conversations.Conversation.type:type_name -> conversations.ConversationType - 24, // 21: conversations.Participant.ID:type_name -> conversations.SmallInfo - 27, // 22: conversations.Participant.muted:type_name -> conversations.Muted - 1, // 23: conversations.SmallInfo.type:type_name -> conversations.IdentifierType - 26, // 24: conversations.LatestMessage.latestMessageStatus:type_name -> conversations.LatestMessageStatus - 3, // 25: conversations.LatestMessageStatus.status:type_name -> conversations.MessageStatusType - 29, // 26: conversations.CustomEmojiData.Inner.first:type_name -> conversations.CustomEmojiData.Inner.ImageData - 30, // 27: conversations.CustomEmojiData.Inner.second:type_name -> conversations.CustomEmojiData.Inner.WrappedImageData - 31, // 28: conversations.CustomEmojiData.Inner.WrappedImageData.data:type_name -> conversations.CustomEmojiData.Inner.WrappedImageData.ImageData - 29, // [29:29] is the sub-list for method output_type - 29, // [29:29] is the sub-list for method input_type - 29, // [29:29] is the sub-list for extension type_name - 29, // [29:29] is the sub-list for extension extendee - 0, // [0:29] is the sub-list for field type_name + 11, // 9: conversations.ReactionData.customEmoji:type_name -> conversations.CustomEmojiData + 14, // 10: conversations.EmojiMeta.emojiMetaData:type_name -> conversations.EmojiMetaData + 16, // 11: conversations.ReplyMessage.replyMessageData:type_name -> conversations.ReplyMessageData + 20, // 12: conversations.MessageInfo.messageContent:type_name -> conversations.MessageContent + 18, // 13: conversations.MessageInfo.mediaContent:type_name -> conversations.MediaContent + 6, // 14: conversations.MediaContent.format:type_name -> conversations.MediaFormats + 19, // 15: conversations.MediaContent.dimensions:type_name -> conversations.Dimensions + 4, // 16: conversations.MessageStatus.status:type_name -> conversations.MessageStatusType + 26, // 17: conversations.Conversation.latestMessage:type_name -> conversations.LatestMessage + 5, // 18: conversations.Conversation.status:type_name -> conversations.ConversationStatus + 2, // 19: conversations.Conversation.sendMode:type_name -> conversations.ConversationSendMode + 24, // 20: conversations.Conversation.participants:type_name -> conversations.Participant + 3, // 21: conversations.Conversation.type:type_name -> conversations.ConversationType + 25, // 22: conversations.Participant.ID:type_name -> conversations.SmallInfo + 28, // 23: conversations.Participant.muted:type_name -> conversations.Muted + 1, // 24: conversations.SmallInfo.type:type_name -> conversations.IdentifierType + 27, // 25: conversations.LatestMessage.latestMessageStatus:type_name -> conversations.LatestMessageStatus + 4, // 26: conversations.LatestMessageStatus.status:type_name -> conversations.MessageStatusType + 30, // 27: conversations.CustomEmojiData.Inner.first:type_name -> conversations.CustomEmojiData.Inner.ImageData + 31, // 28: conversations.CustomEmojiData.Inner.second:type_name -> conversations.CustomEmojiData.Inner.WrappedImageData + 32, // 29: conversations.CustomEmojiData.Inner.WrappedImageData.data:type_name -> conversations.CustomEmojiData.Inner.WrappedImageData.ImageData + 30, // [30:30] is the sub-list for method output_type + 30, // [30:30] is the sub-list for method input_type + 30, // [30:30] is the sub-list for extension type_name + 30, // [30:30] is the sub-list for extension extendee + 0, // [0:30] is the sub-list for field type_name } func init() { file_conversations_proto_init() } @@ -3080,7 +3139,7 @@ func file_conversations_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_conversations_proto_rawDesc, - NumEnums: 6, + NumEnums: 7, NumMessages: 26, NumExtensions: 0, NumServices: 0, diff --git a/libgm/gmproto/conversations.pb.raw b/libgm/gmproto/conversations.pb.raw index 657a899b62e1967c9dd7f22a242b77ab49e7480d..a9af16b60766c326226fa94eb404090bc7c7308b 100644 GIT binary patch delta 154 zcmaFn_1}AgH!ss>w#~l0R~Uuuxj2eb^HO~CQ&J^_6c{!5jFczO 24*time.Hour { lastMessage, err := portal.bridge.DB.Message.GetLastInChat(ctx, portal.Key) @@ -1486,6 +1506,14 @@ func (portal *Portal) UpdateMetadata(ctx context.Context, user *User, info *gmpr portal.Type = info.Type update = true } + if portal.SendMode != info.SendMode { + portal.zlog.Debug(). + Str("old_send_mode", portal.SendMode.String()). + Str("new_send_mode", info.SendMode.String()). + Msg("Conversation send mode changed") + portal.SendMode = info.SendMode + update = true + } if portal.OutgoingID != info.DefaultOutgoingID { portal.zlog.Debug(). Str("old_id", portal.OutgoingID). @@ -1932,6 +1960,7 @@ func (portal *Portal) uploadMedia(ctx context.Context, intent *appservice.Intent func (portal *Portal) convertMatrixMessage(ctx context.Context, sender *User, content *event.MessageEventContent, raw map[string]any, txnID string) (*gmproto.SendMessageRequest, error) { log := zerolog.Ctx(ctx) + sim := sender.GetSIM(portal.OutgoingID) req := &gmproto.SendMessageRequest{ ConversationID: portal.ID, TmpID: txnID, @@ -1941,7 +1970,7 @@ func (portal *Portal) convertMatrixMessage(ctx context.Context, sender *User, co TmpID2: txnID, ParticipantID: portal.OutgoingID, }, - SIMPayload: sender.GetSIM(portal.OutgoingID).GetSIMData().GetSIMPayload(), + SIMPayload: sim.GetSIMData().GetSIMPayload(), } replyToMXID := content.RelatesTo.GetReplyTo() @@ -1955,7 +1984,12 @@ func (portal *Portal) convertMatrixMessage(ctx context.Context, sender *User, co req.Reply = &gmproto.ReplyPayload{MessageID: replyToMsg.ID} } } - req.IsRCS = portal.Type == gmproto.ConversationType_RCS + req.ForceRCS = portal.Type == gmproto.ConversationType_RCS && + portal.SendMode == gmproto.ConversationSendMode_SEND_MODE_AUTO && + portal.ForceRCS + if req.ForceRCS && !sim.GetRCSChats().GetEnabled() { + log.Warn().Msg("Forcing RCS but RCS is disabled on sim") + } switch content.MsgType { case event.MsgText, event.MsgEmote, event.MsgNotice: