diff --git a/roomserver/internal/perform/perform_invite.go b/roomserver/internal/perform/perform_invite.go index 483e78c3f..718192bf7 100644 --- a/roomserver/internal/perform/perform_invite.go +++ b/roomserver/internal/perform/perform_invite.go @@ -18,6 +18,10 @@ import ( "context" "fmt" + "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/util" + log "github.com/sirupsen/logrus" + federationAPI "github.com/matrix-org/dendrite/federationapi/api" "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/roomserver/internal/helpers" @@ -27,9 +31,6 @@ import ( "github.com/matrix-org/dendrite/roomserver/storage/shared" "github.com/matrix-org/dendrite/roomserver/types" "github.com/matrix-org/dendrite/setup/config" - "github.com/matrix-org/gomatrixserverlib" - "github.com/matrix-org/util" - log "github.com/sirupsen/logrus" ) type Inviter struct { diff --git a/roomserver/internal/query/query.go b/roomserver/internal/query/query.go index f3061c222..ee73dcd5c 100644 --- a/roomserver/internal/query/query.go +++ b/roomserver/internal/query/query.go @@ -24,6 +24,7 @@ import ( "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" "github.com/sirupsen/logrus" + "github.com/tidwall/gjson" "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/internal/caching" @@ -301,6 +302,41 @@ func (r *Queryer) QueryMembershipsForRoom( if membershipEventNID == 0 { response.HasBeenInRoom = false response.JoinEvents = nil + + // The sender didn't join this room, maybe a pending invite? + var data []byte + data, err = r.DB.GetInvitesJSON(ctx, request.Sender, info.RoomNID) + if err != nil { + logrus.WithError(err).Error("Failed to get invites JSON") + return nil + } + + // For now, we only care about 1:1 rooms + if !gjson.GetBytes(data, "content.is_direct").Bool() { + return nil + } + + // reconstruct membership events + for _, res := range gjson.GetBytes(data, "unsigned.invite_room_state").Array() { + sKey := res.Get("state_key").Str + if res.Get("type").Str == gomatrixserverlib.MRoomMember && sKey != "" { + ev := gomatrixserverlib.ClientEvent{ + Content: gomatrixserverlib.RawJSON(res.Get("content").Raw), + RoomID: request.RoomID, + StateKey: &sKey, + Type: gomatrixserverlib.MRoomMember, + Sender: res.Get("sender").Str, + } + // Add the unsigned data, as this seems to make Element Web happy and put the + // room under "People" + if sKey == request.Sender { + ev.Unsigned = gomatrixserverlib.RawJSON(gjson.GetBytes(data, "unsigned").Raw) + } + response.JoinEvents = append(response.JoinEvents, ev) + } + } + response.HasBeenInRoom = response.JoinEvents != nil && len(response.JoinEvents) > 0 + return nil } diff --git a/roomserver/storage/interface.go b/roomserver/storage/interface.go index b12025c41..84ef49224 100644 --- a/roomserver/storage/interface.go +++ b/roomserver/storage/interface.go @@ -17,10 +17,11 @@ package storage import ( "context" + "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/dendrite/roomserver/storage/shared" "github.com/matrix-org/dendrite/roomserver/storage/tables" "github.com/matrix-org/dendrite/roomserver/types" - "github.com/matrix-org/gomatrixserverlib" ) type Database interface { @@ -168,4 +169,6 @@ type Database interface { ForgetRoom(ctx context.Context, userID, roomID string, forget bool) error GetHistoryVisibilityState(ctx context.Context, roomInfo *types.RoomInfo, eventID string, domain string) ([]*gomatrixserverlib.Event, error) + + GetInvitesJSON(ctx context.Context, userID string, nid types.RoomNID) ([]byte, error) } diff --git a/roomserver/storage/postgres/invite_table.go b/roomserver/storage/postgres/invite_table.go index 4cddfe2e9..880e68012 100644 --- a/roomserver/storage/postgres/invite_table.go +++ b/roomserver/storage/postgres/invite_table.go @@ -75,10 +75,16 @@ const updateInviteRetiredSQL = "" + " WHERE room_nid = $1 AND target_nid = $2 AND NOT retired" + " RETURNING invite_event_id" +const selectInviteJSONSQL = "" + + "SELECT invite_event_json FROM roomserver_invites" + + " WHERE target_nid = $1 and room_nid = $2" + + " AND NOT retired" + type inviteStatements struct { insertInviteEventStmt *sql.Stmt selectInviteActiveForUserInRoomStmt *sql.Stmt updateInviteRetiredStmt *sql.Stmt + selectInviteJSONStmt *sql.Stmt } func CreateInvitesTable(db *sql.DB) error { @@ -93,6 +99,7 @@ func PrepareInvitesTable(db *sql.DB) (tables.Invites, error) { {&s.insertInviteEventStmt, insertInviteEventSQL}, {&s.selectInviteActiveForUserInRoomStmt, selectInviteActiveForUserInRoomSQL}, {&s.updateInviteRetiredStmt, updateInviteRetiredSQL}, + {&s.selectInviteJSONStmt, selectInviteJSONSQL}, }.Prepare(db) } @@ -163,3 +170,13 @@ func (s *inviteStatements) SelectInviteActiveForUserInRoom( } return result, eventIDs, rows.Err() } + +func (s *inviteStatements) SelectInviteJSON( + ctx context.Context, txn *sql.Tx, + targetUserNID types.EventStateKeyNID, roomNID types.RoomNID, +) ([]byte, error) { + stmt := sqlutil.TxStmt(txn, s.selectInviteJSONStmt) + var resBytes []byte + err := stmt.QueryRowContext(ctx, targetUserNID, roomNID).Scan(&resBytes) + return resBytes, err +} diff --git a/roomserver/storage/shared/membership_updater.go b/roomserver/storage/shared/membership_updater.go index 07fb697f9..73e8c607b 100644 --- a/roomserver/storage/shared/membership_updater.go +++ b/roomserver/storage/shared/membership_updater.go @@ -5,9 +5,10 @@ import ( "database/sql" "fmt" + "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/dendrite/roomserver/storage/tables" "github.com/matrix-org/dendrite/roomserver/types" - "github.com/matrix-org/gomatrixserverlib" ) type MembershipUpdater struct { diff --git a/roomserver/storage/shared/storage.go b/roomserver/storage/shared/storage.go index cbf9c8b20..b9e813335 100644 --- a/roomserver/storage/shared/storage.go +++ b/roomserver/storage/shared/storage.go @@ -7,13 +7,14 @@ import ( "fmt" "sort" + "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/util" + "github.com/tidwall/gjson" + "github.com/matrix-org/dendrite/internal/caching" "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/roomserver/storage/tables" "github.com/matrix-org/dendrite/roomserver/types" - "github.com/matrix-org/gomatrixserverlib" - "github.com/matrix-org/util" - "github.com/tidwall/gjson" ) // Ideally, when we have both events we should redact the event JSON and forget about the redaction, but we currently @@ -1388,6 +1389,14 @@ func (d *Database) loadStateAtSnapshot( return fullState, nil } +func (d *Database) GetInvitesJSON(ctx context.Context, userID string, roomNID types.RoomNID) ([]byte, error) { + userNID, err := d.EventStateKeysTable.SelectEventStateKeyNID(ctx, nil, userID) + if err != nil { + return nil, err + } + return d.InvitesTable.SelectInviteJSON(ctx, nil, userNID, roomNID) +} + type stateEntryListMap []types.StateEntryList func (m stateEntryListMap) lookup(stateBlockNID types.StateBlockNID) (stateEntries []types.StateEntry, ok bool) { diff --git a/roomserver/storage/sqlite3/invite_table.go b/roomserver/storage/sqlite3/invite_table.go index e051d63af..0af0aa0a7 100644 --- a/roomserver/storage/sqlite3/invite_table.go +++ b/roomserver/storage/sqlite3/invite_table.go @@ -60,6 +60,10 @@ const updateInviteRetiredSQL = ` const selectInvitesAboutToRetireSQL = ` SELECT invite_event_id FROM roomserver_invites WHERE room_nid = $1 AND target_nid = $2 AND NOT retired ` +const selectInviteJSONSQL = "" + + "SELECT invite_event_json FROM roomserver_invites" + + " WHERE target_nid = $1 and room_nid = $2" + + " AND NOT retired" type inviteStatements struct { db *sql.DB @@ -67,6 +71,7 @@ type inviteStatements struct { selectInviteActiveForUserInRoomStmt *sql.Stmt updateInviteRetiredStmt *sql.Stmt selectInvitesAboutToRetireStmt *sql.Stmt + selectInviteJSONStmt *sql.Stmt } func CreateInvitesTable(db *sql.DB) error { @@ -84,6 +89,7 @@ func PrepareInvitesTable(db *sql.DB) (tables.Invites, error) { {&s.selectInviteActiveForUserInRoomStmt, selectInviteActiveForUserInRoomSQL}, {&s.updateInviteRetiredStmt, updateInviteRetiredSQL}, {&s.selectInvitesAboutToRetireStmt, selectInvitesAboutToRetireSQL}, + {&s.selectInviteJSONStmt, selectInviteJSONSQL}, }.Prepare(db) } @@ -158,3 +164,13 @@ func (s *inviteStatements) SelectInviteActiveForUserInRoom( } return result, eventIDs, nil } + +func (s *inviteStatements) SelectInviteJSON( + ctx context.Context, txn *sql.Tx, + targetUserNID types.EventStateKeyNID, roomNID types.RoomNID, +) ([]byte, error) { + stmt := sqlutil.TxStmt(txn, s.selectInviteJSONStmt) + var resBytes []byte + err := stmt.QueryRowContext(ctx, targetUserNID, roomNID).Scan(&resBytes) + return resBytes, err +} diff --git a/roomserver/storage/tables/interface.go b/roomserver/storage/tables/interface.go index 0bc389b80..94ee4f8b2 100644 --- a/roomserver/storage/tables/interface.go +++ b/roomserver/storage/tables/interface.go @@ -116,6 +116,10 @@ type Invites interface { UpdateInviteRetired(ctx context.Context, txn *sql.Tx, roomNID types.RoomNID, targetUserNID types.EventStateKeyNID) ([]string, error) // SelectInviteActiveForUserInRoom returns a list of sender state key NIDs and invite event IDs matching those nids. SelectInviteActiveForUserInRoom(ctx context.Context, txn *sql.Tx, targetUserNID types.EventStateKeyNID, roomNID types.RoomNID) ([]types.EventStateKeyNID, []string, error) + SelectInviteJSON( + ctx context.Context, txn *sql.Tx, + targetUserNID types.EventStateKeyNID, roomNID types.RoomNID, + ) ([]byte, error) } type MembershipState int64