Эх сурвалжийг харах

Fix accepting group invites in encrypted rooms

Tulir Asokan 3 жил өмнө
parent
commit
973f80d7a7
2 өөрчлөгдсөн 56 нэмэгдсэн , 45 устгасан
  1. 39 40
      commands.go
  2. 17 5
      portal.go

+ 39 - 40
commands.go

@@ -18,6 +18,7 @@ package main
 
 import (
 	"context"
+	"encoding/json"
 	"errors"
 	"fmt"
 	"html"
@@ -27,6 +28,7 @@ import (
 	"strings"
 
 	"github.com/skip2/go-qrcode"
+	"github.com/tidwall/gjson"
 
 	"maunium.net/go/maulogger/v2"
 
@@ -310,19 +312,51 @@ func (handler *CommandHandler) CommandJoin(ce *CommandEvent) {
 	ce.Reply("Successfully joined group `%s`, the portal should be created momentarily", jid)
 }
 
+func tryDecryptEvent(crypto Crypto, evt *event.Event) (json.RawMessage, error) {
+	var data json.RawMessage
+	if evt.Type != event.EventEncrypted {
+		data = evt.Content.VeryRaw
+	} else {
+		err := evt.Content.ParseRaw(evt.Type)
+		if err != nil && !errors.Is(err, event.ErrContentAlreadyParsed) {
+			return nil, err
+		}
+		decrypted, err := crypto.Decrypt(evt)
+		if err != nil {
+			return nil, err
+		}
+		data = decrypted.Content.VeryRaw
+	}
+	return data, nil
+}
+
+func parseInviteMeta(data json.RawMessage) (*InviteMeta, error) {
+	result := gjson.GetBytes(data, escapedInviteMetaField)
+	if !result.Exists() || !result.IsObject() {
+		return nil, nil
+	}
+	var meta InviteMeta
+	err := json.Unmarshal([]byte(result.Raw), &meta)
+	if err != nil {
+		return nil, nil
+	}
+	return &meta, nil
+}
+
 func (handler *CommandHandler) CommandAccept(ce *CommandEvent) {
 	if ce.Portal == nil || len(ce.ReplyTo) == 0 {
 		ce.Reply("You must reply to a group invite message when using this command.")
 	} else if evt, err := ce.Portal.MainIntent().GetEvent(ce.RoomID, ce.ReplyTo); err != nil {
 		handler.log.Errorln("Failed to get event %s to handle !wa accept command: %v", ce.ReplyTo, err)
 		ce.Reply("Failed to get reply event")
-	} else if meta, ok := evt.Content.Raw[inviteMetaField].(map[string]interface{}); !ok {
-		ce.Reply("That doesn't look like a group invite message.")
-	} else if jid, inviter, code, expiration, ok := parseInviteMeta(meta); !ok {
+	} else if rawContent, err := tryDecryptEvent(ce.Bridge.Crypto, evt); err != nil {
+		handler.log.Errorln("Failed to decrypt event %s to handle !wa accept command: %v", ce.ReplyTo, err)
+		ce.Reply("Failed to decrypt reply event")
+	} else if meta, err := parseInviteMeta(rawContent); err != nil || meta == nil {
 		ce.Reply("That doesn't look like a group invite message.")
-	} else if inviter.User == ce.User.JID.User {
+	} else if meta.Inviter.User == ce.User.JID.User {
 		ce.Reply("You can't accept your own invites")
-	} else if err = ce.User.Client.JoinGroupWithInvite(jid, inviter, code, expiration); err != nil {
+	} else if err = ce.User.Client.JoinGroupWithInvite(meta.JID, meta.Inviter, meta.Code, meta.Expiration); err != nil {
 		ce.Reply("Failed to accept group invite: %v", err)
 	} else {
 		ce.Reply("Successfully accepted the invite, the portal should be created momentarily")
@@ -414,41 +448,6 @@ func (handler *CommandHandler) CommandCreate(ce *CommandEvent) {
 	ce.Reply("Successfully created WhatsApp group %s", portal.Key.JID)
 }
 
-func parseInviteMeta(meta map[string]interface{}) (jid, inviter types.JID, code string, expiration int64, ok bool) {
-	var fieldFound bool
-	code, fieldFound = meta["code"].(string)
-	if !fieldFound {
-		return
-	}
-	expirationStr, fieldFound := meta["expiration"].(string)
-	if !fieldFound {
-		return
-	}
-	inviterStr, fieldFound := meta["inviter"].(string)
-	if !fieldFound {
-		return
-	}
-	jidStr, fieldFound := meta["jid"].(string)
-	if !fieldFound {
-		return
-	}
-	var err error
-	expiration, err = strconv.ParseInt(expirationStr, 10, 64)
-	if err != nil {
-		return
-	}
-	inviter, err = types.ParseJID(inviterStr)
-	if err != nil {
-		return
-	}
-	jid, err = types.ParseJID(jidStr)
-	if err != nil {
-		return
-	}
-	ok = true
-	return
-}
-
 const cmdSetPowerLevelHelp = `set-pl [user ID] <power level> - Change the power level in a portal room. Only for bridge admins.`
 
 func (handler *CommandHandler) CommandSetPowerLevel(ce *CommandEvent) {

+ 17 - 5
portal.go

@@ -1667,6 +1667,14 @@ func (portal *Portal) convertLocationMessage(intent *appservice.IntentAPI, msg *
 
 const inviteMsg = `%s<hr/>This invitation to join "%s" expires at %s. Reply to this message with <code>!wa accept</code> to accept the invite.`
 const inviteMetaField = "fi.mau.whatsapp.invite"
+const escapedInviteMetaField = `fi\.mau\.whatsapp\.invite`
+
+type InviteMeta struct {
+	JID        types.JID `json:"jid"`
+	Code       string    `json:"code"`
+	Expiration int64     `json:"expiration,string"`
+	Inviter    types.JID `json:"inviter"`
+}
 
 func (portal *Portal) convertGroupInviteMessage(intent *appservice.IntentAPI, info *types.MessageInfo, msg *waProto.GroupInviteMessage) *ConvertedMessage {
 	expiry := time.Unix(msg.GetInviteExpiration(), 0)
@@ -1677,12 +1685,16 @@ func (portal *Portal) convertGroupInviteMessage(intent *appservice.IntentAPI, in
 		Format:        event.FormatHTML,
 		FormattedBody: htmlMessage,
 	}
+	groupJID, err := types.ParseJID(msg.GetGroupJid())
+	if err != nil {
+		portal.log.Errorfln("Failed to parse invite group JID: %v", err)
+	}
 	extraAttrs := map[string]interface{}{
-		inviteMetaField: map[string]interface{}{
-			"jid":        msg.GetGroupJid(),
-			"code":       msg.GetInviteCode(),
-			"expiration": strconv.FormatInt(msg.GetInviteExpiration(), 10),
-			"inviter":    info.Sender.ToNonAD().String(),
+		inviteMetaField: InviteMeta{
+			JID:        groupJID,
+			Code:       msg.GetInviteCode(),
+			Expiration: msg.GetInviteExpiration(),
+			Inviter:    info.Sender.ToNonAD(),
 		},
 	}
 	return &ConvertedMessage{