mirror of
https://github.com/hoernschen/dendrite.git
synced 2024-12-27 07:28:27 +00:00
Move CreateRoom logic to Roomserver (#3093)
Move create room logic over to roomserver.
This commit is contained in:
parent
61341aca50
commit
cbdc601f1b
24 changed files with 684 additions and 550 deletions
|
@ -22,17 +22,13 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/getsentry/sentry-go"
|
|
||||||
appserviceAPI "github.com/matrix-org/dendrite/appservice/api"
|
appserviceAPI "github.com/matrix-org/dendrite/appservice/api"
|
||||||
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/dendrite/roomserver/types"
|
|
||||||
roomserverVersion "github.com/matrix-org/dendrite/roomserver/version"
|
roomserverVersion "github.com/matrix-org/dendrite/roomserver/version"
|
||||||
"github.com/matrix-org/dendrite/userapi/api"
|
"github.com/matrix-org/dendrite/userapi/api"
|
||||||
"github.com/matrix-org/gomatrixserverlib/fclient"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/httputil"
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
"github.com/matrix-org/dendrite/internal/eventutil"
|
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
|
@ -41,32 +37,19 @@ import (
|
||||||
|
|
||||||
// https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
|
// https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
|
||||||
type createRoomRequest struct {
|
type createRoomRequest struct {
|
||||||
Invite []string `json:"invite"`
|
Invite []string `json:"invite"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Visibility string `json:"visibility"`
|
Visibility string `json:"visibility"`
|
||||||
Topic string `json:"topic"`
|
Topic string `json:"topic"`
|
||||||
Preset string `json:"preset"`
|
Preset string `json:"preset"`
|
||||||
CreationContent json.RawMessage `json:"creation_content"`
|
CreationContent json.RawMessage `json:"creation_content"`
|
||||||
InitialState []fledglingEvent `json:"initial_state"`
|
InitialState []gomatrixserverlib.FledglingEvent `json:"initial_state"`
|
||||||
RoomAliasName string `json:"room_alias_name"`
|
RoomAliasName string `json:"room_alias_name"`
|
||||||
RoomVersion gomatrixserverlib.RoomVersion `json:"room_version"`
|
RoomVersion gomatrixserverlib.RoomVersion `json:"room_version"`
|
||||||
PowerLevelContentOverride json.RawMessage `json:"power_level_content_override"`
|
PowerLevelContentOverride json.RawMessage `json:"power_level_content_override"`
|
||||||
IsDirect bool `json:"is_direct"`
|
IsDirect bool `json:"is_direct"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
presetPrivateChat = "private_chat"
|
|
||||||
presetTrustedPrivateChat = "trusted_private_chat"
|
|
||||||
presetPublicChat = "public_chat"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
historyVisibilityShared = "shared"
|
|
||||||
// TODO: These should be implemented once history visibility is implemented
|
|
||||||
// historyVisibilityWorldReadable = "world_readable"
|
|
||||||
// historyVisibilityInvited = "invited"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (r createRoomRequest) Validate() *util.JSONResponse {
|
func (r createRoomRequest) Validate() *util.JSONResponse {
|
||||||
whitespace := "\t\n\x0b\x0c\r " // https://docs.python.org/2/library/string.html#string.whitespace
|
whitespace := "\t\n\x0b\x0c\r " // https://docs.python.org/2/library/string.html#string.whitespace
|
||||||
// https://github.com/matrix-org/synapse/blob/v0.19.2/synapse/handlers/room.py#L81
|
// https://github.com/matrix-org/synapse/blob/v0.19.2/synapse/handlers/room.py#L81
|
||||||
|
@ -78,12 +61,7 @@ func (r createRoomRequest) Validate() *util.JSONResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, userID := range r.Invite {
|
for _, userID := range r.Invite {
|
||||||
// TODO: We should put user ID parsing code into gomatrixserverlib and use that instead
|
if _, err := spec.NewUserID(userID, true); err != nil {
|
||||||
// (see https://github.com/matrix-org/gomatrixserverlib/blob/3394e7c7003312043208aa73727d2256eea3d1f6/eventcontent.go#L347 )
|
|
||||||
// It should be a struct (with pointers into a single string to avoid copying) and
|
|
||||||
// we should update all refs to use UserID types rather than strings.
|
|
||||||
// https://github.com/matrix-org/synapse/blob/v0.19.2/synapse/types.py#L92
|
|
||||||
if _, _, err := gomatrixserverlib.SplitID('@', userID); err != nil {
|
|
||||||
return &util.JSONResponse{
|
return &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: spec.BadJSON("user id must be in the form @localpart:domain"),
|
JSON: spec.BadJSON("user id must be in the form @localpart:domain"),
|
||||||
|
@ -91,7 +69,7 @@ func (r createRoomRequest) Validate() *util.JSONResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch r.Preset {
|
switch r.Preset {
|
||||||
case presetPrivateChat, presetTrustedPrivateChat, presetPublicChat, "":
|
case spec.PresetPrivateChat, spec.PresetTrustedPrivateChat, spec.PresetPublicChat, "":
|
||||||
default:
|
default:
|
||||||
return &util.JSONResponse{
|
return &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
|
@ -129,13 +107,6 @@ type createRoomResponse struct {
|
||||||
RoomAlias string `json:"room_alias,omitempty"` // in synapse not spec
|
RoomAlias string `json:"room_alias,omitempty"` // in synapse not spec
|
||||||
}
|
}
|
||||||
|
|
||||||
// fledglingEvent is a helper representation of an event used when creating many events in succession.
|
|
||||||
type fledglingEvent struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
StateKey string `json:"state_key"`
|
|
||||||
Content interface{} `json:"content"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateRoom implements /createRoom
|
// CreateRoom implements /createRoom
|
||||||
func CreateRoom(
|
func CreateRoom(
|
||||||
req *http.Request, device *api.Device,
|
req *http.Request, device *api.Device,
|
||||||
|
@ -143,12 +114,12 @@ func CreateRoom(
|
||||||
profileAPI api.ClientUserAPI, rsAPI roomserverAPI.ClientRoomserverAPI,
|
profileAPI api.ClientUserAPI, rsAPI roomserverAPI.ClientRoomserverAPI,
|
||||||
asAPI appserviceAPI.AppServiceInternalAPI,
|
asAPI appserviceAPI.AppServiceInternalAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
var r createRoomRequest
|
var createRequest createRoomRequest
|
||||||
resErr := httputil.UnmarshalJSONRequest(req, &r)
|
resErr := httputil.UnmarshalJSONRequest(req, &createRequest)
|
||||||
if resErr != nil {
|
if resErr != nil {
|
||||||
return *resErr
|
return *resErr
|
||||||
}
|
}
|
||||||
if resErr = r.Validate(); resErr != nil {
|
if resErr = createRequest.Validate(); resErr != nil {
|
||||||
return *resErr
|
return *resErr
|
||||||
}
|
}
|
||||||
evTime, err := httputil.ParseTSParam(req)
|
evTime, err := httputil.ParseTSParam(req)
|
||||||
|
@ -158,46 +129,52 @@ func CreateRoom(
|
||||||
JSON: spec.InvalidParam(err.Error()),
|
JSON: spec.InvalidParam(err.Error()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return createRoom(req.Context(), r, device, cfg, profileAPI, rsAPI, asAPI, evTime)
|
return createRoom(req.Context(), createRequest, device, cfg, profileAPI, rsAPI, asAPI, evTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createRoom implements /createRoom
|
// createRoom implements /createRoom
|
||||||
// nolint: gocyclo
|
|
||||||
func createRoom(
|
func createRoom(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
r createRoomRequest, device *api.Device,
|
// TODO: remove dependency on createRoomRequest
|
||||||
|
createRequest createRoomRequest, device *api.Device,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
profileAPI api.ClientUserAPI, rsAPI roomserverAPI.ClientRoomserverAPI,
|
profileAPI api.ClientUserAPI, rsAPI roomserverAPI.ClientRoomserverAPI,
|
||||||
asAPI appserviceAPI.AppServiceInternalAPI,
|
asAPI appserviceAPI.AppServiceInternalAPI,
|
||||||
evTime time.Time,
|
evTime time.Time,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
_, userDomain, err := gomatrixserverlib.SplitID('@', device.UserID)
|
userID, err := spec.NewUserID(device.UserID, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger(ctx).WithError(err).Error("gomatrixserverlib.SplitID failed")
|
util.GetLogger(ctx).WithError(err).Error("invalid userID")
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusInternalServerError,
|
Code: http.StatusInternalServerError,
|
||||||
JSON: spec.InternalServerError{},
|
JSON: spec.InternalServerError{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !cfg.Matrix.IsLocalServerName(userDomain) {
|
if !cfg.Matrix.IsLocalServerName(userID.Domain()) {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusForbidden,
|
Code: http.StatusForbidden,
|
||||||
JSON: spec.Forbidden(fmt.Sprintf("User domain %q not configured locally", userDomain)),
|
JSON: spec.Forbidden(fmt.Sprintf("User domain %q not configured locally", userID.Domain())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO (#267): Check room ID doesn't clash with an existing one, and we
|
|
||||||
// probably shouldn't be using pseudo-random strings, maybe GUIDs?
|
|
||||||
roomID := fmt.Sprintf("!%s:%s", util.RandomString(16), userDomain)
|
|
||||||
|
|
||||||
logger := util.GetLogger(ctx)
|
logger := util.GetLogger(ctx)
|
||||||
userID := device.UserID
|
|
||||||
|
// TODO: Check room ID doesn't clash with an existing one, and we
|
||||||
|
// probably shouldn't be using pseudo-random strings, maybe GUIDs?
|
||||||
|
roomID, err := spec.NewRoomID(fmt.Sprintf("!%s:%s", util.RandomString(16), userID.Domain()))
|
||||||
|
if err != nil {
|
||||||
|
util.GetLogger(ctx).WithError(err).Error("invalid roomID")
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Clobber keys: creator, room_version
|
// Clobber keys: creator, room_version
|
||||||
|
|
||||||
roomVersion := roomserverVersion.DefaultRoomVersion()
|
roomVersion := roomserverVersion.DefaultRoomVersion()
|
||||||
if r.RoomVersion != "" {
|
if createRequest.RoomVersion != "" {
|
||||||
candidateVersion := gomatrixserverlib.RoomVersion(r.RoomVersion)
|
candidateVersion := gomatrixserverlib.RoomVersion(createRequest.RoomVersion)
|
||||||
_, roomVersionError := roomserverVersion.SupportedRoomVersion(candidateVersion)
|
_, roomVersionError := roomserverVersion.SupportedRoomVersion(candidateVersion)
|
||||||
if roomVersionError != nil {
|
if roomVersionError != nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
@ -208,17 +185,13 @@ func createRoom(
|
||||||
roomVersion = candidateVersion
|
roomVersion = candidateVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: visibility/presets/raw initial state
|
|
||||||
// TODO: Create room alias association
|
|
||||||
// Make sure this doesn't fall into an application service's namespace though!
|
|
||||||
|
|
||||||
logger.WithFields(log.Fields{
|
logger.WithFields(log.Fields{
|
||||||
"userID": userID,
|
"userID": userID.String(),
|
||||||
"roomID": roomID,
|
"roomID": roomID.String(),
|
||||||
"roomVersion": roomVersion,
|
"roomVersion": roomVersion,
|
||||||
}).Info("Creating new room")
|
}).Info("Creating new room")
|
||||||
|
|
||||||
profile, err := appserviceAPI.RetrieveUserProfile(ctx, userID, asAPI, profileAPI)
|
profile, err := appserviceAPI.RetrieveUserProfile(ctx, userID.String(), asAPI, profileAPI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger(ctx).WithError(err).Error("appserviceAPI.RetrieveUserProfile failed")
|
util.GetLogger(ctx).WithError(err).Error("appserviceAPI.RetrieveUserProfile failed")
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
@ -227,427 +200,38 @@ func createRoom(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createContent := map[string]interface{}{}
|
userDisplayName := profile.DisplayName
|
||||||
if len(r.CreationContent) > 0 {
|
userAvatarURL := profile.AvatarURL
|
||||||
if err = json.Unmarshal(r.CreationContent, &createContent); err != nil {
|
|
||||||
util.GetLogger(ctx).WithError(err).Error("json.Unmarshal for creation_content failed")
|
keyID := cfg.Matrix.KeyID
|
||||||
return util.JSONResponse{
|
privateKey := cfg.Matrix.PrivateKey
|
||||||
Code: http.StatusBadRequest,
|
|
||||||
JSON: spec.BadJSON("invalid create content"),
|
req := roomserverAPI.PerformCreateRoomRequest{
|
||||||
}
|
InvitedUsers: createRequest.Invite,
|
||||||
}
|
RoomName: createRequest.Name,
|
||||||
|
Visibility: createRequest.Visibility,
|
||||||
|
Topic: createRequest.Topic,
|
||||||
|
StatePreset: createRequest.Preset,
|
||||||
|
CreationContent: createRequest.CreationContent,
|
||||||
|
InitialState: createRequest.InitialState,
|
||||||
|
RoomAliasName: createRequest.RoomAliasName,
|
||||||
|
RoomVersion: roomVersion,
|
||||||
|
PowerLevelContentOverride: createRequest.PowerLevelContentOverride,
|
||||||
|
IsDirect: createRequest.IsDirect,
|
||||||
|
|
||||||
|
UserDisplayName: userDisplayName,
|
||||||
|
UserAvatarURL: userAvatarURL,
|
||||||
|
KeyID: keyID,
|
||||||
|
PrivateKey: privateKey,
|
||||||
|
EventTime: evTime,
|
||||||
}
|
}
|
||||||
createContent["creator"] = userID
|
roomAlias, createRes := rsAPI.PerformCreateRoom(ctx, *userID, *roomID, &req)
|
||||||
createContent["room_version"] = roomVersion
|
if createRes != nil {
|
||||||
powerLevelContent := eventutil.InitialPowerLevelsContent(userID)
|
return *createRes
|
||||||
joinRuleContent := gomatrixserverlib.JoinRuleContent{
|
|
||||||
JoinRule: spec.Invite,
|
|
||||||
}
|
|
||||||
historyVisibilityContent := gomatrixserverlib.HistoryVisibilityContent{
|
|
||||||
HistoryVisibility: historyVisibilityShared,
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.PowerLevelContentOverride != nil {
|
|
||||||
// Merge powerLevelContentOverride fields by unmarshalling it atop the defaults
|
|
||||||
err = json.Unmarshal(r.PowerLevelContentOverride, &powerLevelContent)
|
|
||||||
if err != nil {
|
|
||||||
util.GetLogger(ctx).WithError(err).Error("json.Unmarshal for power_level_content_override failed")
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusBadRequest,
|
|
||||||
JSON: spec.BadJSON("malformed power_level_content_override"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var guestsCanJoin bool
|
|
||||||
switch r.Preset {
|
|
||||||
case presetPrivateChat:
|
|
||||||
joinRuleContent.JoinRule = spec.Invite
|
|
||||||
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
|
|
||||||
guestsCanJoin = true
|
|
||||||
case presetTrustedPrivateChat:
|
|
||||||
joinRuleContent.JoinRule = spec.Invite
|
|
||||||
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
|
|
||||||
for _, invitee := range r.Invite {
|
|
||||||
powerLevelContent.Users[invitee] = 100
|
|
||||||
}
|
|
||||||
guestsCanJoin = true
|
|
||||||
case presetPublicChat:
|
|
||||||
joinRuleContent.JoinRule = spec.Public
|
|
||||||
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
|
|
||||||
}
|
|
||||||
|
|
||||||
createEvent := fledglingEvent{
|
|
||||||
Type: spec.MRoomCreate,
|
|
||||||
Content: createContent,
|
|
||||||
}
|
|
||||||
powerLevelEvent := fledglingEvent{
|
|
||||||
Type: spec.MRoomPowerLevels,
|
|
||||||
Content: powerLevelContent,
|
|
||||||
}
|
|
||||||
joinRuleEvent := fledglingEvent{
|
|
||||||
Type: spec.MRoomJoinRules,
|
|
||||||
Content: joinRuleContent,
|
|
||||||
}
|
|
||||||
historyVisibilityEvent := fledglingEvent{
|
|
||||||
Type: spec.MRoomHistoryVisibility,
|
|
||||||
Content: historyVisibilityContent,
|
|
||||||
}
|
|
||||||
membershipEvent := fledglingEvent{
|
|
||||||
Type: spec.MRoomMember,
|
|
||||||
StateKey: userID,
|
|
||||||
Content: gomatrixserverlib.MemberContent{
|
|
||||||
Membership: spec.Join,
|
|
||||||
DisplayName: profile.DisplayName,
|
|
||||||
AvatarURL: profile.AvatarURL,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var nameEvent *fledglingEvent
|
|
||||||
var topicEvent *fledglingEvent
|
|
||||||
var guestAccessEvent *fledglingEvent
|
|
||||||
var aliasEvent *fledglingEvent
|
|
||||||
|
|
||||||
if r.Name != "" {
|
|
||||||
nameEvent = &fledglingEvent{
|
|
||||||
Type: spec.MRoomName,
|
|
||||||
Content: eventutil.NameContent{
|
|
||||||
Name: r.Name,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Topic != "" {
|
|
||||||
topicEvent = &fledglingEvent{
|
|
||||||
Type: spec.MRoomTopic,
|
|
||||||
Content: eventutil.TopicContent{
|
|
||||||
Topic: r.Topic,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if guestsCanJoin {
|
|
||||||
guestAccessEvent = &fledglingEvent{
|
|
||||||
Type: spec.MRoomGuestAccess,
|
|
||||||
Content: eventutil.GuestAccessContent{
|
|
||||||
GuestAccess: "can_join",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var roomAlias string
|
|
||||||
if r.RoomAliasName != "" {
|
|
||||||
roomAlias = fmt.Sprintf("#%s:%s", r.RoomAliasName, userDomain)
|
|
||||||
// check it's free TODO: This races but is better than nothing
|
|
||||||
hasAliasReq := roomserverAPI.GetRoomIDForAliasRequest{
|
|
||||||
Alias: roomAlias,
|
|
||||||
IncludeAppservices: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
var aliasResp roomserverAPI.GetRoomIDForAliasResponse
|
|
||||||
err = rsAPI.GetRoomIDForAlias(ctx, &hasAliasReq, &aliasResp)
|
|
||||||
if err != nil {
|
|
||||||
util.GetLogger(ctx).WithError(err).Error("aliasAPI.GetRoomIDForAlias failed")
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusInternalServerError,
|
|
||||||
JSON: spec.InternalServerError{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if aliasResp.RoomID != "" {
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusBadRequest,
|
|
||||||
JSON: spec.RoomInUse("Room ID already exists."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
aliasEvent = &fledglingEvent{
|
|
||||||
Type: spec.MRoomCanonicalAlias,
|
|
||||||
Content: eventutil.CanonicalAlias{
|
|
||||||
Alias: roomAlias,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var initialStateEvents []fledglingEvent
|
|
||||||
for i := range r.InitialState {
|
|
||||||
if r.InitialState[i].StateKey != "" {
|
|
||||||
initialStateEvents = append(initialStateEvents, r.InitialState[i])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch r.InitialState[i].Type {
|
|
||||||
case spec.MRoomCreate:
|
|
||||||
continue
|
|
||||||
|
|
||||||
case spec.MRoomPowerLevels:
|
|
||||||
powerLevelEvent = r.InitialState[i]
|
|
||||||
|
|
||||||
case spec.MRoomJoinRules:
|
|
||||||
joinRuleEvent = r.InitialState[i]
|
|
||||||
|
|
||||||
case spec.MRoomHistoryVisibility:
|
|
||||||
historyVisibilityEvent = r.InitialState[i]
|
|
||||||
|
|
||||||
case spec.MRoomGuestAccess:
|
|
||||||
guestAccessEvent = &r.InitialState[i]
|
|
||||||
|
|
||||||
case spec.MRoomName:
|
|
||||||
nameEvent = &r.InitialState[i]
|
|
||||||
|
|
||||||
case spec.MRoomTopic:
|
|
||||||
topicEvent = &r.InitialState[i]
|
|
||||||
|
|
||||||
default:
|
|
||||||
initialStateEvents = append(initialStateEvents, r.InitialState[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// send events into the room in order of:
|
|
||||||
// 1- m.room.create
|
|
||||||
// 2- room creator join member
|
|
||||||
// 3- m.room.power_levels
|
|
||||||
// 4- m.room.join_rules
|
|
||||||
// 5- m.room.history_visibility
|
|
||||||
// 6- m.room.canonical_alias (opt)
|
|
||||||
// 7- m.room.guest_access (opt)
|
|
||||||
// 8- other initial state items
|
|
||||||
// 9- m.room.name (opt)
|
|
||||||
// 10- m.room.topic (opt)
|
|
||||||
// 11- invite events (opt) - with is_direct flag if applicable TODO
|
|
||||||
// 12- 3pid invite events (opt) TODO
|
|
||||||
// This differs from Synapse slightly. Synapse would vary the ordering of 3-7
|
|
||||||
// depending on if those events were in "initial_state" or not. This made it
|
|
||||||
// harder to reason about, hence sticking to a strict static ordering.
|
|
||||||
// TODO: Synapse has txn/token ID on each event. Do we need to do this here?
|
|
||||||
eventsToMake := []fledglingEvent{
|
|
||||||
createEvent, membershipEvent, powerLevelEvent, joinRuleEvent, historyVisibilityEvent,
|
|
||||||
}
|
|
||||||
if guestAccessEvent != nil {
|
|
||||||
eventsToMake = append(eventsToMake, *guestAccessEvent)
|
|
||||||
}
|
|
||||||
eventsToMake = append(eventsToMake, initialStateEvents...)
|
|
||||||
if nameEvent != nil {
|
|
||||||
eventsToMake = append(eventsToMake, *nameEvent)
|
|
||||||
}
|
|
||||||
if topicEvent != nil {
|
|
||||||
eventsToMake = append(eventsToMake, *topicEvent)
|
|
||||||
}
|
|
||||||
if aliasEvent != nil {
|
|
||||||
// TODO: bit of a chicken and egg problem here as the alias doesn't exist and cannot until we have made the room.
|
|
||||||
// This means we might fail creating the alias but say the canonical alias is something that doesn't exist.
|
|
||||||
eventsToMake = append(eventsToMake, *aliasEvent)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: invite events
|
|
||||||
// TODO: 3pid invite events
|
|
||||||
|
|
||||||
verImpl, err := gomatrixserverlib.GetRoomVersion(roomVersion)
|
|
||||||
if err != nil {
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusBadRequest,
|
|
||||||
JSON: spec.BadJSON("unknown room version"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var builtEvents []*types.HeaderedEvent
|
|
||||||
authEvents := gomatrixserverlib.NewAuthEvents(nil)
|
|
||||||
for i, e := range eventsToMake {
|
|
||||||
depth := i + 1 // depth starts at 1
|
|
||||||
|
|
||||||
builder := verImpl.NewEventBuilderFromProtoEvent(&gomatrixserverlib.ProtoEvent{
|
|
||||||
Sender: userID,
|
|
||||||
RoomID: roomID,
|
|
||||||
Type: e.Type,
|
|
||||||
StateKey: &e.StateKey,
|
|
||||||
Depth: int64(depth),
|
|
||||||
})
|
|
||||||
err = builder.SetContent(e.Content)
|
|
||||||
if err != nil {
|
|
||||||
util.GetLogger(ctx).WithError(err).Error("builder.SetContent failed")
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusInternalServerError,
|
|
||||||
JSON: spec.InternalServerError{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if i > 0 {
|
|
||||||
builder.PrevEvents = []string{builtEvents[i-1].EventID()}
|
|
||||||
}
|
|
||||||
var ev gomatrixserverlib.PDU
|
|
||||||
if err = builder.AddAuthEvents(&authEvents); err != nil {
|
|
||||||
util.GetLogger(ctx).WithError(err).Error("AddAuthEvents failed")
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusInternalServerError,
|
|
||||||
JSON: spec.InternalServerError{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ev, err = builder.Build(evTime, userDomain, cfg.Matrix.KeyID, cfg.Matrix.PrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
util.GetLogger(ctx).WithError(err).Error("buildEvent failed")
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusInternalServerError,
|
|
||||||
JSON: spec.InternalServerError{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = gomatrixserverlib.Allowed(ev, &authEvents); err != nil {
|
|
||||||
util.GetLogger(ctx).WithError(err).Error("gomatrixserverlib.Allowed failed")
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusInternalServerError,
|
|
||||||
JSON: spec.InternalServerError{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the event to the list of auth events
|
|
||||||
builtEvents = append(builtEvents, &types.HeaderedEvent{PDU: ev})
|
|
||||||
err = authEvents.AddEvent(ev)
|
|
||||||
if err != nil {
|
|
||||||
util.GetLogger(ctx).WithError(err).Error("authEvents.AddEvent failed")
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusInternalServerError,
|
|
||||||
JSON: spec.InternalServerError{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inputs := make([]roomserverAPI.InputRoomEvent, 0, len(builtEvents))
|
|
||||||
for _, event := range builtEvents {
|
|
||||||
inputs = append(inputs, roomserverAPI.InputRoomEvent{
|
|
||||||
Kind: roomserverAPI.KindNew,
|
|
||||||
Event: event,
|
|
||||||
Origin: userDomain,
|
|
||||||
SendAsServer: roomserverAPI.DoNotSendToOtherServers,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if err = roomserverAPI.SendInputRoomEvents(ctx, rsAPI, device.UserDomain(), inputs, false); err != nil {
|
|
||||||
util.GetLogger(ctx).WithError(err).Error("roomserverAPI.SendInputRoomEvents failed")
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusInternalServerError,
|
|
||||||
JSON: spec.InternalServerError{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(#269): Reserve room alias while we create the room. This stops us
|
|
||||||
// from creating the room but still failing due to the alias having already
|
|
||||||
// been taken.
|
|
||||||
if roomAlias != "" {
|
|
||||||
aliasReq := roomserverAPI.SetRoomAliasRequest{
|
|
||||||
Alias: roomAlias,
|
|
||||||
RoomID: roomID,
|
|
||||||
UserID: userID,
|
|
||||||
}
|
|
||||||
|
|
||||||
var aliasResp roomserverAPI.SetRoomAliasResponse
|
|
||||||
err = rsAPI.SetRoomAlias(ctx, &aliasReq, &aliasResp)
|
|
||||||
if err != nil {
|
|
||||||
util.GetLogger(ctx).WithError(err).Error("aliasAPI.SetRoomAlias failed")
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusInternalServerError,
|
|
||||||
JSON: spec.InternalServerError{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if aliasResp.AliasExists {
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusBadRequest,
|
|
||||||
JSON: spec.RoomInUse("Room alias already exists."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is a direct message then we should invite the participants.
|
|
||||||
if len(r.Invite) > 0 {
|
|
||||||
// Build some stripped state for the invite.
|
|
||||||
var globalStrippedState []fclient.InviteV2StrippedState
|
|
||||||
for _, event := range builtEvents {
|
|
||||||
// Chosen events from the spec:
|
|
||||||
// https://spec.matrix.org/v1.3/client-server-api/#stripped-state
|
|
||||||
switch event.Type() {
|
|
||||||
case spec.MRoomCreate:
|
|
||||||
fallthrough
|
|
||||||
case spec.MRoomName:
|
|
||||||
fallthrough
|
|
||||||
case spec.MRoomAvatar:
|
|
||||||
fallthrough
|
|
||||||
case spec.MRoomTopic:
|
|
||||||
fallthrough
|
|
||||||
case spec.MRoomCanonicalAlias:
|
|
||||||
fallthrough
|
|
||||||
case spec.MRoomEncryption:
|
|
||||||
fallthrough
|
|
||||||
case spec.MRoomMember:
|
|
||||||
fallthrough
|
|
||||||
case spec.MRoomJoinRules:
|
|
||||||
ev := event.PDU
|
|
||||||
globalStrippedState = append(
|
|
||||||
globalStrippedState,
|
|
||||||
fclient.NewInviteV2StrippedState(ev),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process the invites.
|
|
||||||
var inviteEvent *types.HeaderedEvent
|
|
||||||
for _, invitee := range r.Invite {
|
|
||||||
// Build the invite event.
|
|
||||||
inviteEvent, err = buildMembershipEvent(
|
|
||||||
ctx, invitee, "", profileAPI, device, spec.Invite,
|
|
||||||
roomID, r.IsDirect, cfg, evTime, rsAPI, asAPI,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
util.GetLogger(ctx).WithError(err).Error("buildMembershipEvent failed")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
inviteStrippedState := append(
|
|
||||||
globalStrippedState,
|
|
||||||
fclient.NewInviteV2StrippedState(inviteEvent.PDU),
|
|
||||||
)
|
|
||||||
// Send the invite event to the roomserver.
|
|
||||||
event := inviteEvent
|
|
||||||
err = rsAPI.PerformInvite(ctx, &roomserverAPI.PerformInviteRequest{
|
|
||||||
Event: event,
|
|
||||||
InviteRoomState: inviteStrippedState,
|
|
||||||
RoomVersion: event.Version(),
|
|
||||||
SendAsServer: string(userDomain),
|
|
||||||
})
|
|
||||||
switch e := err.(type) {
|
|
||||||
case roomserverAPI.ErrInvalidID:
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusBadRequest,
|
|
||||||
JSON: spec.Unknown(e.Error()),
|
|
||||||
}
|
|
||||||
case roomserverAPI.ErrNotAllowed:
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusForbidden,
|
|
||||||
JSON: spec.Forbidden(e.Error()),
|
|
||||||
}
|
|
||||||
case nil:
|
|
||||||
default:
|
|
||||||
util.GetLogger(ctx).WithError(err).Error("PerformInvite failed")
|
|
||||||
sentry.CaptureException(err)
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusInternalServerError,
|
|
||||||
JSON: spec.InternalServerError{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Visibility == spec.Public {
|
|
||||||
// expose this room in the published room list
|
|
||||||
if err = rsAPI.PerformPublish(ctx, &roomserverAPI.PerformPublishRequest{
|
|
||||||
RoomID: roomID,
|
|
||||||
Visibility: spec.Public,
|
|
||||||
}); err != nil {
|
|
||||||
util.GetLogger(ctx).WithError(err).Error("failed to publish room")
|
|
||||||
return util.JSONResponse{
|
|
||||||
Code: http.StatusInternalServerError,
|
|
||||||
JSON: spec.InternalServerError{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
response := createRoomResponse{
|
response := createRoomResponse{
|
||||||
RoomID: roomID,
|
RoomID: roomID.String(),
|
||||||
RoomAlias: roomAlias,
|
RoomAlias: roomAlias,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/appservice"
|
"github.com/matrix-org/dendrite/appservice"
|
||||||
"github.com/matrix-org/dendrite/roomserver"
|
"github.com/matrix-org/dendrite/roomserver"
|
||||||
|
@ -63,7 +64,7 @@ func TestJoinRoomByIDOrAlias(t *testing.T) {
|
||||||
IsDirect: true,
|
IsDirect: true,
|
||||||
Topic: "testing",
|
Topic: "testing",
|
||||||
Visibility: "public",
|
Visibility: "public",
|
||||||
Preset: presetPublicChat,
|
Preset: spec.PresetPublicChat,
|
||||||
RoomAliasName: "alias",
|
RoomAliasName: "alias",
|
||||||
Invite: []string{bob.ID},
|
Invite: []string{bob.ID},
|
||||||
}, aliceDev, &cfg.ClientAPI, userAPI, rsAPI, asAPI, time.Now())
|
}, aliceDev, &cfg.ClientAPI, userAPI, rsAPI, asAPI, time.Now())
|
||||||
|
@ -78,7 +79,7 @@ func TestJoinRoomByIDOrAlias(t *testing.T) {
|
||||||
IsDirect: true,
|
IsDirect: true,
|
||||||
Topic: "testing",
|
Topic: "testing",
|
||||||
Visibility: "public",
|
Visibility: "public",
|
||||||
Preset: presetPublicChat,
|
Preset: spec.PresetPublicChat,
|
||||||
Invite: []string{charlie.ID},
|
Invite: []string{charlie.ID},
|
||||||
}, aliceDev, &cfg.ClientAPI, userAPI, rsAPI, asAPI, time.Now())
|
}, aliceDev, &cfg.ClientAPI, userAPI, rsAPI, asAPI, time.Now())
|
||||||
crRespWithGuestAccess, ok := resp.JSON.(createRoomResponse)
|
crRespWithGuestAccess, ok := resp.JSON.(createRoomResponse)
|
||||||
|
|
|
@ -16,12 +16,14 @@ package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/ed25519"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/getsentry/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
|
|
||||||
appserviceAPI "github.com/matrix-org/dendrite/appservice/api"
|
appserviceAPI "github.com/matrix-org/dendrite/appservice/api"
|
||||||
|
@ -308,6 +310,41 @@ func sendInvite(
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildMembershipEventDirect(
|
||||||
|
ctx context.Context,
|
||||||
|
targetUserID, reason string, userDisplayName, userAvatarURL string,
|
||||||
|
sender string, senderDomain spec.ServerName,
|
||||||
|
membership, roomID string, isDirect bool,
|
||||||
|
keyID gomatrixserverlib.KeyID, privateKey ed25519.PrivateKey, evTime time.Time,
|
||||||
|
rsAPI roomserverAPI.ClientRoomserverAPI,
|
||||||
|
) (*types.HeaderedEvent, error) {
|
||||||
|
proto := gomatrixserverlib.ProtoEvent{
|
||||||
|
Sender: sender,
|
||||||
|
RoomID: roomID,
|
||||||
|
Type: "m.room.member",
|
||||||
|
StateKey: &targetUserID,
|
||||||
|
}
|
||||||
|
|
||||||
|
content := gomatrixserverlib.MemberContent{
|
||||||
|
Membership: membership,
|
||||||
|
DisplayName: userDisplayName,
|
||||||
|
AvatarURL: userAvatarURL,
|
||||||
|
Reason: reason,
|
||||||
|
IsDirect: isDirect,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := proto.SetContent(content); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
identity := &fclient.SigningIdentity{
|
||||||
|
ServerName: senderDomain,
|
||||||
|
KeyID: keyID,
|
||||||
|
PrivateKey: privateKey,
|
||||||
|
}
|
||||||
|
return eventutil.QueryAndBuildEvent(ctx, &proto, identity, evTime, rsAPI, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func buildMembershipEvent(
|
func buildMembershipEvent(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
targetUserID, reason string, profileAPI userapi.ClientUserAPI,
|
targetUserID, reason string, profileAPI userapi.ClientUserAPI,
|
||||||
|
@ -321,31 +358,13 @@ func buildMembershipEvent(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
proto := gomatrixserverlib.ProtoEvent{
|
|
||||||
Sender: device.UserID,
|
|
||||||
RoomID: roomID,
|
|
||||||
Type: "m.room.member",
|
|
||||||
StateKey: &targetUserID,
|
|
||||||
}
|
|
||||||
|
|
||||||
content := gomatrixserverlib.MemberContent{
|
|
||||||
Membership: membership,
|
|
||||||
DisplayName: profile.DisplayName,
|
|
||||||
AvatarURL: profile.AvatarURL,
|
|
||||||
Reason: reason,
|
|
||||||
IsDirect: isDirect,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = proto.SetContent(content); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
identity, err := cfg.Matrix.SigningIdentityFor(device.UserDomain())
|
identity, err := cfg.Matrix.SigningIdentityFor(device.UserDomain())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return eventutil.QueryAndBuildEvent(ctx, &proto, cfg.Matrix, identity, evTime, rsAPI, nil)
|
return buildMembershipEventDirect(ctx, targetUserID, reason, profile.DisplayName, profile.AvatarURL,
|
||||||
|
device.UserID, device.UserDomain(), membership, roomID, isDirect, identity.KeyID, identity.PrivateKey, evTime, rsAPI)
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadProfile lookups the profile of a given user from the database and returns
|
// loadProfile lookups the profile of a given user from the database and returns
|
||||||
|
|
|
@ -387,7 +387,7 @@ func buildMembershipEvents(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
event, err := eventutil.QueryAndBuildEvent(ctx, &proto, cfg.Matrix, identity, evTime, rsAPI, nil)
|
event, err := eventutil.QueryAndBuildEvent(ctx, &proto, identity, evTime, rsAPI, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,7 +137,7 @@ func SendRedaction(
|
||||||
}
|
}
|
||||||
|
|
||||||
var queryRes roomserverAPI.QueryLatestEventsAndStateResponse
|
var queryRes roomserverAPI.QueryLatestEventsAndStateResponse
|
||||||
e, err := eventutil.QueryAndBuildEvent(req.Context(), &proto, cfg.Matrix, identity, time.Now(), rsAPI, &queryRes)
|
e, err := eventutil.QueryAndBuildEvent(req.Context(), &proto, identity, time.Now(), rsAPI, &queryRes)
|
||||||
if errors.Is(err, eventutil.ErrRoomNoExists{}) {
|
if errors.Is(err, eventutil.ErrRoomNoExists{}) {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusNotFound,
|
Code: http.StatusNotFound,
|
||||||
|
|
|
@ -293,7 +293,7 @@ func generateSendEvent(
|
||||||
}
|
}
|
||||||
|
|
||||||
var queryRes api.QueryLatestEventsAndStateResponse
|
var queryRes api.QueryLatestEventsAndStateResponse
|
||||||
e, err := eventutil.QueryAndBuildEvent(ctx, &proto, cfg.Matrix, identity, evTime, rsAPI, &queryRes)
|
e, err := eventutil.QueryAndBuildEvent(ctx, &proto, identity, evTime, rsAPI, &queryRes)
|
||||||
switch specificErr := err.(type) {
|
switch specificErr := err.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
case eventutil.ErrRoomNoExists:
|
case eventutil.ErrRoomNoExists:
|
||||||
|
|
|
@ -155,7 +155,7 @@ func SendServerNotice(
|
||||||
Invite: []string{r.UserID},
|
Invite: []string{r.UserID},
|
||||||
Name: cfgNotices.RoomName,
|
Name: cfgNotices.RoomName,
|
||||||
Visibility: "private",
|
Visibility: "private",
|
||||||
Preset: presetPrivateChat,
|
Preset: spec.PresetPrivateChat,
|
||||||
CreationContent: cc,
|
CreationContent: cc,
|
||||||
RoomVersion: roomVersion,
|
RoomVersion: roomVersion,
|
||||||
PowerLevelContentOverride: pl,
|
PowerLevelContentOverride: pl,
|
||||||
|
|
|
@ -380,7 +380,7 @@ func emit3PIDInviteEvent(
|
||||||
}
|
}
|
||||||
|
|
||||||
queryRes := api.QueryLatestEventsAndStateResponse{}
|
queryRes := api.QueryLatestEventsAndStateResponse{}
|
||||||
event, err := eventutil.QueryAndBuildEvent(ctx, proto, cfg.Matrix, identity, evTime, rsAPI, &queryRes)
|
event, err := eventutil.QueryAndBuildEvent(ctx, proto, identity, evTime, rsAPI, &queryRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
"github.com/matrix-org/gomatrix"
|
"github.com/matrix-org/gomatrix"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
)
|
)
|
||||||
|
|
||||||
const userPassword = "this_is_a_long_password"
|
const userPassword = "this_is_a_long_password"
|
||||||
|
@ -56,7 +57,7 @@ func runTests(baseURL string, v *semver.Version) error {
|
||||||
|
|
||||||
// create DM room, join it and exchange messages
|
// create DM room, join it and exchange messages
|
||||||
createRoomResp, err := users[0].client.CreateRoom(&gomatrix.ReqCreateRoom{
|
createRoomResp, err := users[0].client.CreateRoom(&gomatrix.ReqCreateRoom{
|
||||||
Preset: "trusted_private_chat",
|
Preset: spec.PresetTrustedPrivateChat,
|
||||||
Invite: []string{users[1].userID},
|
Invite: []string{users[1].userID},
|
||||||
IsDirect: true,
|
IsDirect: true,
|
||||||
})
|
})
|
||||||
|
@ -98,7 +99,7 @@ func runTests(baseURL string, v *semver.Version) error {
|
||||||
publicRoomID := ""
|
publicRoomID := ""
|
||||||
createRoomResp, err = users[0].client.CreateRoom(&gomatrix.ReqCreateRoom{
|
createRoomResp, err = users[0].client.CreateRoom(&gomatrix.ReqCreateRoom{
|
||||||
RoomAliasName: "global",
|
RoomAliasName: "global",
|
||||||
Preset: "public_chat",
|
Preset: spec.PresetPublicChat,
|
||||||
})
|
})
|
||||||
if err != nil { // this is okay and expected if the room already exists and the aliases clash
|
if err != nil { // this is okay and expected if the room already exists and the aliases clash
|
||||||
// try to join it
|
// try to join it
|
||||||
|
|
|
@ -121,7 +121,7 @@ func MakeJoin(
|
||||||
queryRes := api.QueryLatestEventsAndStateResponse{
|
queryRes := api.QueryLatestEventsAndStateResponse{
|
||||||
RoomVersion: roomVersion,
|
RoomVersion: roomVersion,
|
||||||
}
|
}
|
||||||
event, err := eventutil.QueryAndBuildEvent(httpReq.Context(), proto, cfg.Matrix, identity, time.Now(), rsAPI, &queryRes)
|
event, err := eventutil.QueryAndBuildEvent(httpReq.Context(), proto, identity, time.Now(), rsAPI, &queryRes)
|
||||||
switch e := err.(type) {
|
switch e := err.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
case eventutil.ErrRoomNoExists:
|
case eventutil.ErrRoomNoExists:
|
||||||
|
|
|
@ -66,7 +66,7 @@ func MakeLeave(
|
||||||
}
|
}
|
||||||
|
|
||||||
queryRes := api.QueryLatestEventsAndStateResponse{}
|
queryRes := api.QueryLatestEventsAndStateResponse{}
|
||||||
event, err := eventutil.QueryAndBuildEvent(httpReq.Context(), proto, cfg.Matrix, identity, time.Now(), rsAPI, &queryRes)
|
event, err := eventutil.QueryAndBuildEvent(httpReq.Context(), proto, identity, time.Now(), rsAPI, &queryRes)
|
||||||
switch e := err.(type) {
|
switch e := err.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
case eventutil.ErrRoomNoExists:
|
case eventutil.ErrRoomNoExists:
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -22,7 +22,7 @@ require (
|
||||||
github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e
|
github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e
|
||||||
github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91
|
github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91
|
||||||
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530
|
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20230524095531-95ba6c68efb6
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20230531143710-c681a0658246
|
||||||
github.com/matrix-org/pinecone v0.11.1-0.20230210171230-8c3b24f2649a
|
github.com/matrix-org/pinecone v0.11.1-0.20230210171230-8c3b24f2649a
|
||||||
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66
|
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66
|
||||||
github.com/mattn/go-sqlite3 v1.14.16
|
github.com/mattn/go-sqlite3 v1.14.16
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -323,8 +323,8 @@ github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91 h1:s7fexw
|
||||||
github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91/go.mod h1:e+cg2q7C7yE5QnAXgzo512tgFh1RbQLC0+jozuegKgo=
|
github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91/go.mod h1:e+cg2q7C7yE5QnAXgzo512tgFh1RbQLC0+jozuegKgo=
|
||||||
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 h1:kHKxCOLcHH8r4Fzarl4+Y3K5hjothkVW5z7T1dUM11U=
|
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 h1:kHKxCOLcHH8r4Fzarl4+Y3K5hjothkVW5z7T1dUM11U=
|
||||||
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
|
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20230524095531-95ba6c68efb6 h1:FQpdh/KGCCQJytz4GAdG6pbx3DJ1HNzdKFc/BCZ0hP0=
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20230531143710-c681a0658246 h1:1sYXx7p9BIf0R7OIV/TZg3SCvNehEQPCKNqwV1ONfwU=
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20230524095531-95ba6c68efb6/go.mod h1:H9V9N3Uqn1bBJqYJNGK1noqtgJTaCEhtTdcH/mp50uU=
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20230531143710-c681a0658246/go.mod h1:H9V9N3Uqn1bBJqYJNGK1noqtgJTaCEhtTdcH/mp50uU=
|
||||||
github.com/matrix-org/pinecone v0.11.1-0.20230210171230-8c3b24f2649a h1:awrPDf9LEFySxTLKYBMCiObelNx/cBuv/wzllvCCH3A=
|
github.com/matrix-org/pinecone v0.11.1-0.20230210171230-8c3b24f2649a h1:awrPDf9LEFySxTLKYBMCiObelNx/cBuv/wzllvCCH3A=
|
||||||
github.com/matrix-org/pinecone v0.11.1-0.20230210171230-8c3b24f2649a/go.mod h1:HchJX9oKMXaT2xYFs0Ha/6Zs06mxLU8k6F1ODnrGkeQ=
|
github.com/matrix-org/pinecone v0.11.1-0.20230210171230-8c3b24f2649a/go.mod h1:HchJX9oKMXaT2xYFs0Ha/6Zs06mxLU8k6F1ODnrGkeQ=
|
||||||
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 h1:6z4KxomXSIGWqhHcfzExgkH3Z3UkIXry4ibJS4Aqz2Y=
|
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 h1:6z4KxomXSIGWqhHcfzExgkH3Z3UkIXry4ibJS4Aqz2Y=
|
||||||
|
|
|
@ -22,7 +22,6 @@ import (
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/roomserver/api"
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/dendrite/roomserver/types"
|
"github.com/matrix-org/dendrite/roomserver/types"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
|
||||||
"github.com/matrix-org/gomatrixserverlib/fclient"
|
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
|
|
||||||
|
@ -51,7 +50,7 @@ func (e ErrRoomNoExists) Unwrap() error {
|
||||||
// Returns an error if something else went wrong
|
// Returns an error if something else went wrong
|
||||||
func QueryAndBuildEvent(
|
func QueryAndBuildEvent(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
proto *gomatrixserverlib.ProtoEvent, cfg *config.Global,
|
proto *gomatrixserverlib.ProtoEvent,
|
||||||
identity *fclient.SigningIdentity, evTime time.Time,
|
identity *fclient.SigningIdentity, evTime time.Time,
|
||||||
rsAPI api.QueryLatestEventsAndStateAPI, queryRes *api.QueryLatestEventsAndStateResponse,
|
rsAPI api.QueryLatestEventsAndStateAPI, queryRes *api.QueryLatestEventsAndStateResponse,
|
||||||
) (*types.HeaderedEvent, error) {
|
) (*types.HeaderedEvent, error) {
|
||||||
|
@ -64,14 +63,14 @@ func QueryAndBuildEvent(
|
||||||
// This can pass through a ErrRoomNoExists to the caller
|
// This can pass through a ErrRoomNoExists to the caller
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return BuildEvent(ctx, proto, cfg, identity, evTime, eventsNeeded, queryRes)
|
return BuildEvent(ctx, proto, identity, evTime, eventsNeeded, queryRes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildEvent builds a Matrix event from the builder and QueryLatestEventsAndStateResponse
|
// BuildEvent builds a Matrix event from the builder and QueryLatestEventsAndStateResponse
|
||||||
// provided.
|
// provided.
|
||||||
func BuildEvent(
|
func BuildEvent(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
proto *gomatrixserverlib.ProtoEvent, cfg *config.Global,
|
proto *gomatrixserverlib.ProtoEvent,
|
||||||
identity *fclient.SigningIdentity, evTime time.Time,
|
identity *fclient.SigningIdentity, evTime time.Time,
|
||||||
eventsNeeded *gomatrixserverlib.StateNeeded, queryRes *api.QueryLatestEventsAndStateResponse,
|
eventsNeeded *gomatrixserverlib.StateNeeded, queryRes *api.QueryLatestEventsAndStateResponse,
|
||||||
) (*types.HeaderedEvent, error) {
|
) (*types.HeaderedEvent, error) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
|
||||||
asAPI "github.com/matrix-org/dendrite/appservice/api"
|
asAPI "github.com/matrix-org/dendrite/appservice/api"
|
||||||
fsAPI "github.com/matrix-org/dendrite/federationapi/api"
|
fsAPI "github.com/matrix-org/dendrite/federationapi/api"
|
||||||
|
@ -169,6 +170,7 @@ type ClientRoomserverAPI interface {
|
||||||
GetRoomIDForAlias(ctx context.Context, req *GetRoomIDForAliasRequest, res *GetRoomIDForAliasResponse) error
|
GetRoomIDForAlias(ctx context.Context, req *GetRoomIDForAliasRequest, res *GetRoomIDForAliasResponse) error
|
||||||
GetAliasesForRoomID(ctx context.Context, req *GetAliasesForRoomIDRequest, res *GetAliasesForRoomIDResponse) error
|
GetAliasesForRoomID(ctx context.Context, req *GetAliasesForRoomIDRequest, res *GetAliasesForRoomIDResponse) error
|
||||||
|
|
||||||
|
PerformCreateRoom(ctx context.Context, userID spec.UserID, roomID spec.RoomID, createRequest *PerformCreateRoomRequest) (string, *util.JSONResponse)
|
||||||
// PerformRoomUpgrade upgrades a room to a newer version
|
// PerformRoomUpgrade upgrades a room to a newer version
|
||||||
PerformRoomUpgrade(ctx context.Context, roomID, userID string, roomVersion gomatrixserverlib.RoomVersion) (newRoomID string, err error)
|
PerformRoomUpgrade(ctx context.Context, roomID, userID string, roomVersion gomatrixserverlib.RoomVersion) (newRoomID string, err error)
|
||||||
PerformAdminEvacuateRoom(ctx context.Context, roomID string) (affected []string, err error)
|
PerformAdminEvacuateRoom(ctx context.Context, roomID string) (affected []string, err error)
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/roomserver/types"
|
"github.com/matrix-org/dendrite/roomserver/types"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/gomatrixserverlib/fclient"
|
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||||
|
@ -8,6 +12,26 @@ import (
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type PerformCreateRoomRequest struct {
|
||||||
|
InvitedUsers []string
|
||||||
|
RoomName string
|
||||||
|
Visibility string
|
||||||
|
Topic string
|
||||||
|
StatePreset string
|
||||||
|
CreationContent json.RawMessage
|
||||||
|
InitialState []gomatrixserverlib.FledglingEvent
|
||||||
|
RoomAliasName string
|
||||||
|
RoomVersion gomatrixserverlib.RoomVersion
|
||||||
|
PowerLevelContentOverride json.RawMessage
|
||||||
|
IsDirect bool
|
||||||
|
|
||||||
|
UserDisplayName string
|
||||||
|
UserAvatarURL string
|
||||||
|
KeyID gomatrixserverlib.KeyID
|
||||||
|
PrivateKey ed25519.PrivateKey
|
||||||
|
EventTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
type PerformJoinRequest struct {
|
type PerformJoinRequest struct {
|
||||||
RoomIDOrAlias string `json:"room_id_or_alias"`
|
RoomIDOrAlias string `json:"room_id_or_alias"`
|
||||||
UserID string `json:"user_id"`
|
UserID string `json:"user_id"`
|
||||||
|
|
|
@ -208,7 +208,7 @@ func (r *RoomserverInternalAPI) RemoveRoomAlias(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
newEvent, err := eventutil.BuildEvent(ctx, proto, &r.Cfg.Global, identity, time.Now(), &eventsNeeded, stateRes)
|
newEvent, err := eventutil.BuildEvent(ctx, proto, identity, time.Now(), &eventsNeeded, stateRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/getsentry/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
@ -40,6 +41,7 @@ type RoomserverInternalAPI struct {
|
||||||
*perform.Forgetter
|
*perform.Forgetter
|
||||||
*perform.Upgrader
|
*perform.Upgrader
|
||||||
*perform.Admin
|
*perform.Admin
|
||||||
|
*perform.Creator
|
||||||
ProcessContext *process.ProcessContext
|
ProcessContext *process.ProcessContext
|
||||||
DB storage.Database
|
DB storage.Database
|
||||||
Cfg *config.Dendrite
|
Cfg *config.Dendrite
|
||||||
|
@ -191,6 +193,11 @@ func (r *RoomserverInternalAPI) SetFederationAPI(fsAPI fsAPI.RoomserverFederatio
|
||||||
Queryer: r.Queryer,
|
Queryer: r.Queryer,
|
||||||
Leaver: r.Leaver,
|
Leaver: r.Leaver,
|
||||||
}
|
}
|
||||||
|
r.Creator = &perform.Creator{
|
||||||
|
DB: r.DB,
|
||||||
|
Cfg: &r.Cfg.RoomServer,
|
||||||
|
RSAPI: r,
|
||||||
|
}
|
||||||
|
|
||||||
if err := r.Inputer.Start(); err != nil {
|
if err := r.Inputer.Start(); err != nil {
|
||||||
logrus.WithError(err).Panic("failed to start roomserver input API")
|
logrus.WithError(err).Panic("failed to start roomserver input API")
|
||||||
|
@ -206,6 +213,12 @@ func (r *RoomserverInternalAPI) SetAppserviceAPI(asAPI asAPI.AppServiceInternalA
|
||||||
r.asAPI = asAPI
|
r.asAPI = asAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RoomserverInternalAPI) PerformCreateRoom(
|
||||||
|
ctx context.Context, userID spec.UserID, roomID spec.RoomID, createRequest *api.PerformCreateRoomRequest,
|
||||||
|
) (string, *util.JSONResponse) {
|
||||||
|
return r.Creator.PerformCreateRoom(ctx, userID, roomID, createRequest)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *RoomserverInternalAPI) PerformInvite(
|
func (r *RoomserverInternalAPI) PerformInvite(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *api.PerformInviteRequest,
|
req *api.PerformInviteRequest,
|
||||||
|
|
|
@ -872,7 +872,7 @@ func (r *Inputer) kickGuests(ctx context.Context, event gomatrixserverlib.PDU, r
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
event, err := eventutil.BuildEvent(ctx, fledglingEvent, r.Cfg.Matrix, r.SigningIdentity, time.Now(), &eventsNeeded, latestRes)
|
event, err := eventutil.BuildEvent(ctx, fledglingEvent, r.SigningIdentity, time.Now(), &eventsNeeded, latestRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,7 @@ func (r *Admin) PerformAdminEvacuateRoom(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
event, err = eventutil.BuildEvent(ctx, fledglingEvent, r.Cfg.Matrix, identity, time.Now(), &eventsNeeded, latestRes)
|
event, err = eventutil.BuildEvent(ctx, fledglingEvent, identity, time.Now(), &eventsNeeded, latestRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -312,7 +312,7 @@ func (r *Admin) PerformAdminDownloadState(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ev, err := eventutil.BuildEvent(ctx, proto, r.Cfg.Matrix, identity, time.Now(), &eventsNeeded, queryRes)
|
ev, err := eventutil.BuildEvent(ctx, proto, identity, time.Now(), &eventsNeeded, queryRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("eventutil.BuildEvent: %w", err)
|
return fmt.Errorf("eventutil.BuildEvent: %w", err)
|
||||||
}
|
}
|
||||||
|
|
498
roomserver/internal/perform/perform_create_room.go
Normal file
498
roomserver/internal/perform/perform_create_room.go
Normal file
|
@ -0,0 +1,498 @@
|
||||||
|
// Copyright 2023 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 perform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/getsentry/sentry-go"
|
||||||
|
"github.com/matrix-org/dendrite/internal/eventutil"
|
||||||
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
|
"github.com/matrix-org/dendrite/roomserver/storage"
|
||||||
|
"github.com/matrix-org/dendrite/roomserver/types"
|
||||||
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
historyVisibilityShared = "shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Creator struct {
|
||||||
|
DB storage.Database
|
||||||
|
Cfg *config.RoomServer
|
||||||
|
RSAPI api.RoomserverInternalAPI
|
||||||
|
}
|
||||||
|
|
||||||
|
// PerformCreateRoom handles all the steps necessary to create a new room.
|
||||||
|
// nolint: gocyclo
|
||||||
|
func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roomID spec.RoomID, createRequest *api.PerformCreateRoomRequest) (string, *util.JSONResponse) {
|
||||||
|
verImpl, err := gomatrixserverlib.GetRoomVersion(createRequest.RoomVersion)
|
||||||
|
if err != nil {
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: spec.BadJSON("unknown room version"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createContent := map[string]interface{}{}
|
||||||
|
if len(createRequest.CreationContent) > 0 {
|
||||||
|
if err = json.Unmarshal(createRequest.CreationContent, &createContent); err != nil {
|
||||||
|
util.GetLogger(ctx).WithError(err).Error("json.Unmarshal for creation_content failed")
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: spec.BadJSON("invalid create content"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
createContent["creator"] = userID.String()
|
||||||
|
createContent["room_version"] = createRequest.RoomVersion
|
||||||
|
powerLevelContent := eventutil.InitialPowerLevelsContent(userID.String())
|
||||||
|
joinRuleContent := gomatrixserverlib.JoinRuleContent{
|
||||||
|
JoinRule: spec.Invite,
|
||||||
|
}
|
||||||
|
historyVisibilityContent := gomatrixserverlib.HistoryVisibilityContent{
|
||||||
|
HistoryVisibility: historyVisibilityShared,
|
||||||
|
}
|
||||||
|
|
||||||
|
if createRequest.PowerLevelContentOverride != nil {
|
||||||
|
// Merge powerLevelContentOverride fields by unmarshalling it atop the defaults
|
||||||
|
err = json.Unmarshal(createRequest.PowerLevelContentOverride, &powerLevelContent)
|
||||||
|
if err != nil {
|
||||||
|
util.GetLogger(ctx).WithError(err).Error("json.Unmarshal for power_level_content_override failed")
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: spec.BadJSON("malformed power_level_content_override"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var guestsCanJoin bool
|
||||||
|
switch createRequest.StatePreset {
|
||||||
|
case spec.PresetPrivateChat:
|
||||||
|
joinRuleContent.JoinRule = spec.Invite
|
||||||
|
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
|
||||||
|
guestsCanJoin = true
|
||||||
|
case spec.PresetTrustedPrivateChat:
|
||||||
|
joinRuleContent.JoinRule = spec.Invite
|
||||||
|
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
|
||||||
|
for _, invitee := range createRequest.InvitedUsers {
|
||||||
|
powerLevelContent.Users[invitee] = 100
|
||||||
|
}
|
||||||
|
guestsCanJoin = true
|
||||||
|
case spec.PresetPublicChat:
|
||||||
|
joinRuleContent.JoinRule = spec.Public
|
||||||
|
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
|
||||||
|
}
|
||||||
|
|
||||||
|
createEvent := gomatrixserverlib.FledglingEvent{
|
||||||
|
Type: spec.MRoomCreate,
|
||||||
|
Content: createContent,
|
||||||
|
}
|
||||||
|
powerLevelEvent := gomatrixserverlib.FledglingEvent{
|
||||||
|
Type: spec.MRoomPowerLevels,
|
||||||
|
Content: powerLevelContent,
|
||||||
|
}
|
||||||
|
joinRuleEvent := gomatrixserverlib.FledglingEvent{
|
||||||
|
Type: spec.MRoomJoinRules,
|
||||||
|
Content: joinRuleContent,
|
||||||
|
}
|
||||||
|
historyVisibilityEvent := gomatrixserverlib.FledglingEvent{
|
||||||
|
Type: spec.MRoomHistoryVisibility,
|
||||||
|
Content: historyVisibilityContent,
|
||||||
|
}
|
||||||
|
membershipEvent := gomatrixserverlib.FledglingEvent{
|
||||||
|
Type: spec.MRoomMember,
|
||||||
|
StateKey: userID.String(),
|
||||||
|
Content: gomatrixserverlib.MemberContent{
|
||||||
|
Membership: spec.Join,
|
||||||
|
DisplayName: createRequest.UserDisplayName,
|
||||||
|
AvatarURL: createRequest.UserAvatarURL,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var nameEvent *gomatrixserverlib.FledglingEvent
|
||||||
|
var topicEvent *gomatrixserverlib.FledglingEvent
|
||||||
|
var guestAccessEvent *gomatrixserverlib.FledglingEvent
|
||||||
|
var aliasEvent *gomatrixserverlib.FledglingEvent
|
||||||
|
|
||||||
|
if createRequest.RoomName != "" {
|
||||||
|
nameEvent = &gomatrixserverlib.FledglingEvent{
|
||||||
|
Type: spec.MRoomName,
|
||||||
|
Content: eventutil.NameContent{
|
||||||
|
Name: createRequest.RoomName,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if createRequest.Topic != "" {
|
||||||
|
topicEvent = &gomatrixserverlib.FledglingEvent{
|
||||||
|
Type: spec.MRoomTopic,
|
||||||
|
Content: eventutil.TopicContent{
|
||||||
|
Topic: createRequest.Topic,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if guestsCanJoin {
|
||||||
|
guestAccessEvent = &gomatrixserverlib.FledglingEvent{
|
||||||
|
Type: spec.MRoomGuestAccess,
|
||||||
|
Content: eventutil.GuestAccessContent{
|
||||||
|
GuestAccess: "can_join",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var roomAlias string
|
||||||
|
if createRequest.RoomAliasName != "" {
|
||||||
|
roomAlias = fmt.Sprintf("#%s:%s", createRequest.RoomAliasName, userID.Domain())
|
||||||
|
// check it's free
|
||||||
|
// TODO: This races but is better than nothing
|
||||||
|
hasAliasReq := api.GetRoomIDForAliasRequest{
|
||||||
|
Alias: roomAlias,
|
||||||
|
IncludeAppservices: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
var aliasResp api.GetRoomIDForAliasResponse
|
||||||
|
err = c.RSAPI.GetRoomIDForAlias(ctx, &hasAliasReq, &aliasResp)
|
||||||
|
if err != nil {
|
||||||
|
util.GetLogger(ctx).WithError(err).Error("aliasAPI.GetRoomIDForAlias failed")
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if aliasResp.RoomID != "" {
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: spec.RoomInUse("Room ID already exists."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aliasEvent = &gomatrixserverlib.FledglingEvent{
|
||||||
|
Type: spec.MRoomCanonicalAlias,
|
||||||
|
Content: eventutil.CanonicalAlias{
|
||||||
|
Alias: roomAlias,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var initialStateEvents []gomatrixserverlib.FledglingEvent
|
||||||
|
for i := range createRequest.InitialState {
|
||||||
|
if createRequest.InitialState[i].StateKey != "" {
|
||||||
|
initialStateEvents = append(initialStateEvents, createRequest.InitialState[i])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch createRequest.InitialState[i].Type {
|
||||||
|
case spec.MRoomCreate:
|
||||||
|
continue
|
||||||
|
|
||||||
|
case spec.MRoomPowerLevels:
|
||||||
|
powerLevelEvent = createRequest.InitialState[i]
|
||||||
|
|
||||||
|
case spec.MRoomJoinRules:
|
||||||
|
joinRuleEvent = createRequest.InitialState[i]
|
||||||
|
|
||||||
|
case spec.MRoomHistoryVisibility:
|
||||||
|
historyVisibilityEvent = createRequest.InitialState[i]
|
||||||
|
|
||||||
|
case spec.MRoomGuestAccess:
|
||||||
|
guestAccessEvent = &createRequest.InitialState[i]
|
||||||
|
|
||||||
|
case spec.MRoomName:
|
||||||
|
nameEvent = &createRequest.InitialState[i]
|
||||||
|
|
||||||
|
case spec.MRoomTopic:
|
||||||
|
topicEvent = &createRequest.InitialState[i]
|
||||||
|
|
||||||
|
default:
|
||||||
|
initialStateEvents = append(initialStateEvents, createRequest.InitialState[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send events into the room in order of:
|
||||||
|
// 1- m.room.create
|
||||||
|
// 2- room creator join member
|
||||||
|
// 3- m.room.power_levels
|
||||||
|
// 4- m.room.join_rules
|
||||||
|
// 5- m.room.history_visibility
|
||||||
|
// 6- m.room.canonical_alias (opt)
|
||||||
|
// 7- m.room.guest_access (opt)
|
||||||
|
// 8- other initial state items
|
||||||
|
// 9- m.room.name (opt)
|
||||||
|
// 10- m.room.topic (opt)
|
||||||
|
// 11- invite events (opt) - with is_direct flag if applicable TODO
|
||||||
|
// 12- 3pid invite events (opt) TODO
|
||||||
|
// This differs from Synapse slightly. Synapse would vary the ordering of 3-7
|
||||||
|
// depending on if those events were in "initial_state" or not. This made it
|
||||||
|
// harder to reason about, hence sticking to a strict static ordering.
|
||||||
|
// TODO: Synapse has txn/token ID on each event. Do we need to do this here?
|
||||||
|
eventsToMake := []gomatrixserverlib.FledglingEvent{
|
||||||
|
createEvent, membershipEvent, powerLevelEvent, joinRuleEvent, historyVisibilityEvent,
|
||||||
|
}
|
||||||
|
if guestAccessEvent != nil {
|
||||||
|
eventsToMake = append(eventsToMake, *guestAccessEvent)
|
||||||
|
}
|
||||||
|
eventsToMake = append(eventsToMake, initialStateEvents...)
|
||||||
|
if nameEvent != nil {
|
||||||
|
eventsToMake = append(eventsToMake, *nameEvent)
|
||||||
|
}
|
||||||
|
if topicEvent != nil {
|
||||||
|
eventsToMake = append(eventsToMake, *topicEvent)
|
||||||
|
}
|
||||||
|
if aliasEvent != nil {
|
||||||
|
// TODO: bit of a chicken and egg problem here as the alias doesn't exist and cannot until we have made the room.
|
||||||
|
// This means we might fail creating the alias but say the canonical alias is something that doesn't exist.
|
||||||
|
eventsToMake = append(eventsToMake, *aliasEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: invite events
|
||||||
|
// TODO: 3pid invite events
|
||||||
|
|
||||||
|
var builtEvents []*types.HeaderedEvent
|
||||||
|
authEvents := gomatrixserverlib.NewAuthEvents(nil)
|
||||||
|
for i, e := range eventsToMake {
|
||||||
|
depth := i + 1 // depth starts at 1
|
||||||
|
|
||||||
|
builder := verImpl.NewEventBuilderFromProtoEvent(&gomatrixserverlib.ProtoEvent{
|
||||||
|
Sender: userID.String(),
|
||||||
|
RoomID: roomID.String(),
|
||||||
|
Type: e.Type,
|
||||||
|
StateKey: &e.StateKey,
|
||||||
|
Depth: int64(depth),
|
||||||
|
})
|
||||||
|
err = builder.SetContent(e.Content)
|
||||||
|
if err != nil {
|
||||||
|
util.GetLogger(ctx).WithError(err).Error("builder.SetContent failed")
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
builder.PrevEvents = []string{builtEvents[i-1].EventID()}
|
||||||
|
}
|
||||||
|
var ev gomatrixserverlib.PDU
|
||||||
|
if err = builder.AddAuthEvents(&authEvents); err != nil {
|
||||||
|
util.GetLogger(ctx).WithError(err).Error("AddAuthEvents failed")
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ev, err = builder.Build(createRequest.EventTime, userID.Domain(), createRequest.KeyID, createRequest.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
util.GetLogger(ctx).WithError(err).Error("buildEvent failed")
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = gomatrixserverlib.Allowed(ev, &authEvents); err != nil {
|
||||||
|
util.GetLogger(ctx).WithError(err).Error("gomatrixserverlib.Allowed failed")
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the event to the list of auth events
|
||||||
|
builtEvents = append(builtEvents, &types.HeaderedEvent{PDU: ev})
|
||||||
|
err = authEvents.AddEvent(ev)
|
||||||
|
if err != nil {
|
||||||
|
util.GetLogger(ctx).WithError(err).Error("authEvents.AddEvent failed")
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs := make([]api.InputRoomEvent, 0, len(builtEvents))
|
||||||
|
for _, event := range builtEvents {
|
||||||
|
inputs = append(inputs, api.InputRoomEvent{
|
||||||
|
Kind: api.KindNew,
|
||||||
|
Event: event,
|
||||||
|
Origin: userID.Domain(),
|
||||||
|
SendAsServer: api.DoNotSendToOtherServers,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err = api.SendInputRoomEvents(ctx, c.RSAPI, userID.Domain(), inputs, false); err != nil {
|
||||||
|
util.GetLogger(ctx).WithError(err).Error("roomserverAPI.SendInputRoomEvents failed")
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(#269): Reserve room alias while we create the room. This stops us
|
||||||
|
// from creating the room but still failing due to the alias having already
|
||||||
|
// been taken.
|
||||||
|
if roomAlias != "" {
|
||||||
|
aliasReq := api.SetRoomAliasRequest{
|
||||||
|
Alias: roomAlias,
|
||||||
|
RoomID: roomID.String(),
|
||||||
|
UserID: userID.String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
var aliasResp api.SetRoomAliasResponse
|
||||||
|
err = c.RSAPI.SetRoomAlias(ctx, &aliasReq, &aliasResp)
|
||||||
|
if err != nil {
|
||||||
|
util.GetLogger(ctx).WithError(err).Error("aliasAPI.SetRoomAlias failed")
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if aliasResp.AliasExists {
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: spec.RoomInUse("Room alias already exists."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a direct message then we should invite the participants.
|
||||||
|
if len(createRequest.InvitedUsers) > 0 {
|
||||||
|
// Build some stripped state for the invite.
|
||||||
|
var globalStrippedState []fclient.InviteV2StrippedState
|
||||||
|
for _, event := range builtEvents {
|
||||||
|
// Chosen events from the spec:
|
||||||
|
// https://spec.matrix.org/v1.3/client-server-api/#stripped-state
|
||||||
|
switch event.Type() {
|
||||||
|
case spec.MRoomCreate:
|
||||||
|
fallthrough
|
||||||
|
case spec.MRoomName:
|
||||||
|
fallthrough
|
||||||
|
case spec.MRoomAvatar:
|
||||||
|
fallthrough
|
||||||
|
case spec.MRoomTopic:
|
||||||
|
fallthrough
|
||||||
|
case spec.MRoomCanonicalAlias:
|
||||||
|
fallthrough
|
||||||
|
case spec.MRoomEncryption:
|
||||||
|
fallthrough
|
||||||
|
case spec.MRoomMember:
|
||||||
|
fallthrough
|
||||||
|
case spec.MRoomJoinRules:
|
||||||
|
ev := event.PDU
|
||||||
|
globalStrippedState = append(
|
||||||
|
globalStrippedState,
|
||||||
|
fclient.NewInviteV2StrippedState(ev),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the invites.
|
||||||
|
var inviteEvent *types.HeaderedEvent
|
||||||
|
for _, invitee := range createRequest.InvitedUsers {
|
||||||
|
proto := gomatrixserverlib.ProtoEvent{
|
||||||
|
Sender: userID.String(),
|
||||||
|
RoomID: roomID.String(),
|
||||||
|
Type: "m.room.member",
|
||||||
|
StateKey: &invitee,
|
||||||
|
}
|
||||||
|
|
||||||
|
content := gomatrixserverlib.MemberContent{
|
||||||
|
Membership: spec.Invite,
|
||||||
|
DisplayName: createRequest.UserDisplayName,
|
||||||
|
AvatarURL: createRequest.UserAvatarURL,
|
||||||
|
Reason: "",
|
||||||
|
IsDirect: createRequest.IsDirect,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = proto.SetContent(content); err != nil {
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the invite event.
|
||||||
|
identity := &fclient.SigningIdentity{
|
||||||
|
ServerName: userID.Domain(),
|
||||||
|
KeyID: createRequest.KeyID,
|
||||||
|
PrivateKey: createRequest.PrivateKey,
|
||||||
|
}
|
||||||
|
inviteEvent, err = eventutil.QueryAndBuildEvent(ctx, &proto, identity, createRequest.EventTime, c.RSAPI, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
util.GetLogger(ctx).WithError(err).Error("buildMembershipEvent failed")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
inviteStrippedState := append(
|
||||||
|
globalStrippedState,
|
||||||
|
fclient.NewInviteV2StrippedState(inviteEvent.PDU),
|
||||||
|
)
|
||||||
|
// Send the invite event to the roomserver.
|
||||||
|
event := inviteEvent
|
||||||
|
err = c.RSAPI.PerformInvite(ctx, &api.PerformInviteRequest{
|
||||||
|
Event: event,
|
||||||
|
InviteRoomState: inviteStrippedState,
|
||||||
|
RoomVersion: event.Version(),
|
||||||
|
SendAsServer: string(userID.Domain()),
|
||||||
|
})
|
||||||
|
switch e := err.(type) {
|
||||||
|
case api.ErrInvalidID:
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: spec.Unknown(e.Error()),
|
||||||
|
}
|
||||||
|
case api.ErrNotAllowed:
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusForbidden,
|
||||||
|
JSON: spec.Forbidden(e.Error()),
|
||||||
|
}
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
util.GetLogger(ctx).WithError(err).Error("PerformInvite failed")
|
||||||
|
sentry.CaptureException(err)
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if createRequest.Visibility == spec.Public {
|
||||||
|
// expose this room in the published room list
|
||||||
|
if err = c.RSAPI.PerformPublish(ctx, &api.PerformPublishRequest{
|
||||||
|
RoomID: roomID.String(),
|
||||||
|
Visibility: spec.Public,
|
||||||
|
}); err != nil {
|
||||||
|
util.GetLogger(ctx).WithError(err).Error("failed to publish room")
|
||||||
|
return "", &util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: visibility/presets/raw initial state
|
||||||
|
// TODO: Create room alias association
|
||||||
|
// Make sure this doesn't fall into an application service's namespace though!
|
||||||
|
|
||||||
|
return roomAlias, nil
|
||||||
|
}
|
|
@ -284,7 +284,7 @@ func (r *Joiner) performJoinRoomByID(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", fmt.Errorf("error joining local room: %q", err)
|
return "", "", fmt.Errorf("error joining local room: %q", err)
|
||||||
}
|
}
|
||||||
event, err := eventutil.QueryAndBuildEvent(ctx, &proto, r.Cfg.Matrix, identity, time.Now(), r.RSAPI, &buildRes)
|
event, err := eventutil.QueryAndBuildEvent(ctx, &proto, identity, time.Now(), r.RSAPI, &buildRes)
|
||||||
|
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
|
|
|
@ -183,7 +183,7 @@ func (r *Leaver) performLeaveRoomByID(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("SigningIdentityFor: %w", err)
|
return nil, fmt.Errorf("SigningIdentityFor: %w", err)
|
||||||
}
|
}
|
||||||
event, err := eventutil.QueryAndBuildEvent(ctx, &proto, r.Cfg.Matrix, identity, time.Now(), r.RSAPI, &buildRes)
|
event, err := eventutil.QueryAndBuildEvent(ctx, &proto, identity, time.Now(), r.RSAPI, &buildRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("eventutil.QueryAndBuildEvent: %w", err)
|
return nil, fmt.Errorf("eventutil.QueryAndBuildEvent: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,13 +35,6 @@ type Upgrader struct {
|
||||||
URSAPI api.RoomserverInternalAPI
|
URSAPI api.RoomserverInternalAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
// fledglingEvent is a helper representation of an event used when creating many events in succession.
|
|
||||||
type fledglingEvent struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
StateKey string `json:"state_key"`
|
|
||||||
Content interface{} `json:"content"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PerformRoomUpgrade upgrades a room from one version to another
|
// PerformRoomUpgrade upgrades a room from one version to another
|
||||||
func (r *Upgrader) PerformRoomUpgrade(
|
func (r *Upgrader) PerformRoomUpgrade(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
@ -154,7 +147,7 @@ func (r *Upgrader) restrictOldRoomPowerLevels(ctx context.Context, evTime time.T
|
||||||
restrictedPowerLevelContent.EventsDefault = restrictedDefaultPowerLevel
|
restrictedPowerLevelContent.EventsDefault = restrictedDefaultPowerLevel
|
||||||
restrictedPowerLevelContent.Invite = restrictedDefaultPowerLevel
|
restrictedPowerLevelContent.Invite = restrictedDefaultPowerLevel
|
||||||
|
|
||||||
restrictedPowerLevelsHeadered, resErr := r.makeHeaderedEvent(ctx, evTime, userID, roomID, fledglingEvent{
|
restrictedPowerLevelsHeadered, resErr := r.makeHeaderedEvent(ctx, evTime, userID, roomID, gomatrixserverlib.FledglingEvent{
|
||||||
Type: spec.MRoomPowerLevels,
|
Type: spec.MRoomPowerLevels,
|
||||||
StateKey: "",
|
StateKey: "",
|
||||||
Content: restrictedPowerLevelContent,
|
Content: restrictedPowerLevelContent,
|
||||||
|
@ -216,7 +209,7 @@ func (r *Upgrader) clearOldCanonicalAliasEvent(ctx context.Context, oldRoom *api
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emptyCanonicalAliasEvent, resErr := r.makeHeaderedEvent(ctx, evTime, userID, roomID, fledglingEvent{
|
emptyCanonicalAliasEvent, resErr := r.makeHeaderedEvent(ctx, evTime, userID, roomID, gomatrixserverlib.FledglingEvent{
|
||||||
Type: spec.MRoomCanonicalAlias,
|
Type: spec.MRoomCanonicalAlias,
|
||||||
Content: map[string]interface{}{},
|
Content: map[string]interface{}{},
|
||||||
})
|
})
|
||||||
|
@ -298,7 +291,7 @@ func (r *Upgrader) userIsAuthorized(ctx context.Context, userID, roomID string,
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:gocyclo
|
// nolint:gocyclo
|
||||||
func (r *Upgrader) generateInitialEvents(ctx context.Context, oldRoom *api.QueryLatestEventsAndStateResponse, userID, roomID string, newVersion gomatrixserverlib.RoomVersion, tombstoneEvent *types.HeaderedEvent) ([]fledglingEvent, error) {
|
func (r *Upgrader) generateInitialEvents(ctx context.Context, oldRoom *api.QueryLatestEventsAndStateResponse, userID, roomID string, newVersion gomatrixserverlib.RoomVersion, tombstoneEvent *types.HeaderedEvent) ([]gomatrixserverlib.FledglingEvent, error) {
|
||||||
state := make(map[gomatrixserverlib.StateKeyTuple]*types.HeaderedEvent, len(oldRoom.StateEvents))
|
state := make(map[gomatrixserverlib.StateKeyTuple]*types.HeaderedEvent, len(oldRoom.StateEvents))
|
||||||
for _, event := range oldRoom.StateEvents {
|
for _, event := range oldRoom.StateEvents {
|
||||||
if event.StateKey() == nil {
|
if event.StateKey() == nil {
|
||||||
|
@ -361,7 +354,7 @@ func (r *Upgrader) generateInitialEvents(ctx context.Context, oldRoom *api.Query
|
||||||
EventID: tombstoneEvent.EventID(),
|
EventID: tombstoneEvent.EventID(),
|
||||||
RoomID: roomID,
|
RoomID: roomID,
|
||||||
}
|
}
|
||||||
newCreateEvent := fledglingEvent{
|
newCreateEvent := gomatrixserverlib.FledglingEvent{
|
||||||
Type: spec.MRoomCreate,
|
Type: spec.MRoomCreate,
|
||||||
StateKey: "",
|
StateKey: "",
|
||||||
Content: newCreateContent,
|
Content: newCreateContent,
|
||||||
|
@ -374,7 +367,7 @@ func (r *Upgrader) generateInitialEvents(ctx context.Context, oldRoom *api.Query
|
||||||
newMembershipContent := map[string]interface{}{}
|
newMembershipContent := map[string]interface{}{}
|
||||||
_ = json.Unmarshal(oldMembershipEvent.Content(), &newMembershipContent)
|
_ = json.Unmarshal(oldMembershipEvent.Content(), &newMembershipContent)
|
||||||
newMembershipContent["membership"] = spec.Join
|
newMembershipContent["membership"] = spec.Join
|
||||||
newMembershipEvent := fledglingEvent{
|
newMembershipEvent := gomatrixserverlib.FledglingEvent{
|
||||||
Type: spec.MRoomMember,
|
Type: spec.MRoomMember,
|
||||||
StateKey: userID,
|
StateKey: userID,
|
||||||
Content: newMembershipContent,
|
Content: newMembershipContent,
|
||||||
|
@ -400,13 +393,13 @@ func (r *Upgrader) generateInitialEvents(ctx context.Context, oldRoom *api.Query
|
||||||
"join_rule": spec.Invite, // sane default
|
"join_rule": spec.Invite, // sane default
|
||||||
}
|
}
|
||||||
_ = json.Unmarshal(oldJoinRulesEvent.Content(), &newJoinRulesContent)
|
_ = json.Unmarshal(oldJoinRulesEvent.Content(), &newJoinRulesContent)
|
||||||
newJoinRulesEvent := fledglingEvent{
|
newJoinRulesEvent := gomatrixserverlib.FledglingEvent{
|
||||||
Type: spec.MRoomJoinRules,
|
Type: spec.MRoomJoinRules,
|
||||||
StateKey: "",
|
StateKey: "",
|
||||||
Content: newJoinRulesContent,
|
Content: newJoinRulesContent,
|
||||||
}
|
}
|
||||||
|
|
||||||
eventsToMake := make([]fledglingEvent, 0, len(state))
|
eventsToMake := make([]gomatrixserverlib.FledglingEvent, 0, len(state))
|
||||||
eventsToMake = append(
|
eventsToMake = append(
|
||||||
eventsToMake, newCreateEvent, newMembershipEvent,
|
eventsToMake, newCreateEvent, newMembershipEvent,
|
||||||
tempPowerLevelsEvent, newJoinRulesEvent,
|
tempPowerLevelsEvent, newJoinRulesEvent,
|
||||||
|
@ -415,7 +408,7 @@ func (r *Upgrader) generateInitialEvents(ctx context.Context, oldRoom *api.Query
|
||||||
// For some reason Sytest expects there to be a guest access event.
|
// For some reason Sytest expects there to be a guest access event.
|
||||||
// Create one if it doesn't exist.
|
// Create one if it doesn't exist.
|
||||||
if _, ok := state[gomatrixserverlib.StateKeyTuple{EventType: spec.MRoomGuestAccess, StateKey: ""}]; !ok {
|
if _, ok := state[gomatrixserverlib.StateKeyTuple{EventType: spec.MRoomGuestAccess, StateKey: ""}]; !ok {
|
||||||
eventsToMake = append(eventsToMake, fledglingEvent{
|
eventsToMake = append(eventsToMake, gomatrixserverlib.FledglingEvent{
|
||||||
Type: spec.MRoomGuestAccess,
|
Type: spec.MRoomGuestAccess,
|
||||||
Content: map[string]string{
|
Content: map[string]string{
|
||||||
"guest_access": "forbidden",
|
"guest_access": "forbidden",
|
||||||
|
@ -430,7 +423,7 @@ func (r *Upgrader) generateInitialEvents(ctx context.Context, oldRoom *api.Query
|
||||||
// are already in `eventsToMake`.
|
// are already in `eventsToMake`.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
newEvent := fledglingEvent{
|
newEvent := gomatrixserverlib.FledglingEvent{
|
||||||
Type: tuple.EventType,
|
Type: tuple.EventType,
|
||||||
StateKey: tuple.StateKey,
|
StateKey: tuple.StateKey,
|
||||||
}
|
}
|
||||||
|
@ -444,7 +437,7 @@ func (r *Upgrader) generateInitialEvents(ctx context.Context, oldRoom *api.Query
|
||||||
// If we sent a temporary power level event into the room before,
|
// If we sent a temporary power level event into the room before,
|
||||||
// override that now by restoring the original power levels.
|
// override that now by restoring the original power levels.
|
||||||
if powerLevelsOverridden {
|
if powerLevelsOverridden {
|
||||||
eventsToMake = append(eventsToMake, fledglingEvent{
|
eventsToMake = append(eventsToMake, gomatrixserverlib.FledglingEvent{
|
||||||
Type: spec.MRoomPowerLevels,
|
Type: spec.MRoomPowerLevels,
|
||||||
Content: powerLevelContent,
|
Content: powerLevelContent,
|
||||||
})
|
})
|
||||||
|
@ -452,7 +445,7 @@ func (r *Upgrader) generateInitialEvents(ctx context.Context, oldRoom *api.Query
|
||||||
return eventsToMake, nil
|
return eventsToMake, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Upgrader) sendInitialEvents(ctx context.Context, evTime time.Time, userID string, userDomain spec.ServerName, newRoomID string, newVersion gomatrixserverlib.RoomVersion, eventsToMake []fledglingEvent) error {
|
func (r *Upgrader) sendInitialEvents(ctx context.Context, evTime time.Time, userID string, userDomain spec.ServerName, newRoomID string, newVersion gomatrixserverlib.RoomVersion, eventsToMake []gomatrixserverlib.FledglingEvent) error {
|
||||||
var err error
|
var err error
|
||||||
var builtEvents []*types.HeaderedEvent
|
var builtEvents []*types.HeaderedEvent
|
||||||
authEvents := gomatrixserverlib.NewAuthEvents(nil)
|
authEvents := gomatrixserverlib.NewAuthEvents(nil)
|
||||||
|
@ -527,14 +520,14 @@ func (r *Upgrader) makeTombstoneEvent(
|
||||||
"body": "This room has been replaced",
|
"body": "This room has been replaced",
|
||||||
"replacement_room": newRoomID,
|
"replacement_room": newRoomID,
|
||||||
}
|
}
|
||||||
event := fledglingEvent{
|
event := gomatrixserverlib.FledglingEvent{
|
||||||
Type: "m.room.tombstone",
|
Type: "m.room.tombstone",
|
||||||
Content: content,
|
Content: content,
|
||||||
}
|
}
|
||||||
return r.makeHeaderedEvent(ctx, evTime, userID, roomID, event)
|
return r.makeHeaderedEvent(ctx, evTime, userID, roomID, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Upgrader) makeHeaderedEvent(ctx context.Context, evTime time.Time, userID, roomID string, event fledglingEvent) (*types.HeaderedEvent, error) {
|
func (r *Upgrader) makeHeaderedEvent(ctx context.Context, evTime time.Time, userID, roomID string, event gomatrixserverlib.FledglingEvent) (*types.HeaderedEvent, error) {
|
||||||
proto := gomatrixserverlib.ProtoEvent{
|
proto := gomatrixserverlib.ProtoEvent{
|
||||||
Sender: userID,
|
Sender: userID,
|
||||||
RoomID: roomID,
|
RoomID: roomID,
|
||||||
|
@ -555,7 +548,7 @@ func (r *Upgrader) makeHeaderedEvent(ctx context.Context, evTime time.Time, user
|
||||||
return nil, fmt.Errorf("failed to get signing identity for %q: %w", senderDomain, err)
|
return nil, fmt.Errorf("failed to get signing identity for %q: %w", senderDomain, err)
|
||||||
}
|
}
|
||||||
var queryRes api.QueryLatestEventsAndStateResponse
|
var queryRes api.QueryLatestEventsAndStateResponse
|
||||||
headeredEvent, err := eventutil.QueryAndBuildEvent(ctx, &proto, r.Cfg.Matrix, identity, evTime, r.URSAPI, &queryRes)
|
headeredEvent, err := eventutil.QueryAndBuildEvent(ctx, &proto, identity, evTime, r.URSAPI, &queryRes)
|
||||||
switch e := err.(type) {
|
switch e := err.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
case eventutil.ErrRoomNoExists:
|
case eventutil.ErrRoomNoExists:
|
||||||
|
@ -581,7 +574,7 @@ func (r *Upgrader) makeHeaderedEvent(ctx context.Context, evTime time.Time, user
|
||||||
return headeredEvent, nil
|
return headeredEvent, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTemporaryPowerLevels(powerLevelContent *gomatrixserverlib.PowerLevelContent, userID string) (fledglingEvent, bool) {
|
func createTemporaryPowerLevels(powerLevelContent *gomatrixserverlib.PowerLevelContent, userID string) (gomatrixserverlib.FledglingEvent, bool) {
|
||||||
// Work out what power level we need in order to be able to send events
|
// Work out what power level we need in order to be able to send events
|
||||||
// of all types into the room.
|
// of all types into the room.
|
||||||
neededPowerLevel := powerLevelContent.StateDefault
|
neededPowerLevel := powerLevelContent.StateDefault
|
||||||
|
@ -612,7 +605,7 @@ func createTemporaryPowerLevels(powerLevelContent *gomatrixserverlib.PowerLevelC
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then return the temporary power levels event.
|
// Then return the temporary power levels event.
|
||||||
return fledglingEvent{
|
return gomatrixserverlib.FledglingEvent{
|
||||||
Type: spec.MRoomPowerLevels,
|
Type: spec.MRoomPowerLevels,
|
||||||
Content: tempPowerLevelContent,
|
Content: tempPowerLevelContent,
|
||||||
}, powerLevelsOverridden
|
}, powerLevelsOverridden
|
||||||
|
|
Loading…
Reference in a new issue