mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-08 23:43:11 -06:00
Start implementing the join room API
This commit is contained in:
parent
e6835660b0
commit
e9bf826255
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/matrix-org/dendrite/clientapi/writers"
|
"github.com/matrix-org/dendrite/clientapi/writers"
|
||||||
"github.com/matrix-org/dendrite/common"
|
"github.com/matrix-org/dendrite/common"
|
||||||
"github.com/matrix-org/dendrite/roomserver/api"
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
)
|
)
|
||||||
|
|
@ -36,8 +37,13 @@ const pathPrefixR0 = "/_matrix/client/r0"
|
||||||
|
|
||||||
// Setup registers HTTP handlers with the given ServeMux. It also supplies the given http.Client
|
// Setup registers HTTP handlers with the given ServeMux. It also supplies the given http.Client
|
||||||
// to clients which need to make outbound HTTP requests.
|
// to clients which need to make outbound HTTP requests.
|
||||||
func Setup(servMux *http.ServeMux, httpClient *http.Client, cfg config.ClientAPI, producer *producers.RoomserverProducer,
|
func Setup(
|
||||||
queryAPI api.RoomserverQueryAPI, accountDB *accounts.Database, deviceDB *devices.Database) {
|
servMux *http.ServeMux, httpClient *http.Client, cfg config.ClientAPI,
|
||||||
|
producer *producers.RoomserverProducer, queryAPI api.RoomserverQueryAPI,
|
||||||
|
accountDB *storage.AccountDatabase,
|
||||||
|
deviceDB *devices.Database,
|
||||||
|
federation *gomatrixserverlib.FederationClient,
|
||||||
|
) {
|
||||||
apiMux := mux.NewRouter()
|
apiMux := mux.NewRouter()
|
||||||
r0mux := apiMux.PathPrefix(pathPrefixR0).Subrouter()
|
r0mux := apiMux.PathPrefix(pathPrefixR0).Subrouter()
|
||||||
r0mux.Handle("/createRoom",
|
r0mux.Handle("/createRoom",
|
||||||
|
|
@ -45,6 +51,14 @@ func Setup(servMux *http.ServeMux, httpClient *http.Client, cfg config.ClientAPI
|
||||||
return writers.CreateRoom(req, device, cfg, producer)
|
return writers.CreateRoom(req, device, cfg, producer)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
r0mux.Handle("/join/{roomIDOrAlias}",
|
||||||
|
makeAPI("join", func(req *http.Request) util.JSONResponse {
|
||||||
|
vars := mux.Vars(req)
|
||||||
|
return writers.JoinRoomByIDOrAlias(
|
||||||
|
req, vars["roomIDOrAlias"], cfg, federation, producer, queryAPI,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
r0mux.Handle("/rooms/{roomID}/send/{eventType}/{txnID}",
|
r0mux.Handle("/rooms/{roomID}/send/{eventType}/{txnID}",
|
||||||
common.MakeAuthAPI("send_message", deviceDB, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
|
common.MakeAuthAPI("send_message", deviceDB, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
|
||||||
vars := mux.Vars(req)
|
vars := mux.Vars(req)
|
||||||
|
|
|
||||||
223
src/github.com/matrix-org/dendrite/clientapi/writers/joinroom.go
Normal file
223
src/github.com/matrix-org/dendrite/clientapi/writers/joinroom.go
Normal file
|
|
@ -0,0 +1,223 @@
|
||||||
|
// 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 (
|
||||||
|
"fmt"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/auth"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/config"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/producers"
|
||||||
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
|
"github.com/matrix-org/gomatrix"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// JoinRoomByIDOrAlias implements the "/join/{roomIDOrAlias}" API.
|
||||||
|
// https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-join-roomidoralias
|
||||||
|
func JoinRoomByIDOrAlias(
|
||||||
|
req *http.Request,
|
||||||
|
roomIDOrAlias string,
|
||||||
|
cfg config.ClientAPI,
|
||||||
|
federation *gomatrixserverlib.FederationClient,
|
||||||
|
producer *producers.RoomserverProducer,
|
||||||
|
queryAPI api.RoomserverQueryAPI,
|
||||||
|
) util.JSONResponse {
|
||||||
|
userID, resErr := auth.VerifyAccessToken(req)
|
||||||
|
if resErr != nil {
|
||||||
|
return *resErr
|
||||||
|
}
|
||||||
|
var content map[string]interface{} // must be a JSON object
|
||||||
|
if resErr := httputil.UnmarshalJSONRequest(req, &content); resErr != nil {
|
||||||
|
return *resErr
|
||||||
|
}
|
||||||
|
|
||||||
|
content["membership"] = "join"
|
||||||
|
|
||||||
|
r := joinRoomReq{req, content, userID, cfg, federation, producer, queryAPI}
|
||||||
|
|
||||||
|
if strings.HasPrefix(roomIDOrAlias, "!") {
|
||||||
|
return r.joinRoomByID()
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(roomIDOrAlias, "#") {
|
||||||
|
return r.joinRoomByAlias(roomIDOrAlias)
|
||||||
|
}
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: 400,
|
||||||
|
JSON: jsonerror.BadJSON("Invalid first character for room ID or alias"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type joinRoomReq struct {
|
||||||
|
req *http.Request
|
||||||
|
content map[string]interface{}
|
||||||
|
userID string
|
||||||
|
cfg config.ClientAPI
|
||||||
|
federation *gomatrixserverlib.FederationClient
|
||||||
|
producer *producers.RoomserverProducer
|
||||||
|
queryAPI api.RoomserverQueryAPI
|
||||||
|
}
|
||||||
|
|
||||||
|
// joinRoomByID joins a room by roomID
|
||||||
|
func (r joinRoomReq) joinRoomByID() util.JSONResponse {
|
||||||
|
// TODO: Implement joining rooms by ID.
|
||||||
|
// A client should only join a room by roomID when it has an invite
|
||||||
|
// to the room. If the server is already in the room then we can
|
||||||
|
// lookup the invite and process the request as a normal state event.
|
||||||
|
// If the server is not in the room the we will need to look up the
|
||||||
|
// remote server the invite came from in order to request a join event
|
||||||
|
// from that server.
|
||||||
|
panic(fmt.Errorf("Joining rooms by ID is not implemented"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// joinRoomByAlias joins a room using a room alias.
|
||||||
|
func (r joinRoomReq) joinRoomByAlias(roomAlias string) util.JSONResponse {
|
||||||
|
domain, err := domainFromID(roomAlias)
|
||||||
|
if err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: 400,
|
||||||
|
JSON: jsonerror.BadJSON("Room alias must be in the form '#localpart:domain'"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if domain == r.cfg.ServerName {
|
||||||
|
// TODO: Implement joining local room aliases.
|
||||||
|
panic(fmt.Errorf("Joining local room aliases is not implemented"))
|
||||||
|
} else {
|
||||||
|
return r.joinRoomByRemoteAlias(domain, roomAlias)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r joinRoomReq) joinRoomByRemoteAlias(
|
||||||
|
domain gomatrixserverlib.ServerName, roomAlias string,
|
||||||
|
) util.JSONResponse {
|
||||||
|
resp, err := r.federation.LookupRoomAlias(domain, roomAlias)
|
||||||
|
if err != nil {
|
||||||
|
switch x := err.(type) {
|
||||||
|
case gomatrix.HTTPError:
|
||||||
|
if x.Code == 404 {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: 404,
|
||||||
|
JSON: jsonerror.NotFound("Room alias not found"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.joinRoomUsingServers(resp.RoomID, resp.Servers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r joinRoomReq) writeToBuilder(eb *gomatrixserverlib.EventBuilder, roomID string) {
|
||||||
|
eb.Type = "m.room.member"
|
||||||
|
eb.SetContent(r.content) // TODO: Set avatar_url / displayname
|
||||||
|
eb.SetUnsigned(struct{}{})
|
||||||
|
eb.Sender = r.userID
|
||||||
|
eb.StateKey = &r.userID
|
||||||
|
eb.RoomID = roomID
|
||||||
|
eb.Redacts = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r joinRoomReq) joinRoomUsingServers(
|
||||||
|
roomID string, servers []gomatrixserverlib.ServerName,
|
||||||
|
) util.JSONResponse {
|
||||||
|
var eb gomatrixserverlib.EventBuilder
|
||||||
|
r.writeToBuilder(&eb, roomID)
|
||||||
|
|
||||||
|
needed, err := gomatrixserverlib.StateNeededForEventBuilder(&eb)
|
||||||
|
if err != nil {
|
||||||
|
return httputil.LogThenError(r.req, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask the roomserver for information about this room
|
||||||
|
queryReq := api.QueryLatestEventsAndStateRequest{
|
||||||
|
RoomID: roomID,
|
||||||
|
StateToFetch: needed.Tuples(),
|
||||||
|
}
|
||||||
|
var queryRes api.QueryLatestEventsAndStateResponse
|
||||||
|
if queryErr := r.queryAPI.QueryLatestEventsAndState(&queryReq, &queryRes); queryErr != nil {
|
||||||
|
return httputil.LogThenError(r.req, queryErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if queryRes.RoomExists {
|
||||||
|
// TODO: Implement joining rooms that already the server is already in.
|
||||||
|
// This should just fall through to the usual event sending code.
|
||||||
|
panic(fmt.Errorf("Joining rooms that the server already in is not implemented"))
|
||||||
|
}
|
||||||
|
|
||||||
|
var event gomatrixserverlib.Event
|
||||||
|
var respMakeJoin gomatrixserverlib.RespMakeJoin
|
||||||
|
//var respSendJoin gomatrixserverlib.RespSendJoin
|
||||||
|
for _, server := range servers {
|
||||||
|
respMakeJoin, err = r.federation.MakeJoin(server, roomID, r.userID)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: Check if the user was not allowed to join the room.
|
||||||
|
|
||||||
|
// Try the next server in the list.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set all the fields to be what they should be, this should be a no-op
|
||||||
|
// but it's possible that the remote server returned us something "odd"
|
||||||
|
r.writeToBuilder(&respMakeJoin.JoinEvent, roomID)
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
eventID := fmt.Sprintf("$%s:%s", util.RandomString(16), r.cfg.ServerName)
|
||||||
|
if event, err = respMakeJoin.JoinEvent.Build(
|
||||||
|
eventID, now, r.cfg.ServerName, r.cfg.KeyID, r.cfg.PrivateKey,
|
||||||
|
); err != nil {
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = r.federation.SendJoin(server, event)
|
||||||
|
if err != nil {
|
||||||
|
// Try the next server in the list.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: validate the state response.
|
||||||
|
// TODO: check that the join event passes auth against the state response.
|
||||||
|
// TODO: send the state and the join event to the room server.
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: 404,
|
||||||
|
JSON: jsonerror.NotFound("No candidate servers found for room"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// domainFromID returns everything after the first ":" character to extract
|
||||||
|
// the domain part of a matrix ID.
|
||||||
|
// TODO: duplicated from gomatrixserverlib.
|
||||||
|
func domainFromID(id string) (gomatrixserverlib.ServerName, error) {
|
||||||
|
// IDs have the format: SIGIL LOCALPART ":" DOMAIN
|
||||||
|
// Split on the first ":" character since the domain can contain ":"
|
||||||
|
// characters.
|
||||||
|
parts := strings.SplitN(id, ":", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
// The ID must have a ":" character.
|
||||||
|
return "", fmt.Errorf("invalid ID: %q", id)
|
||||||
|
}
|
||||||
|
// Return everything after the first ":" character.
|
||||||
|
return gomatrixserverlib.ServerName(parts[1]), nil
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/matrix-org/dendrite/clientapi/routing"
|
"github.com/matrix-org/dendrite/clientapi/routing"
|
||||||
"github.com/matrix-org/dendrite/common"
|
"github.com/matrix-org/dendrite/common"
|
||||||
"github.com/matrix-org/dendrite/roomserver/api"
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
|
|
@ -81,6 +82,8 @@ func main() {
|
||||||
log.Panicf("Failed to setup kafka producers(%s): %s", cfg.KafkaProducerURIs, err)
|
log.Panicf("Failed to setup kafka producers(%s): %s", cfg.KafkaProducerURIs, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
federation := gomatrixserverlib.NewFederationClient(cfg.ServerName, cfg.KeyID, cfg.PrivateKey)
|
||||||
|
|
||||||
queryAPI := api.NewRoomserverQueryAPIHTTP(cfg.RoomserverURL, nil)
|
queryAPI := api.NewRoomserverQueryAPIHTTP(cfg.RoomserverURL, nil)
|
||||||
accountDB, err := accounts.NewDatabase(accountDataSource, serverName)
|
accountDB, err := accounts.NewDatabase(accountDataSource, serverName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -91,6 +94,6 @@ func main() {
|
||||||
log.Panicf("Failed to setup device database(%s): %s", accountDataSource, err.Error())
|
log.Panicf("Failed to setup device database(%s): %s", accountDataSource, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
routing.Setup(http.DefaultServeMux, http.DefaultClient, cfg, roomserverProducer, queryAPI, accountDB, deviceDB)
|
routing.Setup(http.DefaultServeMux, http.DefaultClient, cfg, roomserverProducer, queryAPI, accountDB, deviceDB, federation)
|
||||||
log.Fatal(http.ListenAndServe(bindAddr, nil))
|
log.Fatal(http.ListenAndServe(bindAddr, nil))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue