diff options
author | Kegsay <kegan@matrix.org> | 2020-07-02 15:41:18 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-02 15:41:18 +0100 |
commit | 4c1e6597c0ea82f5390b73f35036db58e65542cc (patch) | |
tree | 641e916f8b4f753f5d45ec674f3512fdb9fbb74b /clientapi | |
parent | 55bc82c439057f379361871c863aa9611d70fce2 (diff) |
Replace publicroomsapi with a combination of clientapi/roomserver/currentstateserver (#1174)
* Use content_value instead of membership
* Fix build
* Replace publicroomsapi with a combination of clientapi/roomserver/currentstateserver
- All public rooms paths are now handled by clientapi
- Requests to (un)publish rooms are sent to the roomserver via `PerformPublish`
which are stored in a new `published_table.go`
- Requests for public rooms are handled in clientapi by:
* Fetch all room IDs which are published using `QueryPublishedRooms` on the roomserver.
* Apply pagination parameters to the slice.
* Do a `QueryBulkStateContent` request to the currentstateserver to pull out
required state event *content* (not entire events).
* Aggregate and return the chunk.
Mostly but not fully implemented (DB queries on currentstateserver are missing)
* Fix pq query
* Make postgres work
* Make sqlite work
* Fix tests
* Unbreak pagination tests
* Linting
Diffstat (limited to 'clientapi')
-rw-r--r-- | clientapi/routing/createroom.go | 13 | ||||
-rw-r--r-- | clientapi/routing/directory.go | 90 | ||||
-rw-r--r-- | clientapi/routing/directory_public.go | 373 | ||||
-rw-r--r-- | clientapi/routing/directory_public_test.go | 48 | ||||
-rw-r--r-- | clientapi/routing/membership.go | 33 | ||||
-rw-r--r-- | clientapi/routing/routing.go | 31 |
6 files changed, 586 insertions, 2 deletions
diff --git a/clientapi/routing/createroom.go b/clientapi/routing/createroom.go index 42e1895c..b6a5d122 100644 --- a/clientapi/routing/createroom.go +++ b/clientapi/routing/createroom.go @@ -410,6 +410,19 @@ func createRoom( } } + if r.Visibility == "public" { + // expose this room in the published room list + var pubRes roomserverAPI.PerformPublishResponse + rsAPI.PerformPublish(req.Context(), &roomserverAPI.PerformPublishRequest{ + RoomID: roomID, + Visibility: "public", + }, &pubRes) + if pubRes.Error != nil { + // treat as non-fatal since the room is already made by this point + util.GetLogger(req.Context()).WithError(pubRes.Error).Error("failed to visibility:public") + } + } + response := createRoomResponse{ RoomID: roomID, RoomAlias: roomAlias, diff --git a/clientapi/routing/directory.go b/clientapi/routing/directory.go index 0dc4d560..0f78f4a2 100644 --- a/clientapi/routing/directory.go +++ b/clientapi/routing/directory.go @@ -1,4 +1,4 @@ -// Copyright 2017 Vector Creations Ltd +// Copyright 2020 The Matrix.org Foundation C.I.C. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,10 +20,12 @@ import ( "github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/jsonerror" + currentstateAPI "github.com/matrix-org/dendrite/currentstateserver/api" federationSenderAPI "github.com/matrix-org/dendrite/federationsender/api" "github.com/matrix-org/dendrite/internal/config" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/userapi/api" + userapi "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" ) @@ -232,3 +234,89 @@ func RemoveLocalAlias( JSON: struct{}{}, } } + +type roomVisibility struct { + Visibility string `json:"visibility"` +} + +// GetVisibility implements GET /directory/list/room/{roomID} +func GetVisibility( + req *http.Request, rsAPI roomserverAPI.RoomserverInternalAPI, + roomID string, +) util.JSONResponse { + var res roomserverAPI.QueryPublishedRoomsResponse + err := rsAPI.QueryPublishedRooms(req.Context(), &roomserverAPI.QueryPublishedRoomsRequest{ + RoomID: roomID, + }, &res) + if err != nil { + util.GetLogger(req.Context()).WithError(err).Error("QueryPublishedRooms failed") + return jsonerror.InternalServerError() + } + + var v roomVisibility + if len(res.RoomIDs) == 1 { + v.Visibility = gomatrixserverlib.Public + } else { + v.Visibility = "private" + } + + return util.JSONResponse{ + Code: http.StatusOK, + JSON: v, + } +} + +// SetVisibility implements PUT /directory/list/room/{roomID} +// TODO: Allow admin users to edit the room visibility +func SetVisibility( + req *http.Request, stateAPI currentstateAPI.CurrentStateInternalAPI, rsAPI roomserverAPI.RoomserverInternalAPI, dev *userapi.Device, + roomID string, +) util.JSONResponse { + resErr := checkMemberInRoom(req.Context(), stateAPI, dev.UserID, roomID) + if resErr != nil { + return *resErr + } + + queryEventsReq := roomserverAPI.QueryLatestEventsAndStateRequest{ + RoomID: roomID, + StateToFetch: []gomatrixserverlib.StateKeyTuple{{ + EventType: gomatrixserverlib.MRoomPowerLevels, + StateKey: "", + }}, + } + var queryEventsRes roomserverAPI.QueryLatestEventsAndStateResponse + err := rsAPI.QueryLatestEventsAndState(req.Context(), &queryEventsReq, &queryEventsRes) + if err != nil || len(queryEventsRes.StateEvents) == 0 { + util.GetLogger(req.Context()).WithError(err).Error("could not query events from room") + return jsonerror.InternalServerError() + } + + // NOTSPEC: Check if the user's power is greater than power required to change m.room.aliases event + power, _ := gomatrixserverlib.NewPowerLevelContentFromEvent(queryEventsRes.StateEvents[0].Event) + if power.UserLevel(dev.UserID) < power.EventLevel(gomatrixserverlib.MRoomAliases, true) { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("userID doesn't have power level to change visibility"), + } + } + + var v roomVisibility + if reqErr := httputil.UnmarshalJSONRequest(req, &v); reqErr != nil { + return *reqErr + } + + var publishRes roomserverAPI.PerformPublishResponse + rsAPI.PerformPublish(req.Context(), &roomserverAPI.PerformPublishRequest{ + RoomID: roomID, + Visibility: v.Visibility, + }, &publishRes) + if publishRes.Error != nil { + util.GetLogger(req.Context()).WithError(publishRes.Error).Error("PerformPublish failed") + return publishRes.Error.JSONResponse() + } + + return util.JSONResponse{ + Code: http.StatusOK, + JSON: struct{}{}, + } +} diff --git a/clientapi/routing/directory_public.go b/clientapi/routing/directory_public.go new file mode 100644 index 00000000..6d0db579 --- /dev/null +++ b/clientapi/routing/directory_public.go @@ -0,0 +1,373 @@ +// Copyright 2020 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package routing + +import ( + "context" + "math/rand" + "net/http" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/matrix-org/dendrite/clientapi/httputil" + "github.com/matrix-org/dendrite/clientapi/jsonerror" + currentstateAPI "github.com/matrix-org/dendrite/currentstateserver/api" + "github.com/matrix-org/dendrite/publicroomsapi/types" + roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" + "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/util" +) + +type PublicRoomReq struct { + Since string `json:"since,omitempty"` + Limit int16 `json:"limit,omitempty"` + Filter filter `json:"filter,omitempty"` +} + +type filter struct { + SearchTerms string `json:"generic_search_term,omitempty"` +} + +// GetPostPublicRooms implements GET and POST /publicRooms +func GetPostPublicRooms( + req *http.Request, rsAPI roomserverAPI.RoomserverInternalAPI, stateAPI currentstateAPI.CurrentStateInternalAPI, +) util.JSONResponse { + var request PublicRoomReq + if fillErr := fillPublicRoomsReq(req, &request); fillErr != nil { + return *fillErr + } + response, err := publicRooms(req.Context(), request, rsAPI, stateAPI) + if err != nil { + return jsonerror.InternalServerError() + } + return util.JSONResponse{ + Code: http.StatusOK, + JSON: response, + } +} + +// GetPostPublicRoomsWithExternal is the same as GetPostPublicRooms but also mixes in public rooms from the provider supplied. +func GetPostPublicRoomsWithExternal( + req *http.Request, rsAPI roomserverAPI.RoomserverInternalAPI, stateAPI currentstateAPI.CurrentStateInternalAPI, + fedClient *gomatrixserverlib.FederationClient, extRoomsProvider types.ExternalPublicRoomsProvider, +) util.JSONResponse { + var request PublicRoomReq + if fillErr := fillPublicRoomsReq(req, &request); fillErr != nil { + return *fillErr + } + response, err := publicRooms(req.Context(), request, rsAPI, stateAPI) + if err != nil { + return jsonerror.InternalServerError() + } + + if request.Since != "" { + // TODO: handle pagination tokens sensibly rather than ignoring them. + // ignore paginated requests since we don't handle them yet over federation. + // Only the initial request will contain federated rooms. + return util.JSONResponse{ + Code: http.StatusOK, + JSON: response, + } + } + + // If we have already hit the limit on the number of rooms, bail. + var limit int + if request.Limit > 0 { + limit = int(request.Limit) - len(response.Chunk) + if limit <= 0 { + return util.JSONResponse{ + Code: http.StatusOK, + JSON: response, + } + } + } + + // downcasting `limit` is safe as we know it isn't bigger than request.Limit which is int16 + fedRooms := bulkFetchPublicRoomsFromServers(req.Context(), fedClient, extRoomsProvider.Homeservers(), int16(limit)) + response.Chunk = append(response.Chunk, fedRooms...) + + // de-duplicate rooms with the same room ID. We can join the room via any of these aliases as we know these servers + // are alive and well, so we arbitrarily pick one (purposefully shuffling them to spread the load a bit) + var publicRooms []gomatrixserverlib.PublicRoom + haveRoomIDs := make(map[string]bool) + rand.Shuffle(len(response.Chunk), func(i, j int) { + response.Chunk[i], response.Chunk[j] = response.Chunk[j], response.Chunk[i] + }) + for _, r := range response.Chunk { + if haveRoomIDs[r.RoomID] { + continue + } + haveRoomIDs[r.RoomID] = true + publicRooms = append(publicRooms, r) + } + // sort by member count + sort.SliceStable(publicRooms, func(i, j int) bool { + return publicRooms[i].JoinedMembersCount > publicRooms[j].JoinedMembersCount + }) + + response.Chunk = publicRooms + + return util.JSONResponse{ + Code: http.StatusOK, + JSON: response, + } +} + +// bulkFetchPublicRoomsFromServers fetches public rooms from the list of homeservers. +// Returns a list of public rooms up to the limit specified. +func bulkFetchPublicRoomsFromServers( + ctx context.Context, fedClient *gomatrixserverlib.FederationClient, homeservers []string, limit int16, +) (publicRooms []gomatrixserverlib.PublicRoom) { + // follow pipeline semantics, see https://blog.golang.org/pipelines for more info. + // goroutines send rooms to this channel + roomCh := make(chan gomatrixserverlib.PublicRoom, int(limit)) + // signalling channel to tell goroutines to stop sending rooms and quit + done := make(chan bool) + // signalling to say when we can close the room channel + var wg sync.WaitGroup + wg.Add(len(homeservers)) + // concurrently query for public rooms + for _, hs := range homeservers { + go func(homeserverDomain string) { + defer wg.Done() + util.GetLogger(ctx).WithField("hs", homeserverDomain).Info("Querying HS for public rooms") + fres, err := fedClient.GetPublicRooms(ctx, gomatrixserverlib.ServerName(homeserverDomain), int(limit), "", false, "") + if err != nil { + util.GetLogger(ctx).WithError(err).WithField("hs", homeserverDomain).Warn( + "bulkFetchPublicRoomsFromServers: failed to query hs", + ) + return + } + for _, room := range fres.Chunk { + // atomically send a room or stop + select { + case roomCh <- room: + case <-done: + util.GetLogger(ctx).WithError(err).WithField("hs", homeserverDomain).Info("Interrupted whilst sending rooms") + return + } + } + }(hs) + } + + // Close the room channel when the goroutines have quit so we don't leak, but don't let it stop the in-flight request. + // This also allows the request to fail fast if all HSes experience errors as it will cause the room channel to be + // closed. + go func() { + wg.Wait() + util.GetLogger(ctx).Info("Cleaning up resources") + close(roomCh) + }() + + // fan-in results with timeout. We stop when we reach the limit. +FanIn: + for len(publicRooms) < int(limit) || limit == 0 { + // add a room or timeout + select { + case room, ok := <-roomCh: + if !ok { + util.GetLogger(ctx).Info("All homeservers have been queried, returning results.") + break FanIn + } + publicRooms = append(publicRooms, room) + case <-time.After(15 * time.Second): // we've waited long enough, let's tell the client what we got. + util.GetLogger(ctx).Info("Waited 15s for federated public rooms, returning early") + break FanIn + case <-ctx.Done(): // the client hung up on us, let's stop. + util.GetLogger(ctx).Info("Client hung up, returning early") + break FanIn + } + } + // tell goroutines to stop + close(done) + + return publicRooms +} + +func publicRooms(ctx context.Context, request PublicRoomReq, rsAPI roomserverAPI.RoomserverInternalAPI, + stateAPI currentstateAPI.CurrentStateInternalAPI) (*gomatrixserverlib.RespPublicRooms, error) { + + var response gomatrixserverlib.RespPublicRooms + var limit int16 + var offset int64 + limit = request.Limit + if limit == 0 { + limit = 50 + } + offset, err := strconv.ParseInt(request.Since, 10, 64) + // ParseInt returns 0 and an error when trying to parse an empty string + // In that case, we want to assign 0 so we ignore the error + if err != nil && len(request.Since) > 0 { + util.GetLogger(ctx).WithError(err).Error("strconv.ParseInt failed") + return nil, err + } + + var queryRes roomserverAPI.QueryPublishedRoomsResponse + err = rsAPI.QueryPublishedRooms(ctx, &roomserverAPI.QueryPublishedRoomsRequest{}, &queryRes) + if err != nil { + util.GetLogger(ctx).WithError(err).Error("QueryPublishedRooms failed") + return nil, err + } + response.TotalRoomCountEstimate = len(queryRes.RoomIDs) + + roomIDs, prev, next := sliceInto(queryRes.RoomIDs, offset, limit) + if prev >= 0 { + response.PrevBatch = "T" + strconv.Itoa(prev) + } + if next >= 0 { + response.NextBatch = "T" + strconv.Itoa(next) + } + response.Chunk, err = fillInRooms(ctx, roomIDs, stateAPI) + return &response, err +} + +// fillPublicRoomsReq fills the Limit, Since and Filter attributes of a GET or POST request +// on /publicRooms by parsing the incoming HTTP request +// Filter is only filled for POST requests +func fillPublicRoomsReq(httpReq *http.Request, request *PublicRoomReq) *util.JSONResponse { + if httpReq.Method != "GET" && httpReq.Method != "POST" { + return &util.JSONResponse{ + Code: http.StatusMethodNotAllowed, + JSON: jsonerror.NotFound("Bad method"), + } + } + if httpReq.Method == "GET" { + limit, err := strconv.Atoi(httpReq.FormValue("limit")) + // Atoi returns 0 and an error when trying to parse an empty string + // In that case, we want to assign 0 so we ignore the error + if err != nil && len(httpReq.FormValue("limit")) > 0 { + util.GetLogger(httpReq.Context()).WithError(err).Error("strconv.Atoi failed") + return &util.JSONResponse{ + Code: 400, + JSON: jsonerror.BadJSON("limit param is not a number"), + } + } + request.Limit = int16(limit) + request.Since = httpReq.FormValue("since") + } else { + resErr := httputil.UnmarshalJSONRequest(httpReq, request) + if resErr != nil { + return resErr + } + } + + // strip the 'T' which is only required because when sytest does pagination tests it stops + // iterating when !prev_batch which then fails if prev_batch==0, so add arbitrary text to + // make it truthy not falsey. + request.Since = strings.TrimPrefix(request.Since, "T") + return nil +} + +// due to lots of switches +// nolint:gocyclo +func fillInRooms(ctx context.Context, roomIDs []string, stateAPI currentstateAPI.CurrentStateInternalAPI) ([]gomatrixserverlib.PublicRoom, error) { + avatarTuple := gomatrixserverlib.StateKeyTuple{EventType: "m.room.avatar", StateKey: ""} + nameTuple := gomatrixserverlib.StateKeyTuple{EventType: "m.room.name", StateKey: ""} + canonicalTuple := gomatrixserverlib.StateKeyTuple{EventType: gomatrixserverlib.MRoomCanonicalAlias, StateKey: ""} + topicTuple := gomatrixserverlib.StateKeyTuple{EventType: "m.room.topic", StateKey: ""} + guestTuple := gomatrixserverlib.StateKeyTuple{EventType: "m.room.guest_access", StateKey: ""} + visibilityTuple := gomatrixserverlib.StateKeyTuple{EventType: gomatrixserverlib.MRoomHistoryVisibility, StateKey: ""} + joinRuleTuple := gomatrixserverlib.StateKeyTuple{EventType: gomatrixserverlib.MRoomJoinRules, StateKey: ""} + + var stateRes currentstateAPI.QueryBulkStateContentResponse + err := stateAPI.QueryBulkStateContent(ctx, ¤tstateAPI.QueryBulkStateContentRequest{ + RoomIDs: roomIDs, + AllowWildcards: true, + StateTuples: []gomatrixserverlib.StateKeyTuple{ + nameTuple, canonicalTuple, topicTuple, guestTuple, visibilityTuple, joinRuleTuple, avatarTuple, + {EventType: gomatrixserverlib.MRoomMember, StateKey: "*"}, + }, + }, &stateRes) + if err != nil { + util.GetLogger(ctx).WithError(err).Error("QueryBulkStateContent failed") + return nil, err + } + chunk := make([]gomatrixserverlib.PublicRoom, len(roomIDs)) + i := 0 + for roomID, data := range stateRes.Rooms { + pub := gomatrixserverlib.PublicRoom{ + RoomID: roomID, + } + joinCount := 0 + var joinRule, guestAccess string + for tuple, contentVal := range data { + if tuple.EventType == gomatrixserverlib.MRoomMember && contentVal == "join" { + joinCount++ + continue + } + switch tuple { + case avatarTuple: + pub.AvatarURL = contentVal + case nameTuple: + pub.Name = contentVal + case topicTuple: + pub.Topic = contentVal + case canonicalTuple: + pub.CanonicalAlias = contentVal + case visibilityTuple: + pub.WorldReadable = contentVal == "world_readable" + // need both of these to determine whether guests can join + case joinRuleTuple: + joinRule = contentVal + case guestTuple: + guestAccess = contentVal + } + } + if joinRule == gomatrixserverlib.Public && guestAccess == "can_join" { + pub.GuestCanJoin = true + } + pub.JoinedMembersCount = joinCount + chunk[i] = pub + i++ + } + return chunk, nil +} + +// sliceInto returns a subslice of `slice` which honours the since/limit values given. +// +// 0 1 2 3 4 5 6 index +// [A, B, C, D, E, F, G] slice +// +// limit=3 => A,B,C (prev='', next='3') +// limit=3&since=3 => D,E,F (prev='0', next='6') +// limit=3&since=6 => G (prev='3', next='') +// +// A value of '-1' for prev/next indicates no position. +func sliceInto(slice []string, since int64, limit int16) (subset []string, prev, next int) { + prev = -1 + next = -1 + + if since > 0 { + prev = int(since) - int(limit) + } + nextIndex := int(since) + int(limit) + if len(slice) > nextIndex { // there are more rooms ahead of us + next = nextIndex + } + + // apply sanity caps + if since < 0 { + since = 0 + } + if nextIndex > len(slice) { + nextIndex = len(slice) + } + + subset = slice[since:nextIndex] + return +} diff --git a/clientapi/routing/directory_public_test.go b/clientapi/routing/directory_public_test.go new file mode 100644 index 00000000..f2a1d551 --- /dev/null +++ b/clientapi/routing/directory_public_test.go @@ -0,0 +1,48 @@ +package routing + +import ( + "reflect" + "testing" +) + +func TestSliceInto(t *testing.T) { + slice := []string{"a", "b", "c", "d", "e", "f", "g"} + limit := int16(3) + testCases := []struct { + since int64 + wantPrev int + wantNext int + wantSubset []string + }{ + { + since: 0, + wantPrev: -1, + wantNext: 3, + wantSubset: slice[0:3], + }, + { + since: 3, + wantPrev: 0, + wantNext: 6, + wantSubset: slice[3:6], + }, + { + since: 6, + wantPrev: 3, + wantNext: -1, + wantSubset: slice[6:7], + }, + } + for _, tc := range testCases { + subset, prev, next := sliceInto(slice, tc.since, limit) + if !reflect.DeepEqual(subset, tc.wantSubset) { + t.Errorf("returned subset is wrong, got %v want %v", subset, tc.wantSubset) + } + if prev != tc.wantPrev { + t.Errorf("returned prev is wrong, got %d want %d", prev, tc.wantPrev) + } + if next != tc.wantNext { + t.Errorf("returned next is wrong, got %d want %d", next, tc.wantNext) + } + } +} diff --git a/clientapi/routing/membership.go b/clientapi/routing/membership.go index 1f316384..c2145159 100644 --- a/clientapi/routing/membership.go +++ b/clientapi/routing/membership.go @@ -25,6 +25,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/clientapi/threepid" + currentstateAPI "github.com/matrix-org/dendrite/currentstateserver/api" "github.com/matrix-org/dendrite/internal/config" "github.com/matrix-org/dendrite/internal/eventutil" "github.com/matrix-org/dendrite/roomserver/api" @@ -358,3 +359,35 @@ func checkAndProcessThreepid( } return } + +func checkMemberInRoom(ctx context.Context, stateAPI currentstateAPI.CurrentStateInternalAPI, userID, roomID string) *util.JSONResponse { + tuple := gomatrixserverlib.StateKeyTuple{ + EventType: gomatrixserverlib.MRoomMember, + StateKey: userID, + } + var membershipRes currentstateAPI.QueryCurrentStateResponse + err := stateAPI.QueryCurrentState(ctx, ¤tstateAPI.QueryCurrentStateRequest{ + RoomID: roomID, + StateTuples: []gomatrixserverlib.StateKeyTuple{tuple}, + }, &membershipRes) + if err != nil { + util.GetLogger(ctx).WithError(err).Error("QueryCurrentState: could not query membership for user") + e := jsonerror.InternalServerError() + return &e + } + ev, ok := membershipRes.StateEvents[tuple] + if !ok { + return &util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("user does not belong to room"), + } + } + membership, err := ev.Membership() + if err != nil || membership != "join" { + return &util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("user does not belong to room"), + } + } + return nil +} diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index deaa7b32..57bb921d 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -1,4 +1,4 @@ -// Copyright 2017 Vector Creations Ltd +// Copyright 2020 The Matrix.org Foundation C.I.C. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -31,6 +31,7 @@ import ( "github.com/matrix-org/dendrite/internal/transactions" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/userapi/api" + userapi "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/storage/accounts" "github.com/matrix-org/dendrite/userapi/storage/devices" "github.com/matrix-org/gomatrixserverlib" @@ -290,6 +291,34 @@ func Setup( return RemoveLocalAlias(req, device, vars["roomAlias"], rsAPI) }), ).Methods(http.MethodDelete, http.MethodOptions) + r0mux.Handle("/directory/list/room/{roomID}", + httputil.MakeExternalAPI("directory_list", func(req *http.Request) util.JSONResponse { + vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) + if err != nil { + return util.ErrorResponse(err) + } + return GetVisibility(req, rsAPI, vars["roomID"]) + }), + ).Methods(http.MethodGet, http.MethodOptions) + // TODO: Add AS support + r0mux.Handle("/directory/list/room/{roomID}", + httputil.MakeAuthAPI("directory_list", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { + vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) + if err != nil { + return util.ErrorResponse(err) + } + return SetVisibility(req, stateAPI, rsAPI, device, vars["roomID"]) + }), + ).Methods(http.MethodPut, http.MethodOptions) + r0mux.Handle("/publicRooms", + httputil.MakeExternalAPI("public_rooms", func(req *http.Request) util.JSONResponse { + /* TODO: + if extRoomsProvider != nil { + return GetPostPublicRoomsWithExternal(req, stateAPI, fedClient, extRoomsProvider) + } */ + return GetPostPublicRooms(req, rsAPI, stateAPI) + }), + ).Methods(http.MethodGet, http.MethodPost, http.MethodOptions) r0mux.Handle("/logout", httputil.MakeAuthAPI("logout", userAPI, func(req *http.Request, device *api.Device) util.JSONResponse { |