diff --git a/src/github.com/matrix-org/dendrite/clientapi/routing/routing.go b/src/github.com/matrix-org/dendrite/clientapi/routing/routing.go index 68a9de075..0ce65e544 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/routing/routing.go +++ b/src/github.com/matrix-org/dendrite/clientapi/routing/routing.go @@ -41,7 +41,7 @@ const pathPrefixUnstable = "/_matrix/client/unstable" func Setup( apiMux *mux.Router, httpClient *http.Client, cfg config.Dendrite, producer *producers.RoomserverProducer, queryAPI api.RoomserverQueryAPI, - aliasAPI api.RoomserverAliasAPI, + aliasAPI api.RoomserverAliasAPI, publicRoomAPI api.RoomserverPublicRoomAPI, accountDB *accounts.Database, deviceDB *devices.Database, federation *gomatrixserverlib.FederationClient, @@ -133,6 +133,20 @@ func Setup( }), ).Methods("DELETE") + r0mux.Handle("/directory/list/room/{roomID}", + common.MakeAPI("directory_list", func(req *http.Request) util.JSONResponse { + vars := mux.Vars(req) + return writers.GetVisibility(req, vars["roomID"], publicRoomAPI) + }), + ).Methods("GET") + + r0mux.Handle("/directory/list/room/{roomID}", + common.MakeAuthAPI("directory_list", deviceDB, func(req *http.Request, device *authtypes.Device) util.JSONResponse { + vars := mux.Vars(req) + return writers.SetVisibility(req, *device, vars["roomID"], publicRoomAPI) + }), + ).Methods("PUT", "OPTIONS") + r0mux.Handle("/logout", common.MakeAuthAPI("logout", deviceDB, func(req *http.Request, device *authtypes.Device) util.JSONResponse { return readers.Logout(req, deviceDB, device) @@ -262,16 +276,9 @@ func Setup( r0mux.Handle("/publicRooms", common.MakeAPI("public_rooms", func(req *http.Request) util.JSONResponse { // TODO: Return a list of public rooms - return util.JSONResponse{ - Code: 200, - JSON: struct { - Chunk []struct{} `json:"chunk"` - Start string `json:"start"` - End string `json:"end"` - }{[]struct{}{}, "", ""}, - } + return writers.GetPublicRooms(req, publicRoomAPI) }), - ) + ).Methods("GET", "POST", "OPTIONS") unstableMux.Handle("/thirdparty/protocols", common.MakeAPI("thirdparty_protocols", func(req *http.Request) util.JSONResponse { diff --git a/src/github.com/matrix-org/dendrite/clientapi/writers/room_directory.go b/src/github.com/matrix-org/dendrite/clientapi/writers/room_directory.go new file mode 100644 index 000000000..8298fec9c --- /dev/null +++ b/src/github.com/matrix-org/dendrite/clientapi/writers/room_directory.go @@ -0,0 +1,112 @@ +// Copyright 2017 Vector Creations Ltd +// +// 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 writers + +import ( + "net/http" + "strconv" + + "github.com/matrix-org/dendrite/clientapi/auth/authtypes" + "github.com/matrix-org/dendrite/clientapi/httputil" + "github.com/matrix-org/dendrite/roomserver/api" + "github.com/matrix-org/util" +) + +type roomDirectoryVisibility struct { + Visibility string `json:"visibility"` +} + +// GetVisibility implements GET /directory/list/room/{roomID} +func GetVisibility( + req *http.Request, roomID string, publicRoomAPI api.RoomserverPublicRoomAPI, +) util.JSONResponse { + queryReq := api.GetRoomVisibilityRequest{roomID} + var queryRes api.GetRoomVisibilityResponse + if err := publicRoomAPI.GetRoomVisibility(&queryReq, &queryRes); err != nil { + return httputil.LogThenError(req, err) + } + + return util.JSONResponse{ + Code: 200, + JSON: roomDirectoryVisibility{queryRes.Visibility}, + } +} + +// SetVisibility implements PUT /directory/list/room/{roomID} +// TODO: Check if user has the power leven to edit the room visibility +func SetVisibility( + req *http.Request, device authtypes.Device, roomID string, + publicRoomAPI api.RoomserverPublicRoomAPI, +) util.JSONResponse { + var r roomDirectoryVisibility + if resErr := httputil.UnmarshalJSONRequest(req, &r); resErr != nil { + return *resErr + } + + queryReq := api.SetRoomVisibilityRequest{ + RoomID: roomID, + Visibility: r.Visibility, + } + var queryRes api.SetRoomVisibilityResponse + if err := publicRoomAPI.SetRoomVisibility(&queryReq, &queryRes); err != nil { + return httputil.LogThenError(req, err) + } + + return util.JSONResponse{ + Code: 200, + JSON: struct{}{}, + } +} + +// GetPublicRooms implements GET and POST /publicRooms +func GetPublicRooms( + req *http.Request, publicRoomAPI api.RoomserverPublicRoomAPI, +) util.JSONResponse { + queryReq := api.GetPublicRoomsRequest{} + if fillErr := fillPublicRoomsReq(req, &queryReq); fillErr != nil { + return *fillErr + } + var queryRes api.GetPublicRoomsResponse + if err := publicRoomAPI.GetPublicRooms(&queryReq, &queryRes); err != nil { + return httputil.LogThenError(req, err) + } + + return util.JSONResponse{ + Code: 200, + JSON: queryRes, + } +} + +// fillPublicRoomsReq fills the Limit, Since and Filter attributes of a request to the roomserver's +// GetPublicRooms API by parsing the incoming HTTP request +func fillPublicRoomsReq(httpReq *http.Request, queryReq *api.GetPublicRoomsRequest) *util.JSONResponse { + 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 { + reqErr := httputil.LogThenError(httpReq, err) + return &reqErr + } + queryReq.Limit = int16(limit) + queryReq.Since = httpReq.FormValue("since") + } else if httpReq.Method == "POST" { + if reqErr := httputil.UnmarshalJSONRequest(httpReq, queryReq); reqErr != nil { + return reqErr + } + } + + return nil +} diff --git a/src/github.com/matrix-org/dendrite/cmd/dendrite-client-api-server/main.go b/src/github.com/matrix-org/dendrite/cmd/dendrite-client-api-server/main.go index 5d195bee8..beddb2978 100644 --- a/src/github.com/matrix-org/dendrite/cmd/dendrite-client-api-server/main.go +++ b/src/github.com/matrix-org/dendrite/cmd/dendrite-client-api-server/main.go @@ -54,6 +54,7 @@ func main() { queryAPI := api.NewRoomserverQueryAPIHTTP(cfg.RoomServerURL(), nil) aliasAPI := api.NewRoomserverAliasAPIHTTP(cfg.RoomServerURL(), nil) + publicRoomAPI := api.NewRoomserverPublicRoomAPIHTTP(cfg.RoomServerURL(), nil) inputAPI := api.NewRoomserverInputAPIHTTP(cfg.RoomServerURL(), nil) roomserverProducer := producers.NewRoomserverProducer(inputAPI) @@ -108,8 +109,8 @@ func main() { api := mux.NewRouter() routing.Setup( api, http.DefaultClient, *cfg, roomserverProducer, - queryAPI, aliasAPI, accountDB, deviceDB, federation, keyRing, - userUpdateProducer, syncProducer, + queryAPI, aliasAPI, publicRoomAPI, accountDB, deviceDB, federation, + keyRing, userUpdateProducer, syncProducer, ) common.SetupHTTPAPI(http.DefaultServeMux, api) diff --git a/src/github.com/matrix-org/dendrite/cmd/dendrite-monolith-server/main.go b/src/github.com/matrix-org/dendrite/cmd/dendrite-monolith-server/main.go index f0046d7b1..66c77933c 100644 --- a/src/github.com/matrix-org/dendrite/cmd/dendrite-monolith-server/main.go +++ b/src/github.com/matrix-org/dendrite/cmd/dendrite-monolith-server/main.go @@ -33,6 +33,7 @@ import ( roomserver_alias "github.com/matrix-org/dendrite/roomserver/alias" roomserver_input "github.com/matrix-org/dendrite/roomserver/input" + roomserver_publicroom "github.com/matrix-org/dendrite/roomserver/publicroom" roomserver_query "github.com/matrix-org/dendrite/roomserver/query" roomserver_storage "github.com/matrix-org/dendrite/roomserver/storage" @@ -121,9 +122,10 @@ type monolith struct { federation *gomatrixserverlib.FederationClient keyRing gomatrixserverlib.KeyRing - inputAPI *roomserver_input.RoomserverInputAPI - queryAPI *roomserver_query.RoomserverQueryAPI - aliasAPI *roomserver_alias.RoomserverAliasAPI + inputAPI *roomserver_input.RoomserverInputAPI + queryAPI *roomserver_query.RoomserverQueryAPI + aliasAPI *roomserver_alias.RoomserverAliasAPI + publicRoomAPI *roomserver_publicroom.RoomserverPublicRoomAPI roomServerProducer *producers.RoomserverProducer userUpdateProducer *producers.UserUpdateProducer @@ -204,6 +206,10 @@ func (m *monolith) setupRoomServer() { InputAPI: m.inputAPI, QueryAPI: m.queryAPI, } + + m.publicRoomAPI = &roomserver_publicroom.RoomserverPublicRoomAPI{ + DB: m.roomServerDB, + } } func (m *monolith) setupProducers() { @@ -280,8 +286,8 @@ func (m *monolith) setupConsumers() { func (m *monolith) setupAPIs() { clientapi_routing.Setup( m.api, http.DefaultClient, *m.cfg, m.roomServerProducer, - m.queryAPI, m.aliasAPI, m.accountDB, m.deviceDB, m.federation, m.keyRing, - m.userUpdateProducer, m.syncProducer, + m.queryAPI, m.aliasAPI, m.publicRoomAPI, m.accountDB, m.deviceDB, + m.federation, m.keyRing, m.userUpdateProducer, m.syncProducer, ) mediaapi_routing.Setup( diff --git a/src/github.com/matrix-org/dendrite/roomserver/alias/alias.go b/src/github.com/matrix-org/dendrite/roomserver/alias/alias.go index faf91bc47..1a35d6770 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/alias/alias.go +++ b/src/github.com/matrix-org/dendrite/roomserver/alias/alias.go @@ -23,21 +23,30 @@ import ( "github.com/matrix-org/dendrite/common" "github.com/matrix-org/dendrite/common/config" "github.com/matrix-org/dendrite/roomserver/api" + "github.com/matrix-org/dendrite/roomserver/types" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" ) // RoomserverAliasAPIDatabase has the storage APIs needed to implement the alias API. type RoomserverAliasAPIDatabase interface { - // Save a given room alias with the room ID it refers to. + // Lookup the numeric ID for the room. + // Returns 0 if the room doesn't exists. // Returns an error if there was a problem talking to the database. - SetRoomAlias(alias string, roomID string) error - // Lookup the room ID a given alias refers to. + RoomNID(roomID string) (types.RoomNID, error) + // Lookup the ID of a room identified by a given numeric ID. + // Returns an error if there was a problem talking to the database or if no + // room matches the given numeric ID. + RoomID(roomNID types.RoomNID) (string, error) + // Save a given room alias with the room numeric ID it refers to. // Returns an error if there was a problem talking to the database. - GetRoomIDFromAlias(alias string) (string, error) - // Lookup all aliases referring to a given room ID. + SetRoomAlias(alias string, roomNID types.RoomNID) error + // Lookup the room numeric ID a given alias refers to. // Returns an error if there was a problem talking to the database. - GetAliasesFromRoomID(roomID string) ([]string, error) + GetRoomNIDFromAlias(alias string) (types.RoomNID, error) + // Lookup all aliases referring to a given room numeric ID. + // Returns an error if there was a problem talking to the database. + GetAliasesFromRoomNID(roomNID types.RoomNID) ([]string, error) // Remove a given room alias. // Returns an error if there was a problem talking to the database. RemoveRoomAlias(alias string) error @@ -57,24 +66,28 @@ func (r *RoomserverAliasAPI) SetRoomAlias( response *api.SetRoomAliasResponse, ) error { // Check if the alias isn't already referring to a room - roomID, err := r.DB.GetRoomIDFromAlias(request.Alias) + roomNID, err := r.DB.GetRoomNIDFromAlias(request.Alias) if err != nil { return err } - if len(roomID) > 0 { + if roomNID != 0 { // If the alias already exists, stop the process response.AliasExists = true return nil } response.AliasExists = false + roomNID, err = r.DB.RoomNID(request.RoomID) + if err != nil { + return err + } // Save the new alias - if err := r.DB.SetRoomAlias(request.Alias, request.RoomID); err != nil { + if err := r.DB.SetRoomAlias(request.Alias, roomNID); err != nil { return err } // Send a m.room.aliases event with the updated list of aliases for this room - if err := r.sendUpdatedAliasesEvent(request.UserID, request.RoomID); err != nil { + if err := r.sendUpdatedAliasesEvent(request.UserID, request.RoomID, roomNID); err != nil { return err } @@ -87,7 +100,12 @@ func (r *RoomserverAliasAPI) GetAliasRoomID( response *api.GetAliasRoomIDResponse, ) error { // Lookup the room ID in the database - roomID, err := r.DB.GetRoomIDFromAlias(request.Alias) + roomNID, err := r.DB.GetRoomNIDFromAlias(request.Alias) + if err != nil { + return err + } + + roomID, err := r.DB.RoomID(roomNID) if err != nil { return err } @@ -102,7 +120,12 @@ func (r *RoomserverAliasAPI) RemoveRoomAlias( response *api.RemoveRoomAliasResponse, ) error { // Lookup the room ID in the database - roomID, err := r.DB.GetRoomIDFromAlias(request.Alias) + roomNID, err := r.DB.GetRoomNIDFromAlias(request.Alias) + if err != nil { + return err + } + + roomID, err := r.DB.RoomID(roomNID) if err != nil { return err } @@ -113,7 +136,7 @@ func (r *RoomserverAliasAPI) RemoveRoomAlias( } // Send an updated m.room.aliases event - if err := r.sendUpdatedAliasesEvent(request.UserID, roomID); err != nil { + if err := r.sendUpdatedAliasesEvent(request.UserID, roomID, roomNID); err != nil { return err } @@ -126,7 +149,7 @@ type roomAliasesContent struct { // Build the updated m.room.aliases event to send to the room after addition or // removal of an alias -func (r *RoomserverAliasAPI) sendUpdatedAliasesEvent(userID string, roomID string) error { +func (r *RoomserverAliasAPI) sendUpdatedAliasesEvent(userID string, roomID string, roomNID types.RoomNID) error { serverName := string(r.Cfg.Matrix.ServerName) builder := gomatrixserverlib.EventBuilder{ @@ -138,7 +161,7 @@ func (r *RoomserverAliasAPI) sendUpdatedAliasesEvent(userID string, roomID strin // Retrieve the updated list of aliases, marhal it and set it as the // event's content - aliases, err := r.DB.GetAliasesFromRoomID(roomID) + aliases, err := r.DB.GetAliasesFromRoomNID(roomNID) if err != nil { return err } diff --git a/src/github.com/matrix-org/dendrite/roomserver/api/alias.go b/src/github.com/matrix-org/dendrite/roomserver/api/alias.go index bb65c3ae3..999b818cf 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/api/alias.go +++ b/src/github.com/matrix-org/dendrite/roomserver/api/alias.go @@ -115,11 +115,11 @@ func (h *httpRoomserverAliasAPI) GetAliasRoomID( request *GetAliasRoomIDRequest, response *GetAliasRoomIDResponse, ) error { - // RemoveRoomAlias implements RoomserverAliasAPI apiURL := h.roomserverURL + RoomserverGetAliasRoomIDPath return postJSON(h.httpClient, apiURL, request, response) } +// RemoveRoomAlias implements RoomserverAliasAPI func (h *httpRoomserverAliasAPI) RemoveRoomAlias( request *RemoveRoomAliasRequest, response *RemoveRoomAliasResponse, diff --git a/src/github.com/matrix-org/dendrite/roomserver/api/public_room.go b/src/github.com/matrix-org/dendrite/roomserver/api/public_room.go new file mode 100644 index 000000000..63f7afc7b --- /dev/null +++ b/src/github.com/matrix-org/dendrite/roomserver/api/public_room.go @@ -0,0 +1,148 @@ +// Copyright 2017 Vector Creations Ltd +// +// 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 api + +import ( + "net/http" +) + +// SetRoomVisibilityRequest is a request to SetRoomVisibility +type SetRoomVisibilityRequest struct { + // ID of the room of which the visibility will be updated + RoomID string `json:"room_id"` + // The new visibility of the room. Either "public" or "private" + Visibility string `json:"visibility"` +} + +// SetRoomVisibilityResponse is a response to SetRoomVisibility +type SetRoomVisibilityResponse struct{} + +// GetRoomVisibilityRequest is a request to GetRoomVisibility +type GetRoomVisibilityRequest struct { + // ID of the room to get the visibility of + RoomID string `json:"room_id"` +} + +// GetRoomVisibilityResponse is a response to GetRoomVisibility +type GetRoomVisibilityResponse struct { + // Visibility of the room. Either "public" or "private" + Visibility string `json:"visibility"` +} + +// GetPublicRoomsRequest is a request to GetPublicRooms +// TODO: Support the "server" request parameter +type GetPublicRoomsRequest struct { + Limit int16 `json:"limit"` + Since string `json:"since"` + Filter Filter `json:"filter"` +} + +// Filter implements the Filter structure from the Matrix spec +type Filter struct { + SearchTerm string `json:"generic_search_term"` +} + +// GetPublicRoomsResponse is a response to GetPublicRooms +type GetPublicRoomsResponse struct { + Chunks []PublicRoomsChunk `json:"chunk"` + NextBatch string `json:"next_batch,omitempty"` + PrevBatch string `json:"prev_batch,omitempty"` + TotalRoomCountEstimate int64 `json:"total_room_count_estimate,omitempty"` +} + +// PublicRoomsChunk implements the PublicRoomsChunk structure from the Matrix spec +type PublicRoomsChunk struct { + RoomID string `json:"room_id"` + Aliases []string `json:"aliases,omitempty"` + CanonicalAlias string `json:"canonical_alias,omitempty"` + Name string `json:"name,omitempty"` + Topic string `json:"topic,omitempty"` + AvatarURL string `json:"avatar_url,omitempty"` + NumJoinedMembers int64 `json:"num_joined_members"` + WorldReadable bool `json:"world_readable"` + GuestCanJoin bool `json:"guest_can_join"` +} + +// RoomserverPublicRoomAPI is used to update or retrieve the visibility setting +// of a room, or to retrieve all of the public rooms +type RoomserverPublicRoomAPI interface { + // Set the visibility for a room + SetRoomVisibility( + req *SetRoomVisibilityRequest, + response *SetRoomVisibilityResponse, + ) error + + // Get the visibility for a room + GetRoomVisibility( + req *GetRoomVisibilityRequest, + response *GetRoomVisibilityResponse, + ) error + + // Get all rooms publicly visible rooms on the server + GetPublicRooms( + req *GetPublicRoomsRequest, + response *GetPublicRoomsResponse, + ) error +} + +// RoomserverSetRoomVisibilityPath is the HTTP path for the SetRoomVisibility API +const RoomserverSetRoomVisibilityPath = "/api/roomserver/setRoomVisibility" + +// RoomserverGetRoomVisibilityPath is the HTTP path for the GetRoomVisibility API +const RoomserverGetRoomVisibilityPath = "/api/roomserver/getRoomVisibility" + +// RoomserverGetPublicRoomsPath is the HTTP path for the GetPublicRooms API +const RoomserverGetPublicRoomsPath = "/api/roomserver/getPublicRooms" + +// NewRoomserverPublicRoomAPIHTTP creates a RoomserverPublicRoomAPI implemented by talking to a HTTP POST API. +// If httpClient is nil then it uses the http.DefaultClient +func NewRoomserverPublicRoomAPIHTTP(roomserverURL string, httpClient *http.Client) RoomserverPublicRoomAPI { + if httpClient == nil { + httpClient = http.DefaultClient + } + return &httpRoomserverPublicRoomAPI{roomserverURL, httpClient} +} + +type httpRoomserverPublicRoomAPI struct { + roomserverURL string + httpClient *http.Client +} + +// SetRoomVisibility implements RoomserverPublicRoomAPI +func (h *httpRoomserverPublicRoomAPI) SetRoomVisibility( + request *SetRoomVisibilityRequest, + response *SetRoomVisibilityResponse, +) error { + apiURL := h.roomserverURL + RoomserverSetRoomVisibilityPath + return postJSON(h.httpClient, apiURL, request, response) +} + +// GetRoomVisibility implements RoomserverPublicRoomAPI +func (h *httpRoomserverPublicRoomAPI) GetRoomVisibility( + request *GetRoomVisibilityRequest, + response *GetRoomVisibilityResponse, +) error { + apiURL := h.roomserverURL + RoomserverGetRoomVisibilityPath + return postJSON(h.httpClient, apiURL, request, response) +} + +// GetPublicRooms implements RoomserverPublicRoomAPI +func (h *httpRoomserverPublicRoomAPI) GetPublicRooms( + request *GetPublicRoomsRequest, + response *GetPublicRoomsResponse, +) error { + apiURL := h.roomserverURL + RoomserverGetPublicRoomsPath + return postJSON(h.httpClient, apiURL, request, response) +} diff --git a/src/github.com/matrix-org/dendrite/roomserver/publicroom/public_room.go b/src/github.com/matrix-org/dendrite/roomserver/publicroom/public_room.go new file mode 100644 index 000000000..d4943b18e --- /dev/null +++ b/src/github.com/matrix-org/dendrite/roomserver/publicroom/public_room.go @@ -0,0 +1,224 @@ +// Copyright 2017 Vector Creations Ltd +// +// 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 publicroom + +import ( + "encoding/json" + "errors" + "net/http" + "strconv" + + "github.com/matrix-org/dendrite/common" + "github.com/matrix-org/dendrite/roomserver/api" + "github.com/matrix-org/dendrite/roomserver/types" + "github.com/matrix-org/util" +) + +// RoomserverPublicRoomAPIDatabase has the storage APIs needed to implement the +// public room API. +type RoomserverPublicRoomAPIDatabase interface { + // Lookup the numeric ID for the room. + // Returns 0 if the room doesn't exists. + // Returns an error if there was a problem talking to the database. + RoomNID(roomID string) (types.RoomNID, error) + // Lookup the room IDs matching a batch of room numeric IDs, ordered by + // numeric ID (ie map[roomNID] = roomID). + // Returns an error if the retrieval failed. + RoomIDs([]types.RoomNID) (map[types.RoomNID]string, error) + // Checks the visibility of the room identified by the given numeric ID. + // Returns true if the room is publicly visible, returns false if not. + // If there's no room matching this numeric ID, or if the retrieval failed, + // returns an error. + IsRoomPublic(roomNID types.RoomNID) (bool, error) + // Returns an array of string containing the room numeric IDs of the rooms + // that are publicly visible. + // Returns an error if the retrieval failed. + GetPublicRoomNIDs() ([]types.RoomNID, error) + // Updates the visibility for a room to the given value: true means that the + // room is publicly visible, false means that the room isn't publicly visible. + // Returns an error if the update failed. + UpdateRoomVisibility(roomNID types.RoomNID, visibility bool) error + // Returns a map of the aliases bound to a given set of room numeric IDs, + // ordered by room NID (ie map[roomNID] = []alias) + // Returns an error if the retrieval failed + GetAliasesFromRoomNIDs(roomNIDs []types.RoomNID) (map[types.RoomNID][]string, error) + // Returns the number of joined user in roms identified by given room numeric IDs. + // Returns an error if the retrieval failed + CountJoinedMembersInRooms(roomNIDs []types.RoomNID) (map[types.RoomNID]int64, error) +} + +// RoomserverPublicRoomAPI is an implementation of api.RoomserverPublicRoomAPI +type RoomserverPublicRoomAPI struct { + DB RoomserverPublicRoomAPIDatabase +} + +// SetRoomVisibility implements api.RoomserverPublicRoomAPI +func (r *RoomserverPublicRoomAPI) SetRoomVisibility( + req *api.SetRoomVisibilityRequest, + response *api.SetRoomVisibilityResponse, +) error { + roomNID, err := r.DB.RoomNID(req.RoomID) + if err != nil || roomNID == 0 { + return err + } + + var visibility bool + if req.Visibility == "public" { + visibility = true + } else if req.Visibility == "private" { + visibility = false + } else { + return errors.New("Invalid visibility setting") + } + + if err = r.DB.UpdateRoomVisibility(roomNID, visibility); err != nil { + return err + } + + return nil +} + +// GetRoomVisibility implements api.RoomserverPublicRoomAPI +func (r *RoomserverPublicRoomAPI) GetRoomVisibility( + req *api.GetRoomVisibilityRequest, + response *api.GetRoomVisibilityResponse, +) error { + roomNID, err := r.DB.RoomNID(req.RoomID) + if err != nil || roomNID == 0 { + return err + } + + if isPublic, err := r.DB.IsRoomPublic(roomNID); err != nil { + return err + } else if isPublic { + response.Visibility = "public" + } else { + response.Visibility = "private" + } + + return nil +} + +// GetPublicRooms implements api.RoomserverPublicRoomAPI +func (r *RoomserverPublicRoomAPI) GetPublicRooms( + req *api.GetPublicRoomsRequest, + response *api.GetPublicRoomsResponse, +) error { + var limit int16 + var offset int64 + + limit = req.Limit + offset, err := strconv.ParseInt(req.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(req.Since) > 0 { + return err + } + + roomNIDs, err := r.DB.GetPublicRoomNIDs() + if err != nil { + return err + } + + aliases, err := r.DB.GetAliasesFromRoomNIDs(roomNIDs) + if err != nil { + return err + } + roomIDs, err := r.DB.RoomIDs(roomNIDs) + if err != nil { + return err + } + nbMembers, err := r.DB.CountJoinedMembersInRooms(roomNIDs) + if err != nil { + return err + } + + totalChunks := []api.PublicRoomsChunk{} + // Iterate over the array of aliases instead of the array of rooms, because + // a room must have at least one alias to be listed + for roomNID, as := range aliases { + chunk := api.PublicRoomsChunk{ + RoomID: roomIDs[roomNID], + Aliases: as, + NumJoinedMembers: nbMembers[roomNID], + WorldReadable: true, + GuestCanJoin: true, + } + totalChunks = append(totalChunks, chunk) + } + + chunks := totalChunks[offset:] + + if len(chunks) >= int(limit) { + chunks = chunks[offset:limit] + } + + response.Chunks = chunks + response.TotalRoomCountEstimate = int64(len(totalChunks)) + if offset > 0 { + response.PrevBatch = strconv.Itoa(int(offset) - 1) + } + if len(totalChunks) > int(limit) { + response.NextBatch = strconv.Itoa(int(offset) + int(limit)) + } + + return nil +} + +// SetupHTTP adds the RoomserverPublicRoomAPI handlers to the http.ServeMux. +func (r *RoomserverPublicRoomAPI) SetupHTTP(servMux *http.ServeMux) { + servMux.Handle( + api.RoomserverSetRoomVisibilityPath, + common.MakeAPI("setRoomVisibility", func(req *http.Request) util.JSONResponse { + var request api.SetRoomVisibilityRequest + var response api.SetRoomVisibilityResponse + if err := json.NewDecoder(req.Body).Decode(&request); err != nil { + return util.ErrorResponse(err) + } + if err := r.SetRoomVisibility(&request, &response); err != nil { + return util.ErrorResponse(err) + } + return util.JSONResponse{Code: 200, JSON: &response} + }), + ) + servMux.Handle( + api.RoomserverGetRoomVisibilityPath, + common.MakeAPI("getRoomVisibility", func(req *http.Request) util.JSONResponse { + var request api.GetRoomVisibilityRequest + var response api.GetRoomVisibilityResponse + if err := json.NewDecoder(req.Body).Decode(&request); err != nil { + return util.ErrorResponse(err) + } + if err := r.GetRoomVisibility(&request, &response); err != nil { + return util.ErrorResponse(err) + } + return util.JSONResponse{Code: 200, JSON: &response} + }), + ) + servMux.Handle( + api.RoomserverGetPublicRoomsPath, + common.MakeAPI("getPublicRooms", func(req *http.Request) util.JSONResponse { + var request api.GetPublicRoomsRequest + var response api.GetPublicRoomsResponse + if err := json.NewDecoder(req.Body).Decode(&request); err != nil { + return util.ErrorResponse(err) + } + if err := r.GetPublicRooms(&request, &response); err != nil { + return util.ErrorResponse(err) + } + return util.JSONResponse{Code: 200, JSON: &response} + }), + ) +} diff --git a/src/github.com/matrix-org/dendrite/roomserver/query/query.go b/src/github.com/matrix-org/dendrite/roomserver/query/query.go index 30b695fb4..7ec8dcb5b 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/query/query.go +++ b/src/github.com/matrix-org/dendrite/roomserver/query/query.go @@ -40,18 +40,6 @@ type RoomserverQueryAPIDatabase interface { // Lookup the numeric IDs for a list of events. // Returns an error if there was a problem talking to the database. EventNIDs(eventIDs []string) (map[string]types.EventNID, error) - // Save a given room alias with the room ID it refers to. - // Returns an error if there was a problem talking to the database. - SetRoomAlias(alias string, roomID string) error - // Lookup the room ID a given alias refers to. - // Returns an error if there was a problem talking to the database. - GetRoomIDFromAlias(alias string) (string, error) - // Lookup all aliases referring to a given room ID. - // Returns an error if there was a problem talking to the database. - GetAliasesFromRoomID(roomID string) ([]string, error) - // Remove a given room alias. - // Returns an error if there was a problem talking to the database. - RemoveRoomAlias(alias string) error } // RoomserverQueryAPI is an implementation of api.RoomserverQueryAPI diff --git a/src/github.com/matrix-org/dendrite/roomserver/storage/membership_table.go b/src/github.com/matrix-org/dendrite/roomserver/storage/membership_table.go index 725e5b8d9..f209c0d2c 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/storage/membership_table.go +++ b/src/github.com/matrix-org/dendrite/roomserver/storage/membership_table.go @@ -17,6 +17,7 @@ package storage import ( "database/sql" + "github.com/lib/pq" "github.com/matrix-org/dendrite/roomserver/types" ) @@ -33,7 +34,7 @@ const membershipSchema = ` -- and the room state tables. -- This table is updated in one of 3 ways: -- 1) The membership of a user changes within the current state of the room. --- 2) An invite is received outside of a room over federation. +-- 2) An invite is received outside of a room over federation. -- 3) An invite is rejected outside of a room over federation. CREATE TABLE IF NOT EXISTS roomserver_membership ( room_nid BIGINT NOT NULL, @@ -61,6 +62,11 @@ const selectMembershipForUpdateSQL = "" + "SELECT membership_nid FROM roomserver_membership" + " WHERE room_nid = $1 AND target_nid = $2 FOR UPDATE" +const countJoinedMembersInRoomsSQL = "" + + "SELECT room_nid, COUNT(*) FROM roomserver_membership" + + " WHERE room_nid = ANY($1) AND membership_nid = $2" + + " GROUP BY room_nid" + const updateMembershipSQL = "" + "UPDATE roomserver_membership SET sender_nid = $3, membership_nid = $4" + " WHERE room_nid = $1 AND target_nid = $2" @@ -68,6 +74,7 @@ const updateMembershipSQL = "" + type membershipStatements struct { insertMembershipStmt *sql.Stmt selectMembershipForUpdateStmt *sql.Stmt + countJoinedMembersInRoomsStmt *sql.Stmt updateMembershipStmt *sql.Stmt } @@ -80,6 +87,7 @@ func (s *membershipStatements) prepare(db *sql.DB) (err error) { return statementList{ {&s.insertMembershipStmt, insertMembershipSQL}, {&s.selectMembershipForUpdateStmt, selectMembershipForUpdateSQL}, + {&s.countJoinedMembersInRoomsStmt, countJoinedMembersInRoomsSQL}, {&s.updateMembershipStmt, updateMembershipSQL}, }.prepare(db) } @@ -100,6 +108,32 @@ func (s *membershipStatements) selectMembershipForUpdate( return } +func (s *membershipStatements) countJoinedMembersInRooms(roomNIDs []types.RoomNID) (map[types.RoomNID]int64, error) { + nIDs := []int64{} + for _, roomNID := range roomNIDs { + nIDs = append(nIDs, int64(roomNID)) + } + + rows, err := s.countJoinedMembersInRoomsStmt.Query(pq.Int64Array(nIDs), membershipStateJoin) + if err != nil { + return nil, err + } + + members := make(map[types.RoomNID]int64) + for rows.Next() { + var roomNID types.RoomNID + var count int64 + + if err = rows.Scan(&roomNID, &count); err != nil { + return members, err + } + + members[roomNID] = count + } + + return members, err +} + func (s *membershipStatements) updateMembership( txn *sql.Tx, roomNID types.RoomNID, targetUserNID types.EventStateKeyNID, senderUserNID types.EventStateKeyNID, membership membershipState, diff --git a/src/github.com/matrix-org/dendrite/roomserver/storage/room_aliases_table.go b/src/github.com/matrix-org/dendrite/roomserver/storage/room_aliases_table.go index 433835d7a..00f35a0f4 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/storage/room_aliases_table.go +++ b/src/github.com/matrix-org/dendrite/roomserver/storage/room_aliases_table.go @@ -16,6 +16,9 @@ package storage import ( "database/sql" + + "github.com/lib/pq" + "github.com/matrix-org/dendrite/roomserver/types" ) const roomAliasesSchema = ` @@ -23,30 +26,34 @@ const roomAliasesSchema = ` CREATE TABLE IF NOT EXISTS roomserver_room_aliases ( -- Alias of the room alias TEXT NOT NULL PRIMARY KEY, - -- Room ID the alias refers to - room_id TEXT NOT NULL + -- Room numeric ID the alias refers to + room_nid TEXT NOT NULL ); -CREATE UNIQUE INDEX IF NOT EXISTS roomserver_room_id_idx ON roomserver_room_aliases(room_id); +CREATE UNIQUE INDEX IF NOT EXISTS roomserver_room_nid_idx ON roomserver_room_aliases(room_nid); ` const insertRoomAliasSQL = "" + - "INSERT INTO roomserver_room_aliases (alias, room_id) VALUES ($1, $2)" + "INSERT INTO roomserver_room_aliases (alias, room_nid) VALUES ($1, $2)" -const selectRoomIDFromAliasSQL = "" + - "SELECT room_id FROM roomserver_room_aliases WHERE alias = $1" +const selectRoomNIDFromAliasSQL = "" + + "SELECT room_nid FROM roomserver_room_aliases WHERE alias = $1" -const selectAliasesFromRoomIDSQL = "" + - "SELECT alias FROM roomserver_room_aliases WHERE room_id = $1" +const selectAliasesFromRoomNIDSQL = "" + + "SELECT alias FROM roomserver_room_aliases WHERE room_nid = $1" + +const selectAliasesFromRoomNIDsSQL = "" + + "SELECT alias, room_nid FROM roomserver_room_aliases WHERE room_nid = ANY($1)" const deleteRoomAliasSQL = "" + "DELETE FROM roomserver_room_aliases WHERE alias = $1" type roomAliasesStatements struct { - insertRoomAliasStmt *sql.Stmt - selectRoomIDFromAliasStmt *sql.Stmt - selectAliasesFromRoomIDStmt *sql.Stmt - deleteRoomAliasStmt *sql.Stmt + insertRoomAliasStmt *sql.Stmt + selectRoomNIDFromAliasStmt *sql.Stmt + selectAliasesFromRoomNIDStmt *sql.Stmt + selectAliasesFromRoomNIDsStmt *sql.Stmt + deleteRoomAliasStmt *sql.Stmt } func (s *roomAliasesStatements) prepare(db *sql.DB) (err error) { @@ -56,28 +63,29 @@ func (s *roomAliasesStatements) prepare(db *sql.DB) (err error) { } return statementList{ {&s.insertRoomAliasStmt, insertRoomAliasSQL}, - {&s.selectRoomIDFromAliasStmt, selectRoomIDFromAliasSQL}, - {&s.selectAliasesFromRoomIDStmt, selectAliasesFromRoomIDSQL}, + {&s.selectRoomNIDFromAliasStmt, selectRoomNIDFromAliasSQL}, + {&s.selectAliasesFromRoomNIDStmt, selectAliasesFromRoomNIDSQL}, + {&s.selectAliasesFromRoomNIDsStmt, selectAliasesFromRoomNIDsSQL}, {&s.deleteRoomAliasStmt, deleteRoomAliasSQL}, }.prepare(db) } -func (s *roomAliasesStatements) insertRoomAlias(alias string, roomID string) (err error) { - _, err = s.insertRoomAliasStmt.Exec(alias, roomID) +func (s *roomAliasesStatements) insertRoomAlias(alias string, roomNID types.RoomNID) (err error) { + _, err = s.insertRoomAliasStmt.Exec(alias, roomNID) return } -func (s *roomAliasesStatements) selectRoomIDFromAlias(alias string) (roomID string, err error) { - err = s.selectRoomIDFromAliasStmt.QueryRow(alias).Scan(&roomID) +func (s *roomAliasesStatements) selectRoomNIDFromAlias(alias string) (roomNID types.RoomNID, err error) { + err = s.selectRoomNIDFromAliasStmt.QueryRow(alias).Scan(&roomNID) if err == sql.ErrNoRows { - return "", nil + return 0, nil } return } -func (s *roomAliasesStatements) selectAliasesFromRoomID(roomID string) (aliases []string, err error) { +func (s *roomAliasesStatements) selectAliasesFromRoomNID(roomNID types.RoomNID) (aliases []string, err error) { aliases = []string{} - rows, err := s.selectAliasesFromRoomIDStmt.Query(roomID) + rows, err := s.selectAliasesFromRoomNIDStmt.Query(roomNID) if err != nil { return } @@ -94,6 +102,37 @@ func (s *roomAliasesStatements) selectAliasesFromRoomID(roomID string) (aliases return } +func (s *roomAliasesStatements) selectAliasesFromRoomNIDs(roomNIDs []types.RoomNID) (aliases map[types.RoomNID][]string, err error) { + aliases = make(map[types.RoomNID][]string) + // Convert the array of numeric IDs into one that can be used in the query + nIDs := []int64{} + for _, roomNID := range roomNIDs { + nIDs = append(nIDs, int64(roomNID)) + } + rows, err := s.selectAliasesFromRoomNIDsStmt.Query(pq.Int64Array(nIDs)) + + if err != nil { + return + } + + for rows.Next() { + var alias string + var roomNID types.RoomNID + + if err = rows.Scan(&alias, &roomNID); err != nil { + return + } + + if len(aliases[roomNID]) > 0 { + aliases[roomNID] = append(aliases[roomNID], alias) + } else { + aliases[roomNID] = []string{alias} + } + } + + return +} + func (s *roomAliasesStatements) deleteRoomAlias(alias string) (err error) { _, err = s.deleteRoomAliasStmt.Exec(alias) return diff --git a/src/github.com/matrix-org/dendrite/roomserver/storage/rooms_table.go b/src/github.com/matrix-org/dendrite/roomserver/storage/rooms_table.go index 03cacd7db..6966ef88a 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/storage/rooms_table.go +++ b/src/github.com/matrix-org/dendrite/roomserver/storage/rooms_table.go @@ -36,7 +36,11 @@ CREATE TABLE IF NOT EXISTS roomserver_rooms ( last_event_sent_nid BIGINT NOT NULL DEFAULT 0, -- The state of the room after the current set of latest events. -- This will be 0 if there are no latest events in the room. - state_snapshot_nid BIGINT NOT NULL DEFAULT 0 + state_snapshot_nid BIGINT NOT NULL DEFAULT 0, + -- The visibility of the room. + -- This will be true if the room has a public visibility, and false if it + -- has a private visibility. + visibility BOOLEAN NOT NULL DEFAULT false ); ` @@ -49,21 +53,41 @@ const insertRoomNIDSQL = "" + const selectRoomNIDSQL = "" + "SELECT room_nid FROM roomserver_rooms WHERE room_id = $1" +const selectRoomIDSQL = "" + + "SELECT room_id FROM roomserver_rooms WHERE room_nid = $1" + +const selectRoomIDsSQL = "" + + "SELECT room_nid, room_id FROM roomserver_rooms WHERE room_nid = ANY($1)" + const selectLatestEventNIDsSQL = "" + "SELECT latest_event_nids, state_snapshot_nid FROM roomserver_rooms WHERE room_nid = $1" const selectLatestEventNIDsForUpdateSQL = "" + "SELECT latest_event_nids, last_event_sent_nid, state_snapshot_nid FROM roomserver_rooms WHERE room_nid = $1 FOR UPDATE" +const selectVisibilityForRoomNIDSQL = "" + + "SELECT visibility FROM roomserver_rooms WHERE room_nid = $1" + +const selectPublicRoomIDsSQL = "" + + "SELECT room_nid FROM roomserver_rooms WHERE visibility = true" + const updateLatestEventNIDsSQL = "" + "UPDATE roomserver_rooms SET latest_event_nids = $2, last_event_sent_nid = $3, state_snapshot_nid = $4 WHERE room_nid = $1" +const updateVisibilityForRoomNIDSQL = "" + + "UPDATE roomserver_rooms SET visibility = $1 WHERE room_nid = $2" + type roomStatements struct { insertRoomNIDStmt *sql.Stmt selectRoomNIDStmt *sql.Stmt + selectRoomIDStmt *sql.Stmt + selectRoomIDsStmt *sql.Stmt selectLatestEventNIDsStmt *sql.Stmt selectLatestEventNIDsForUpdateStmt *sql.Stmt + selectVisibilityForRoomNIDStmt *sql.Stmt + selectPublicRoomIDsStmt *sql.Stmt updateLatestEventNIDsStmt *sql.Stmt + updateVisibilityForRoomNIDStmt *sql.Stmt } func (s *roomStatements) prepare(db *sql.DB) (err error) { @@ -74,9 +98,14 @@ func (s *roomStatements) prepare(db *sql.DB) (err error) { return statementList{ {&s.insertRoomNIDStmt, insertRoomNIDSQL}, {&s.selectRoomNIDStmt, selectRoomNIDSQL}, + {&s.selectRoomIDStmt, selectRoomIDSQL}, + {&s.selectRoomIDsStmt, selectRoomIDsSQL}, {&s.selectLatestEventNIDsStmt, selectLatestEventNIDsSQL}, {&s.selectLatestEventNIDsForUpdateStmt, selectLatestEventNIDsForUpdateSQL}, + {&s.selectVisibilityForRoomNIDStmt, selectVisibilityForRoomNIDSQL}, + {&s.selectPublicRoomIDsStmt, selectPublicRoomIDsSQL}, {&s.updateLatestEventNIDsStmt, updateLatestEventNIDsSQL}, + {&s.updateVisibilityForRoomNIDStmt, updateVisibilityForRoomNIDSQL}, }.prepare(db) } @@ -92,6 +121,39 @@ func (s *roomStatements) selectRoomNID(roomID string) (types.RoomNID, error) { return types.RoomNID(roomNID), err } +func (s *roomStatements) selectRoomID(roomNID types.RoomNID) (string, error) { + var roomID string + err := s.selectRoomIDStmt.QueryRow(roomNID).Scan(&roomID) + return roomID, err +} + +func (s *roomStatements) selectRoomIDs(roomNIDs []types.RoomNID) (map[types.RoomNID]string, error) { + roomIDs := make(map[types.RoomNID]string) + + nIDs := []int64{} + for _, roomNID := range roomNIDs { + nIDs = append(nIDs, int64(roomNID)) + } + + rows, err := s.selectRoomIDsStmt.Query(pq.Int64Array(nIDs)) + if err != nil { + return roomIDs, err + } + + for rows.Next() { + var roomNID types.RoomNID + var roomID string + + if err := rows.Scan(&roomNID, &roomID); err != nil { + return roomIDs, err + } + + roomIDs[roomNID] = roomID + } + + return roomIDs, nil +} + func (s *roomStatements) selectLatestEventNIDs(roomNID types.RoomNID) ([]types.EventNID, types.StateSnapshotNID, error) { var nids pq.Int64Array var stateSnapshotNID int64 @@ -123,6 +185,35 @@ func (s *roomStatements) selectLatestEventsNIDsForUpdate(txn *sql.Tx, roomNID ty return eventNIDs, types.EventNID(lastEventSentNID), types.StateSnapshotNID(stateSnapshotNID), nil } +func (s *roomStatements) selectVisibilityForRoomNID(roomNID types.RoomNID) (bool, error) { + var visibility bool + err := s.selectVisibilityForRoomNIDStmt.QueryRow(roomNID).Scan(&visibility) + return visibility, err +} + +func (s *roomStatements) selectPublicRoomNIDs() ([]types.RoomNID, error) { + roomNIDs := []types.RoomNID{} + rows, err := s.selectPublicRoomIDsStmt.Query() + if err != nil { + return roomNIDs, err + } + + for rows.Next() { + var roomID types.RoomNID + if err = rows.Scan(&roomID); err != nil { + return roomNIDs, err + } + roomNIDs = append(roomNIDs, roomID) + } + + return roomNIDs, nil +} + +func (s *roomStatements) updateVisibilityForRoomNID(roomNID types.RoomNID, visibility bool) error { + _, err := s.updateVisibilityForRoomNIDStmt.Exec(visibility, roomNID) + return err +} + func (s *roomStatements) updateLatestEventNIDs( txn *sql.Tx, roomNID types.RoomNID, eventNIDs []types.EventNID, lastEventSentNID types.EventNID, stateSnapshotNID types.StateSnapshotNID, diff --git a/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go b/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go index d323fd139..388f61cc2 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go +++ b/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go @@ -353,19 +353,29 @@ func (d *Database) LatestEventIDs(roomNID types.RoomNID) ([]gomatrixserverlib.Ev return references, currentStateSnapshotNID, depth, nil } +// RoomID implements alias.RoomserverAliasAPIDB +func (d *Database) RoomID(roomNID types.RoomNID) (string, error) { + return d.statements.selectRoomID(roomNID) +} + // SetRoomAlias implements alias.RoomserverAliasAPIDB -func (d *Database) SetRoomAlias(alias string, roomID string) error { - return d.statements.insertRoomAlias(alias, roomID) +func (d *Database) SetRoomAlias(alias string, roomNID types.RoomNID) error { + return d.statements.insertRoomAlias(alias, roomNID) } -// GetRoomIDFromAlias implements alias.RoomserverAliasAPIDB -func (d *Database) GetRoomIDFromAlias(alias string) (string, error) { - return d.statements.selectRoomIDFromAlias(alias) +// GetRoomNIDFromAlias implements alias.RoomserverAliasAPIDB +func (d *Database) GetRoomNIDFromAlias(alias string) (types.RoomNID, error) { + return d.statements.selectRoomNIDFromAlias(alias) } -// GetAliasesFromRoomID implements alias.RoomserverAliasAPIDB -func (d *Database) GetAliasesFromRoomID(roomID string) ([]string, error) { - return d.statements.selectAliasesFromRoomID(roomID) +// GetAliasesFromRoomNID implements alias.RoomserverAliasAPIDB +func (d *Database) GetAliasesFromRoomNID(roomNID types.RoomNID) ([]string, error) { + return d.statements.selectAliasesFromRoomNID(roomNID) +} + +// GetAliasesFromRoomNIDs implements publicroom.RoomserverPublicRoomAPIDB +func (d *Database) GetAliasesFromRoomNIDs(roomNIDs []types.RoomNID) (map[types.RoomNID][]string, error) { + return d.statements.selectAliasesFromRoomNIDs(roomNIDs) } // RemoveRoomAlias implements alias.RoomserverAliasAPIDB @@ -380,6 +390,31 @@ func (d *Database) StateEntriesForTuples( return d.statements.bulkSelectFilteredStateBlockEntries(stateBlockNIDs, stateKeyTuples) } +// RoomIDs implements publicroom.RoomserverPublicRoomAPIDB +func (d *Database) RoomIDs(roomNIDs []types.RoomNID) (map[types.RoomNID]string, error) { + return d.statements.selectRoomIDs(roomNIDs) +} + +// IsRoomPublic implements publicroom.RoomserverPublicRoomAPIDB +func (d *Database) IsRoomPublic(roomNID types.RoomNID) (bool, error) { + return d.statements.selectVisibilityForRoomNID(roomNID) +} + +// GetPublicRoomNIDs implements publicroom.RoomserverPublicRoomAPIDB +func (d *Database) GetPublicRoomNIDs() ([]types.RoomNID, error) { + return d.statements.selectPublicRoomNIDs() +} + +// UpdateRoomVisibility implements publicroom.RoomserverPublicRoomAPIDB +func (d *Database) UpdateRoomVisibility(roomNID types.RoomNID, visibility bool) error { + return d.statements.updateVisibilityForRoomNID(roomNID, visibility) +} + +// CountJoinedMembersInRooms implements publicroom.RoomserverPublicRoomAPIDB +func (d *Database) CountJoinedMembersInRooms(roomNIDs []types.RoomNID) (map[types.RoomNID]int64, error) { + return d.statements.countJoinedMembersInRooms(roomNIDs) +} + type membershipUpdater struct { transaction d *Database