diff --git a/clientapi/routing/room_hierarchy.go b/clientapi/routing/room_hierarchy.go index cd53aac330..a1c2a610ae 100644 --- a/clientapi/routing/room_hierarchy.go +++ b/clientapi/routing/room_hierarchy.go @@ -29,17 +29,20 @@ import ( log "github.com/sirupsen/logrus" ) +// For storing pagination information for room hierarchies type RoomHierarchyPaginationCache struct { cache map[string]roomserverAPI.RoomHierarchyWalker mu sync.Mutex } +// Create a new, empty, pagination cache. func NewRoomHierarchyPaginationCache() RoomHierarchyPaginationCache { return RoomHierarchyPaginationCache{ cache: map[string]roomserverAPI.RoomHierarchyWalker{}, } } +// Get a cached page, or nil if there is no associated page in the cache. func (c *RoomHierarchyPaginationCache) Get(token string) *roomserverAPI.RoomHierarchyWalker { c.mu.Lock() defer c.mu.Unlock() @@ -51,6 +54,7 @@ func (c *RoomHierarchyPaginationCache) Get(token string) *roomserverAPI.RoomHier } } +// Add a cache line to the pagination cache. func (c *RoomHierarchyPaginationCache) AddLine(line roomserverAPI.RoomHierarchyWalker) string { c.mu.Lock() defer c.mu.Unlock() @@ -166,6 +170,7 @@ func QueryRoomHierarchy(req *http.Request, device *userapi.Device, roomIDStr str } +// Success response for /_matrix/client/v1/rooms/{roomID}/hierarchy type RoomHierarchyClientResponse struct { Rooms []fclient.MSC2946Room `json:"rooms"` NextBatch string `json:"next_batch,omitempty"` diff --git a/roomserver/api/api.go b/roomserver/api/api.go index c30767a9b7..e2a87906df 100644 --- a/roomserver/api/api.go +++ b/roomserver/api/api.go @@ -124,6 +124,13 @@ type QueryEventsAPI interface { } type QueryRoomHierarchyAPI interface { + // Traverse the room hierarchy using the provided walker up to the provided limit, + // returning a new walker which can be used to fetch the next page. + // + // If limit is -1, this is treated as no limit, and the entire hierarchy will be traversed. + // + // If returned walker is nil, then there are no more rooms left to traverse. This method does not modify the provided walker, so it + // can be cached. QueryNextRoomHierarchyPage(ctx context.Context, walker RoomHierarchyWalker, limit int) ([]fclient.MSC2946Room, *RoomHierarchyWalker, error) } diff --git a/roomserver/api/query.go b/roomserver/api/query.go index e4ac73b898..e6f82a9f18 100644 --- a/roomserver/api/query.go +++ b/roomserver/api/query.go @@ -513,7 +513,8 @@ type QueryRoomHierarchyRequest struct { // // Used for implementing space summaries / room hierarchies // -// Use NewRoomHierarchyWalker on the roomserver API to construct this. +// Use NewRoomHierarchyWalker to construct this, and QueryNextRoomHierarchyPage on the roomserver API +// to traverse the room hierarchy. type RoomHierarchyWalker struct { RootRoomID spec.RoomID Caller types.DeviceOrServerName @@ -530,6 +531,9 @@ type RoomHierarchyWalkerQueuedRoom struct { Vias []string // vias to query this room by } +// Create a new room hierarchy walker, starting from the provided root room ID. +// +// Use the resulting struct with QueryNextRoomHierarchyPage on the roomserver API to traverse the room hierarchy. func NewRoomHierarchyWalker(caller types.DeviceOrServerName, roomID spec.RoomID, suggestedOnly bool, maxDepth int) RoomHierarchyWalker { walker := RoomHierarchyWalker{ RootRoomID: roomID, @@ -547,17 +551,21 @@ func NewRoomHierarchyWalker(caller types.DeviceOrServerName, roomID spec.RoomID, return walker } +// A set of room IDs. type RoomSet map[spec.RoomID]struct{} +// Create a new empty room set. func NewRoomSet() RoomSet { return RoomSet{} } +// Check if a room ID is in a room set. func (s RoomSet) Contains(val spec.RoomID) bool { _, ok := s[val] return ok } +// Add a room ID to a room set. func (s RoomSet) Add(val spec.RoomID) { s[val] = struct{}{} } diff --git a/roomserver/internal/query/query_room_hierarchy.go b/roomserver/internal/query/query_room_hierarchy.go index cab552cdc8..4b1986d4b7 100644 --- a/roomserver/internal/query/query_room_hierarchy.go +++ b/roomserver/internal/query/query_room_hierarchy.go @@ -39,9 +39,13 @@ const ( ConstSpaceParentEventType = "m.space.parent" ) -// Walk the room hierarchy to retrieve room information until either -// no room left, or provided limit reached. If limit provided is -1, then this is -// treated as no limit. +// Traverse the room hierarchy using the provided walker up to the provided limit, +// returning a new walker which can be used to fetch the next page. +// +// If limit is -1, this is treated as no limit, and the entire hierarchy will be traversed. +// +// If returned walker is nil, then there are no more rooms left to traverse. This method does not modify the provided walker, so it +// can be cached. func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker roomserver.RoomHierarchyWalker, limit int) ([]fclient.MSC2946Room, *roomserver.RoomHierarchyWalker, error) { if authorised, _ := authorised(ctx, querier, walker.Caller, walker.RootRoomID, nil); !authorised { return nil, nil, roomserver.ErrRoomUnknownOrNotAllowed{Err: fmt.Errorf("room is unknown/forbidden")} @@ -234,7 +238,7 @@ func authorisedServer(ctx context.Context, querier *Queryer, roomID spec.RoomID, } if rule == spec.Restricted { - allowJoinedToRoomIDs = append(allowJoinedToRoomIDs, restrictedJoinRuleAllowedRooms(ctx, joinRuleEv, "m.room_membership")...) + allowJoinedToRoomIDs = append(allowJoinedToRoomIDs, restrictedJoinRuleAllowedRooms(ctx, joinRuleEv)...) } } @@ -308,7 +312,7 @@ func authorisedUser(ctx context.Context, querier *Queryer, clientCaller *userapi } else if rule == spec.Public || rule == spec.Knock { allowed = true } else if rule == spec.Restricted { - allowedRoomIDs := restrictedJoinRuleAllowedRooms(ctx, joinRuleEv, "m.room_membership") + allowedRoomIDs := restrictedJoinRuleAllowedRooms(ctx, joinRuleEv) // check parent is in the allowed set for _, a := range allowedRoomIDs { if *parentRoomID == a { @@ -342,6 +346,7 @@ func authorisedUser(ctx context.Context, querier *Queryer, clientCaller *userapi return false, false } +// helper function to fetch a state event func stateEvent(ctx context.Context, querier *Queryer, roomID spec.RoomID, evType, stateKey string) *types.HeaderedEvent { var queryRes roomserver.QueryCurrentStateResponse tuple := gomatrixserverlib.StateKeyTuple{ @@ -358,6 +363,7 @@ func stateEvent(ctx context.Context, querier *Queryer, roomID spec.RoomID, evTyp return queryRes.StateEvents[tuple] } +// returns true if the current server is participating in the provided room func roomExists(ctx context.Context, querier *Queryer, roomID spec.RoomID) bool { var queryRes roomserver.QueryServerJoinedToRoomResponse err := querier.QueryServerJoinedToRoom(ctx, &roomserver.QueryServerJoinedToRoomRequest{ @@ -473,6 +479,7 @@ func childReferences(querier *Queryer, suggestedOnly bool, roomID spec.RoomID) ( return el, nil } +// fetch public room information for provided room func publicRoomsChunk(ctx context.Context, querier *Queryer, roomID spec.RoomID) *fclient.PublicRoom { pubRooms, err := roomserver.PopulatePublicRooms(ctx, []string{roomID.String()}, querier) if err != nil { @@ -498,7 +505,8 @@ func stripped(ev gomatrixserverlib.PDU) *fclient.MSC2946StrippedEvent { } } -func restrictedJoinRuleAllowedRooms(ctx context.Context, joinRuleEv *types.HeaderedEvent, allowType string) (allows []spec.RoomID) { +// given join_rule event, return list of rooms where membership of that room allows joining. +func restrictedJoinRuleAllowedRooms(ctx context.Context, joinRuleEv *types.HeaderedEvent) (allows []spec.RoomID) { rule, _ := joinRuleEv.JoinRule() if rule != spec.Restricted { return nil @@ -509,7 +517,7 @@ func restrictedJoinRuleAllowedRooms(ctx context.Context, joinRuleEv *types.Heade return nil } for _, allow := range jrContent.Allow { - if allow.Type == allowType { + if allow.Type == spec.MRoomMembership { allowedRoomID, err := spec.NewRoomID(allow.RoomID) if err != nil { util.GetLogger(ctx).Warnf("invalid room ID '%s' found in join_rule on room %s: %s", allow.RoomID, joinRuleEv.RoomID(), err)