diff --git a/clientapi/routing/createroom.go b/clientapi/routing/createroom.go index 2d8867466..977f7812d 100644 --- a/clientapi/routing/createroom.go +++ b/clientapi/routing/createroom.go @@ -43,7 +43,7 @@ type createRoomRequest struct { Visibility string `json:"visibility"` Topic string `json:"topic"` Preset string `json:"preset"` - CreationContent map[string]interface{} `json:"creation_content"` + CreationContent json.RawMessage `json:"creation_content"` InitialState []fledglingEvent `json:"initial_state"` RoomAliasName string `json:"room_alias_name"` GuestCanJoin bool `json:"guest_can_join"` @@ -177,11 +177,6 @@ func createRoom( // Clobber keys: creator, room_version - if r.CreationContent == nil { - r.CreationContent = make(map[string]interface{}, 2) - } - - r.CreationContent["creator"] = userID roomVersion := roomserverVersion.DefaultRoomVersion() if r.RoomVersion != "" { candidateVersion := gomatrixserverlib.RoomVersion(r.RoomVersion) @@ -194,7 +189,6 @@ func createRoom( } roomVersion = candidateVersion } - r.CreationContent["room_version"] = roomVersion // TODO: visibility/presets/raw initial state // TODO: Create room alias association @@ -203,7 +197,7 @@ func createRoom( logger.WithFields(log.Fields{ "userID": userID, "roomID": roomID, - "roomVersion": r.CreationContent["room_version"], + "roomVersion": roomVersion, }).Info("Creating new room") profile, err := appserviceAPI.RetrieveUserProfile(req.Context(), userID, asAPI, accountDB) @@ -212,6 +206,98 @@ func createRoom( return jsonerror.InternalServerError() } + powerLevelContent := eventutil.InitialPowerLevelsContent(userID) + joinRuleContent := gomatrixserverlib.JoinRuleContent{ + JoinRule: gomatrixserverlib.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 { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: jsonerror.BadJSON("malformed power_level_content_override"), + } + } + } + + switch r.Preset { + case presetPrivateChat: + joinRuleContent.JoinRule = gomatrixserverlib.Invite + historyVisibilityContent.HistoryVisibility = historyVisibilityShared + case presetTrustedPrivateChat: + joinRuleContent.JoinRule = gomatrixserverlib.Invite + historyVisibilityContent.HistoryVisibility = historyVisibilityShared + // TODO If trusted_private_chat, all invitees are given the same power level as the room creator. + case presetPublicChat: + joinRuleContent.JoinRule = gomatrixserverlib.Public + historyVisibilityContent.HistoryVisibility = historyVisibilityShared + } + + createEvent := fledglingEvent{ + Type: gomatrixserverlib.MRoomCreate, + Content: gomatrixserverlib.CreateContent{ + Creator: userID, + RoomVersion: &roomVersion, + }, + } + powerLevelEvent := fledglingEvent{ + Type: gomatrixserverlib.MRoomPowerLevels, + Content: powerLevelContent, + } + joinRuleEvent := fledglingEvent{ + Type: gomatrixserverlib.MRoomJoinRules, + Content: joinRuleContent, + } + historyVisibilityEvent := fledglingEvent{ + Type: gomatrixserverlib.MRoomHistoryVisibility, + Content: historyVisibilityContent, + } + membershipEvent := fledglingEvent{ + Type: gomatrixserverlib.MRoomHistoryVisibility, + StateKey: userID, + Content: gomatrixserverlib.MemberContent{ + Membership: gomatrixserverlib.Join, + DisplayName: profile.DisplayName, + AvatarURL: profile.AvatarURL, + }, + } + + var nameEvent *fledglingEvent + var topicEvent *fledglingEvent + var guestAccessEvent *fledglingEvent + + if r.Name != "" { + nameEvent = &fledglingEvent{ + Type: gomatrixserverlib.MRoomName, + Content: eventutil.NameContent{ + Name: r.Name, + }, + } + } + + if r.Topic != "" { + topicEvent = &fledglingEvent{ + Type: "m.room.topic", + Content: eventutil.TopicContent{ + Topic: r.Topic, + }, + } + } + + if r.GuestCanJoin { + guestAccessEvent = &fledglingEvent{ + Type: "m.room.guest_access", + Content: eventutil.GuestAccessContent{ + GuestAccess: "can_join", + }, + } + } + var roomAlias string if r.RoomAliasName != "" { roomAlias = fmt.Sprintf("#%s:%s", r.RoomAliasName, cfg.Matrix.ServerName) @@ -232,42 +318,32 @@ func createRoom( } } - membershipContent := gomatrixserverlib.MemberContent{ - Membership: gomatrixserverlib.Join, - DisplayName: profile.DisplayName, - AvatarURL: profile.AvatarURL, - } + var initialStateEvents []fledglingEvent + for i := range r.InitialState { + switch r.InitialState[i].Type { + case gomatrixserverlib.MRoomCreate: + createEvent = r.InitialState[i] - var joinRules, historyVisibility string - switch r.Preset { - case presetPrivateChat: - joinRules = gomatrixserverlib.Invite - historyVisibility = historyVisibilityShared - case presetTrustedPrivateChat: - joinRules = gomatrixserverlib.Invite - historyVisibility = historyVisibilityShared - // TODO If trusted_private_chat, all invitees are given the same power level as the room creator. - case presetPublicChat: - joinRules = gomatrixserverlib.Public - historyVisibility = historyVisibilityShared - default: - // Default room rules, r.Preset was previously checked for valid values so - // only a request with no preset should end up here. - joinRules = gomatrixserverlib.Invite - historyVisibility = historyVisibilityShared - } + case gomatrixserverlib.MRoomPowerLevels: + powerLevelEvent = r.InitialState[i] - var builtEvents []*gomatrixserverlib.HeaderedEvent + case gomatrixserverlib.MRoomJoinRules: + joinRuleEvent = r.InitialState[i] - powerLevelContent := eventutil.InitialPowerLevelsContent(userID) - if r.PowerLevelContentOverride != nil { - // Merge powerLevelContentOverride fields by unmarshalling it atop the defaults - err = json.Unmarshal(r.PowerLevelContentOverride, &powerLevelContent) - if err != nil { - return util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: jsonerror.BadJSON("malformed power_level_content_override"), - } + case gomatrixserverlib.MRoomHistoryVisibility: + historyVisibilityEvent = r.InitialState[i] + + case "m.room.guest_access": + guestAccessEvent = &r.InitialState[i] + + case gomatrixserverlib.MRoomName: + nameEvent = &r.InitialState[i] + + case "m.room.topic": + topicEvent = &r.InitialState[i] + + default: + initialStateEvents = append(initialStateEvents, r.InitialState[i]) } } @@ -290,11 +366,16 @@ func createRoom( // 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{ - {"m.room.create", "", r.CreationContent}, - {"m.room.member", userID, membershipContent}, - {"m.room.power_levels", "", powerLevelContent}, - {"m.room.join_rules", "", gomatrixserverlib.JoinRuleContent{JoinRule: joinRules}}, - {"m.room.history_visibility", "", eventutil.HistoryVisibilityContent{HistoryVisibility: historyVisibility}}, + createEvent, membershipEvent, powerLevelEvent, joinRuleEvent, historyVisibilityEvent, + } + if guestAccessEvent != nil { + eventsToMake = append(eventsToMake, *guestAccessEvent) + } + if nameEvent != nil { + eventsToMake = append(eventsToMake, *nameEvent) + } + if topicEvent != nil { + eventsToMake = append(eventsToMake, *topicEvent) } if roomAlias != "" { // TODO: bit of a chicken and egg problem here as the alias doesn't exist and cannot until we have made the room. @@ -302,19 +383,12 @@ func createRoom( // m.room.aliases is handled when we call roomserver.SetRoomAlias eventsToMake = append(eventsToMake, fledglingEvent{"m.room.canonical_alias", "", eventutil.CanonicalAlias{Alias: roomAlias}}) } - if r.GuestCanJoin { - eventsToMake = append(eventsToMake, fledglingEvent{"m.room.guest_access", "", eventutil.GuestAccessContent{GuestAccess: "can_join"}}) - } - eventsToMake = append(eventsToMake, r.InitialState...) - if r.Name != "" { - eventsToMake = append(eventsToMake, fledglingEvent{"m.room.name", "", eventutil.NameContent{Name: r.Name}}) - } - if r.Topic != "" { - eventsToMake = append(eventsToMake, fledglingEvent{"m.room.topic", "", eventutil.TopicContent{Topic: r.Topic}}) - } + eventsToMake = append(eventsToMake, initialStateEvents...) + // TODO: invite events // TODO: 3pid invite events + var builtEvents []*gomatrixserverlib.HeaderedEvent authEvents := gomatrixserverlib.NewAuthEvents(nil) for i, e := range eventsToMake { depth := i + 1 // depth starts at 1