diff --git a/src/github.com/matrix-org/dendrite/clientapi/readers/memberships.go b/src/github.com/matrix-org/dendrite/clientapi/readers/memberships.go index 77c3d85aa..c734e3b34 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/readers/memberships.go +++ b/src/github.com/matrix-org/dendrite/clientapi/readers/memberships.go @@ -23,7 +23,6 @@ import ( "github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/common/config" "github.com/matrix-org/dendrite/roomserver/api" - "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" ) @@ -33,59 +32,24 @@ func GetMemberships( accountDB *accounts.Database, cfg config.Dendrite, queryAPI api.RoomserverQueryAPI, ) util.JSONResponse { - localpart, _, err := gomatrixserverlib.SplitID('@', device.UserID) - if err != nil { - return httputil.LogThenError(req, err) + queryReq := api.QueryMembershipsForRoomRequest{ + RoomID: roomID, + Sender: device.UserID, } - _, server, err := gomatrixserverlib.SplitID('!', roomID) - if err != nil { + var queryRes api.QueryMembershipsForRoomResponse + if err := queryAPI.QueryMembershipsForRoom(&queryReq, &queryRes); err != nil { return httputil.LogThenError(req, err) } - events := []gomatrixserverlib.ClientEvent{} - if server == cfg.Matrix.ServerName { - // TODO: If the user has been in the room before but isn't - // anymore, only send the members list as it was before they left. - membership, err := accountDB.GetMembership(localpart, roomID) - if err != nil { - return httputil.LogThenError(req, err) + if !queryRes.HasBeenInRoom { + return util.JSONResponse{ + Code: 403, + JSON: jsonerror.Forbidden("You aren't a member of the room and weren't previously a member of the room."), } - - if membership == nil { - return util.JSONResponse{ - Code: 403, - JSON: jsonerror.Forbidden("You aren't a member of the room and weren't previously a member of the room."), - } - } - - memberships, err := accountDB.GetMembershipsByRoomID(roomID) - if err != nil { - return httputil.LogThenError(req, err) - } - - eventIDs := []string{} - for _, membership := range memberships { - eventIDs = append(eventIDs, membership.EventID) - } - - queryReq := api.QueryEventsByIDRequest{ - EventIDs: eventIDs, - } - var queryRes api.QueryEventsByIDResponse - if err := queryAPI.QueryEventsByID(&queryReq, &queryRes); err != nil { - return httputil.LogThenError(req, err) - } - - for _, event := range queryRes.Events { - ev := gomatrixserverlib.ToClientEvent(event, gomatrixserverlib.FormatAll) - events = append(events, ev) - } - } else { - // TODO: Get memberships from federation } return util.JSONResponse{ Code: 200, - JSON: events, + JSON: queryRes.JoinEvents, } } diff --git a/src/github.com/matrix-org/dendrite/roomserver/api/query.go b/src/github.com/matrix-org/dendrite/roomserver/api/query.go index 6e6a838a9..f07da59e5 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/api/query.go +++ b/src/github.com/matrix-org/dendrite/roomserver/api/query.go @@ -100,6 +100,23 @@ type QueryEventsByIDResponse struct { Events []gomatrixserverlib.Event `json:"events"` } +// QueryMembershipsForRoomRequest is a request to QueryMembershipsForRoom +type QueryMembershipsForRoomRequest struct { + // ID of the room to fetch memberships from + RoomID string `json:"room_id"` + // ID of the user sending the request + Sender string `json:"sender"` +} + +// QueryMembershipsForRoomResponse is a response to QueryMembershipsForRoom +type QueryMembershipsForRoomResponse struct { + // The "m.room.member" events (of "join" membership) in the client format + JoinEvents []gomatrixserverlib.ClientEvent `json:"join_events"` + // True if the user has been in room before and has either stayed in it or + // left it. + HasBeenInRoom bool `json:"has_been_in_room"` +} + // RoomserverQueryAPI is used to query information from the room server. type RoomserverQueryAPI interface { // Query the latest events and state for a room from the room server. @@ -119,6 +136,12 @@ type RoomserverQueryAPI interface { request *QueryEventsByIDRequest, response *QueryEventsByIDResponse, ) error + + // Query a list of membership events for a room + QueryMembershipsForRoom( + request *QueryMembershipsForRoomRequest, + response *QueryMembershipsForRoomResponse, + ) error } // RoomserverQueryLatestEventsAndStatePath is the HTTP path for the QueryLatestEventsAndState API. @@ -130,6 +153,9 @@ const RoomserverQueryStateAfterEventsPath = "/api/roomserver/queryStateAfterEven // RoomserverQueryEventsByIDPath is the HTTP path for the QueryEventsByID API. const RoomserverQueryEventsByIDPath = "/api/roomserver/queryEventsByID" +// RoomserverQueryMembershipsForRoomPath is the HTTP path for the QueryMembershipsForRoom API +const RoomserverQueryMembershipsForRoomPath = "/api/roomserver/queryMembershipsForRoom" + // NewRoomserverQueryAPIHTTP creates a RoomserverQueryAPI implemented by talking to a HTTP POST API. // If httpClient is nil then it uses the http.DefaultClient func NewRoomserverQueryAPIHTTP(roomserverURL string, httpClient *http.Client) RoomserverQueryAPI { @@ -171,6 +197,15 @@ func (h *httpRoomserverQueryAPI) QueryEventsByID( return postJSON(h.httpClient, apiURL, request, response) } +// QueryMembershipsForRoom implements RoomserverQueryAPI +func (h *httpRoomserverQueryAPI) QueryMembershipsForRoom( + request *QueryMembershipsForRoomRequest, + response *QueryMembershipsForRoomResponse, +) error { + apiURL := h.roomserverURL + RoomserverQueryMembershipsForRoomPath + return postJSON(h.httpClient, apiURL, request, response) +} + func postJSON(httpClient *http.Client, apiURL string, request, response interface{}) error { jsonBytes, err := json.Marshal(request) if err != nil { 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..84f5d44c3 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/query/query.go +++ b/src/github.com/matrix-org/dendrite/roomserver/query/query.go @@ -52,6 +52,13 @@ type RoomserverQueryAPIDatabase interface { // Remove a given room alias. // Returns an error if there was a problem talking to the database. RemoveRoomAlias(alias string) error + // Lookup the join events for all members in a room as requested by a given + // user. If the user is currently in the room, returns the room's current + // members, if not returns an empty array (TODO: Fix it) + // If the user requesting the list of members has never been in the room, + // returns nil. + // If there was an issue retrieving the events, returns an error. + GetMembershipEvents(roomNID types.RoomNID, requestSenderUserID string) (events []types.Event, err error) } // RoomserverQueryAPI is an implementation of api.RoomserverQueryAPI @@ -182,6 +189,37 @@ func (r *RoomserverQueryAPI) loadEvents(eventNIDs []types.EventNID) ([]gomatrixs return result, nil } +// QueryMembershipsForRoom implements api.RoomserverQueryAPI +func (r *RoomserverQueryAPI) QueryMembershipsForRoom( + request *api.QueryMembershipsForRoomRequest, + response *api.QueryMembershipsForRoomResponse, +) error { + roomNID, err := r.DB.RoomNID(request.RoomID) + if err != nil { + return err + } + + events, err := r.DB.GetMembershipEvents(roomNID, request.Sender) + if err != nil { + return nil + } + + if events == nil { + response.HasBeenInRoom = false + response.JoinEvents = nil + return nil + } + + response.HasBeenInRoom = true + response.JoinEvents = []gomatrixserverlib.ClientEvent{} + for _, event := range events { + clientEvent := gomatrixserverlib.ToClientEvent(event.Event, gomatrixserverlib.FormatAll) + response.JoinEvents = append(response.JoinEvents, clientEvent) + } + + return nil +} + // SetupHTTP adds the RoomserverQueryAPI handlers to the http.ServeMux. func (r *RoomserverQueryAPI) SetupHTTP(servMux *http.ServeMux) { servMux.Handle( @@ -226,4 +264,18 @@ func (r *RoomserverQueryAPI) SetupHTTP(servMux *http.ServeMux) { return util.JSONResponse{Code: 200, JSON: &response} }), ) + servMux.Handle( + api.RoomserverQueryMembershipsForRoomPath, + common.MakeAPI("queryMembershipsForRoom", func(req *http.Request) util.JSONResponse { + var request api.QueryMembershipsForRoomRequest + var response api.QueryMembershipsForRoomResponse + if err := json.NewDecoder(req.Body).Decode(&request); err != nil { + return util.ErrorResponse(err) + } + if err := r.QueryMembershipsForRoom(&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/storage/storage.go b/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go index ac6a8f664..17b30860c 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go +++ b/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go @@ -508,18 +508,13 @@ func (u *membershipUpdater) SetToLeave(senderUserID string, eventID string) ([]s return inviteEventIDs, nil } -// GetMembershipEvents returns an array containing the join events for all -// members in a room as requested by a given user. If the user is currently in -// the room, returns the room's current members, if not returns an empty array -// TODO: in this case, send the list of members as it was when the user left -// If the user requesting the list of members has never been in the room, returns -// nil. -// If there was an issue retrieving the events, returns an error. +// GetMembershipEvents implements query.RoomserverQueryAPIDB func (d *Database) GetMembershipEvents(roomNID types.RoomNID, requestSenderUserID string) (events []types.Event, err error) { txn, err := d.db.Begin() if err != nil { return } + defer txn.Commit() requestSenderUserNID, err := d.assignStateKeyNID(txn, requestSenderUserID) if err != nil { @@ -550,6 +545,9 @@ func (d *Database) GetMembershipEvents(roomNID types.RoomNID, requestSenderUserI // only stores the latest join event NID for a given target user. // The solution would be to build the state of a room after before // the leave event and extract a members list from it. + // For now, we return an empty slice so we know the user has been + // in the room before. + events = []types.Event{} } return